mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-29 11:25:38 +00:00
Merge commit 'b83248d844d7d3fc178843f1f9cdc607004a5c68'
This commit is contained in:
commit
b15ce8cc3b
@ -221,7 +221,7 @@ private func archivedStickerPacksControllerEntries(presentationData: Presentatio
|
||||
return entries
|
||||
}
|
||||
|
||||
public func archivedStickerPacksController(account: Account, archived: [ArchivedStickerPackItem]?) -> ViewController {
|
||||
public func archivedStickerPacksController(account: Account, archived: [ArchivedStickerPackItem]?, updatedPacks: @escaping([ArchivedStickerPackItem]?)->Void) -> ViewController {
|
||||
let statePromise = ValuePromise(ArchivedStickerPacksControllerState(), ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: ArchivedStickerPacksControllerState())
|
||||
let updateState: ((ArchivedStickerPacksControllerState) -> ArchivedStickerPacksControllerState) -> Void = { f in
|
||||
@ -241,6 +241,11 @@ public func archivedStickerPacksController(account: Account, archived: [Archived
|
||||
let stickerPacks = Promise<[ArchivedStickerPackItem]?>()
|
||||
stickerPacks.set(.single(archived) |> then(archivedStickerPacks(account: account) |> map(Optional.init)))
|
||||
|
||||
|
||||
actionsDisposable.add(stickerPacks.get().start(next: { packs in
|
||||
updatedPacks(packs)
|
||||
}))
|
||||
|
||||
let installedStickerPacks = Promise<CombinedView>()
|
||||
installedStickerPacks.set(account.postbox.combinedView(keys: [.itemCollectionIds(namespaces: [Namespaces.ItemCollection.CloudStickerPacks])]))
|
||||
|
||||
|
||||
@ -560,13 +560,16 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon
|
||||
}
|
||||
}))
|
||||
}, addAdmin: {
|
||||
presentControllerImpl?(ChannelMembersSearchController(account: account, peerId: peerId, mode: .promote, openPeer: { peer, participant in
|
||||
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
if peer.id == account.peerId {
|
||||
return
|
||||
}
|
||||
if let participant = participant {
|
||||
switch participant.participant {
|
||||
updateState { current in
|
||||
|
||||
|
||||
presentControllerImpl?(ChannelMembersSearchController(account: account, peerId: peerId, mode: .promote, filters: [.exclude(current.temporaryAdmins.map({$0.peer.id}))], openPeer: { peer, participant in
|
||||
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
if peer.id == account.peerId {
|
||||
return
|
||||
}
|
||||
if let participant = participant {
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
return
|
||||
case let .member(_, _, _, banInfo):
|
||||
@ -574,11 +577,15 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon
|
||||
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Channel_Members_AddAdminErrorBlacklisted, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
presentControllerImpl?(channelAdminController(account: account, peerId: peerId, adminId: peer.id, initialParticipant: participant?.participant, updated: { _ in
|
||||
presentControllerImpl?(channelAdminController(account: account, peerId: peerId, adminId: peer.id, initialParticipant: participant?.participant, updated: { _ in
|
||||
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
}, openAdmin: { participant in
|
||||
if case let .member(adminId, _, _, _) = participant {
|
||||
presentControllerImpl?(channelAdminController(account: account, peerId: peerId, adminId: participant.peerId, initialParticipant: participant, updated: { _ in
|
||||
@ -619,6 +626,8 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
if !state.editing {
|
||||
if rightNavigationButton == nil {
|
||||
rightNavigationButton = ItemListNavigationButton(content: .icon(.search), style: .regular, enabled: true, action: {
|
||||
@ -634,6 +643,8 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
_ = stateValue.swap(state.withUpdatedTemporaryAdmins(admins))
|
||||
}
|
||||
|
||||
let previous = previousPeers
|
||||
|
||||
@ -142,7 +142,7 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
|
||||
|
||||
private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings)>
|
||||
|
||||
init(account: Account, peerId: PeerId, mode: ChannelMembersSearchMode, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void) {
|
||||
init(account: Account, peerId: PeerId, mode: ChannelMembersSearchMode, filters: [ChannelMembersSearchFilter], openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void) {
|
||||
self.account = account
|
||||
self.openPeer = openPeer
|
||||
self.mode = mode
|
||||
@ -227,6 +227,12 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
|
||||
var entries: [ChannelMembersSearchEntry] = []
|
||||
|
||||
var existingPeerIds = Set<PeerId>()
|
||||
for filter in filters {
|
||||
switch filter {
|
||||
case let .exclude(ids):
|
||||
existingPeerIds = existingPeerIds.union(ids)
|
||||
}
|
||||
}
|
||||
switch mode {
|
||||
case .inviteActions, .banAndPromoteActions:
|
||||
existingPeerIds.insert(account.peerId)
|
||||
|
||||
@ -9,12 +9,17 @@ enum ChannelMembersSearchControllerMode {
|
||||
case ban
|
||||
}
|
||||
|
||||
enum ChannelMembersSearchFilter {
|
||||
case exclude([PeerId])
|
||||
}
|
||||
|
||||
final class ChannelMembersSearchController: ViewController {
|
||||
private let queue = Queue()
|
||||
|
||||
private let account: Account
|
||||
private let peerId: PeerId
|
||||
private let mode: ChannelMembersSearchControllerMode
|
||||
private let filters: [ChannelMembersSearchFilter]
|
||||
private let openPeer: (Peer, RenderedChannelParticipant?) -> Void
|
||||
|
||||
private var presentationData: PresentationData
|
||||
@ -25,12 +30,12 @@ final class ChannelMembersSearchController: ViewController {
|
||||
return self.displayNode as! ChannelMembersSearchControllerNode
|
||||
}
|
||||
|
||||
init(account: Account, peerId: PeerId, mode: ChannelMembersSearchControllerMode, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void) {
|
||||
init(account: Account, peerId: PeerId, mode: ChannelMembersSearchControllerMode, filters: [ChannelMembersSearchFilter] = [], openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void) {
|
||||
self.account = account
|
||||
self.peerId = peerId
|
||||
self.mode = mode
|
||||
self.openPeer = openPeer
|
||||
|
||||
self.filters = filters
|
||||
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
@ -52,7 +57,7 @@ final class ChannelMembersSearchController: ViewController {
|
||||
}
|
||||
|
||||
override func loadDisplayNode() {
|
||||
self.displayNode = ChannelMembersSearchControllerNode(account: self.account, theme: self.presentationData.theme, strings: self.presentationData.strings, peerId: self.peerId, mode: self.mode)
|
||||
self.displayNode = ChannelMembersSearchControllerNode(account: self.account, theme: self.presentationData.theme, strings: self.presentationData.strings, peerId: self.peerId, mode: self.mode, filters: self.filters)
|
||||
self.controllerNode.navigationBar = self.navigationBar
|
||||
self.controllerNode.requestActivateSearch = { [weak self] in
|
||||
self?.activateSearch()
|
||||
|
||||
@ -108,7 +108,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
||||
private let account: Account
|
||||
private let peerId: PeerId
|
||||
private let mode: ChannelMembersSearchControllerMode
|
||||
|
||||
private let filters: [ChannelMembersSearchFilter]
|
||||
let listNode: ListView
|
||||
var navigationBar: NavigationBar?
|
||||
|
||||
@ -127,12 +127,12 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
||||
private var disposable: Disposable?
|
||||
private var listControl: PeerChannelMemberCategoryControl?
|
||||
|
||||
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, peerId: PeerId, mode: ChannelMembersSearchControllerMode) {
|
||||
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, peerId: PeerId, mode: ChannelMembersSearchControllerMode, filters: [ChannelMembersSearchFilter]) {
|
||||
self.account = account
|
||||
self.listNode = ListView()
|
||||
self.peerId = peerId
|
||||
self.mode = mode
|
||||
|
||||
self.filters = filters
|
||||
self.themeAndStrings = (theme, strings)
|
||||
|
||||
super.init()
|
||||
@ -168,10 +168,26 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
||||
if participant.peer.id == account.peerId {
|
||||
continue
|
||||
}
|
||||
for filter in filters {
|
||||
switch filter {
|
||||
case let .exclude(ids):
|
||||
if ids.contains(participant.peer.id) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
case .promote:
|
||||
if participant.peer.id == account.peerId {
|
||||
continue
|
||||
}
|
||||
for filter in filters {
|
||||
switch filter {
|
||||
case let .exclude(ids):
|
||||
if ids.contains(participant.peer.id) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if case .creator = participant.participant {
|
||||
label = strings.Channel_Management_LabelCreator
|
||||
enabled = false
|
||||
@ -268,7 +284,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if let placeholderNode = maybePlaceholderNode {
|
||||
self.searchDisplayController = SearchDisplayController(theme: self.themeAndStrings.0, strings: self.themeAndStrings.1, contentNode: ChannelMembersSearchContainerNode(account: self.account, peerId: self.peerId, mode: .banAndPromoteActions, openPeer: { [weak self] peer, participant in
|
||||
self.searchDisplayController = SearchDisplayController(theme: self.themeAndStrings.0, strings: self.themeAndStrings.1, contentNode: ChannelMembersSearchContainerNode(account: self.account, peerId: self.peerId, mode: .banAndPromoteActions, filters: self.filters, openPeer: { [weak self] peer, participant in
|
||||
self?.requestOpenPeerFromSearch?(peer, participant)
|
||||
}), cancel: { [weak self] in
|
||||
if let requestDeactivateSearch = self?.requestDeactivateSearch {
|
||||
|
||||
@ -307,6 +307,26 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin
|
||||
|
||||
controllerInteraction.hiddenMedia = messageIdAndMedia
|
||||
|
||||
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
itemNode.updateHiddenMedia()
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}, chatAvatarHiddenMedia: { signal, media in
|
||||
if let strongSelf = self {
|
||||
strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).start(next: { messageId in
|
||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
||||
|
||||
if let messageId = messageId {
|
||||
messageIdAndMedia[messageId] = [media]
|
||||
}
|
||||
|
||||
controllerInteraction.hiddenMedia = messageIdAndMedia
|
||||
|
||||
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
itemNode.updateHiddenMedia()
|
||||
@ -3828,7 +3848,7 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin
|
||||
searchDisposable = MetaDisposable()
|
||||
self.searchDisposable = searchDisposable
|
||||
}
|
||||
searchDisposable.set((searchMessages(account: self.account, location: location, query: query)
|
||||
searchDisposable.set((searchMessages(account: self.account, location: location, query: query) |> map {$0.0}
|
||||
|> delay(0.2, queue: Queue.mainQueue())
|
||||
|> deliverOnMainQueue).start(next: { [weak self] results in
|
||||
if let strongSelf = self {
|
||||
|
||||
@ -5,6 +5,65 @@ import Display
|
||||
import AsyncDisplayKit
|
||||
import TelegramCore
|
||||
|
||||
|
||||
private class ChatGridLiveSelectorRecognizer: UIPanGestureRecognizer {
|
||||
|
||||
private let selectionGestureActivationThreshold: CGFloat = 2.0
|
||||
private let selectionGestureVerticalFailureThreshold: CGFloat = 5.0
|
||||
|
||||
var validatedGesture: Bool? = nil
|
||||
var firstLocation: CGPoint = CGPoint()
|
||||
|
||||
var shouldBegin: (() -> Bool)?
|
||||
|
||||
override init(target: Any?, action: Selector?) {
|
||||
super.init(target: target, action: action)
|
||||
|
||||
self.maximumNumberOfTouches = 1
|
||||
}
|
||||
|
||||
override func reset() {
|
||||
super.reset()
|
||||
|
||||
self.validatedGesture = nil
|
||||
}
|
||||
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesBegan(touches, with: event)
|
||||
|
||||
if let shouldBegin = self.shouldBegin, !shouldBegin() {
|
||||
self.state = .failed
|
||||
} else {
|
||||
let touch = touches.first!
|
||||
self.firstLocation = touch.location(in: self.view)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
let location = touches.first!.location(in: self.view)
|
||||
let translation = CGPoint(x: location.x - firstLocation.x, y: location.y - firstLocation.y)
|
||||
|
||||
if validatedGesture == nil {
|
||||
if (fabs(translation.y) >= selectionGestureVerticalFailureThreshold)
|
||||
{
|
||||
validatedGesture = false
|
||||
}
|
||||
else if (fabs(translation.x) >= selectionGestureActivationThreshold) {
|
||||
validatedGesture = true
|
||||
}
|
||||
}
|
||||
|
||||
if let validatedGesture = validatedGesture {
|
||||
if validatedGesture {
|
||||
super.touchesMoved(touches, with: event)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct ChatHistoryGridViewTransition {
|
||||
let historyView: ChatHistoryView
|
||||
let topOffsetWithinMonth: Int
|
||||
@ -180,13 +239,13 @@ public final class ChatHistoryGridNode: GridNode, ChatHistoryNode {
|
||||
|
||||
public private(set) var loadState: ChatHistoryNodeLoadState?
|
||||
private var loadStateUpdated: ((ChatHistoryNodeLoadState, Bool) -> Void)?
|
||||
|
||||
private let controllerInteraction: ChatControllerInteraction
|
||||
public init(account: Account, peerId: PeerId, messageId: MessageId?, tagMask: MessageTags?, controllerInteraction: ChatControllerInteraction) {
|
||||
self.account = account
|
||||
self.peerId = peerId
|
||||
self.messageId = messageId
|
||||
self.tagMask = tagMask
|
||||
|
||||
self.controllerInteraction = controllerInteraction
|
||||
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
|
||||
super.init()
|
||||
@ -283,6 +342,39 @@ public final class ChatHistoryGridNode: GridNode, ChatHistoryNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let selectorRecogizner = ChatGridLiveSelectorRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
||||
selectorRecogizner.shouldBegin = { [weak controllerInteraction] in
|
||||
return controllerInteraction?.selectionState != nil
|
||||
}
|
||||
self.view.addGestureRecognizer(selectorRecogizner)
|
||||
}
|
||||
|
||||
public override func didLoad() {
|
||||
super.didLoad()
|
||||
}
|
||||
|
||||
private var liveSelectingState: (selecting: Bool, currentMessageId: MessageId)?
|
||||
|
||||
@objc private func panGesture(_ recognizer: UIGestureRecognizer) -> Void {
|
||||
guard let selectionState = controllerInteraction.selectionState else {return}
|
||||
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
if let itemNode = self.itemNodeAtPoint(recognizer.location(in: self.view)) as? GridMessageItemNode, let messageId = itemNode.messageId {
|
||||
liveSelectingState = (selecting: !selectionState.selectedIds.contains(messageId), currentMessageId: messageId)
|
||||
controllerInteraction.toggleMessagesSelection([messageId], !selectionState.selectedIds.contains(messageId))
|
||||
}
|
||||
case .changed:
|
||||
if let liveSelectingState = liveSelectingState, let itemNode = self.itemNodeAtPoint(recognizer.location(in: self.view)) as? GridMessageItemNode, let messageId = itemNode.messageId, messageId != liveSelectingState.currentMessageId {
|
||||
controllerInteraction.toggleMessagesSelection([messageId], liveSelectingState.selecting)
|
||||
self.liveSelectingState?.currentMessageId = messageId
|
||||
}
|
||||
case .ended, .failed, .cancelled:
|
||||
liveSelectingState = nil
|
||||
case .possible:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
|
||||
@ -151,7 +151,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode {
|
||||
if let strongSelf = self {
|
||||
let signal: Signal<[ChatHistorySearchEntry]?, NoError>
|
||||
if let query = query, !query.isEmpty {
|
||||
let foundRemoteMessages: Signal<[Message], NoError> = searchMessages(account: account, location: .peer(peerId: peerId, fromId: nil, tags: tagMask), query: query)
|
||||
let foundRemoteMessages: Signal<[Message], NoError> = searchMessages(account: account, location: .peer(peerId: peerId, fromId: nil, tags: tagMask), query: query) |> map {$0.0}
|
||||
|> delay(0.2, queue: Queue.concurrentDefaultQueue())
|
||||
|
||||
signal = combineLatest(foundRemoteMessages, themeAndStringsPromise.get())
|
||||
|
||||
@ -268,14 +268,16 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
}
|
||||
}
|
||||
|
||||
var hasUneditableAttributes = false
|
||||
|
||||
|
||||
if let peer = message.peers[message.id.peerId] as? TelegramChannel {
|
||||
if peer.hasBannedRights(.banSendMessages) {
|
||||
restrictEdit = true
|
||||
hasUneditableAttributes = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasEditRights {
|
||||
var hasUneditableAttributes = false
|
||||
for attribute in message.attributes {
|
||||
if let _ = attribute as? InlineBotMessageAttribute {
|
||||
hasUneditableAttributes = true
|
||||
@ -298,13 +300,16 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
} else if let _ = media as? TelegramMediaExpiredContent {
|
||||
hasUneditableAttributes = true
|
||||
break
|
||||
} else if let _ = media as? TelegramMediaMap {
|
||||
hasUneditableAttributes = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !hasUneditableAttributes {
|
||||
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||
if canPerformEditingActions(limits: limitsConfiguration, accountPeerId: account.peerId, message: message) {
|
||||
canEdit = !restrictEdit
|
||||
canEdit = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,7 +235,7 @@ enum ChatListSearchEntryStableId: Hashable {
|
||||
enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
case localPeer(Peer, Peer?, UnreadSearchBadge?, Int, PresentationTheme, PresentationStrings)
|
||||
case globalPeer(FoundPeer, UnreadSearchBadge?, Int, PresentationTheme, PresentationStrings)
|
||||
case message(Message, ChatListPresentationData)
|
||||
case message(Message, CombinedPeerReadState?, ChatListPresentationData)
|
||||
|
||||
var stableId: ChatListSearchEntryStableId {
|
||||
switch self {
|
||||
@ -243,7 +243,7 @@ enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
return .localPeerId(peer.id)
|
||||
case let .globalPeer(peer, _, _, _, _):
|
||||
return .globalPeerId(peer.peer.id)
|
||||
case let .message(message, _):
|
||||
case let .message(message, _, _):
|
||||
return .messageId(message.id)
|
||||
}
|
||||
}
|
||||
@ -262,8 +262,8 @@ enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .message(lhsMessage, lhsPresentationData):
|
||||
if case let .message(rhsMessage, rhsPresentationData) = rhs {
|
||||
case let .message(lhsMessage, lhsCombinedPeerReadState, lhsPresentationData):
|
||||
if case let .message(rhsMessage, rhsCombinedPeerReadState, rhsPresentationData) = rhs {
|
||||
if lhsMessage.id != rhsMessage.id {
|
||||
return false
|
||||
}
|
||||
@ -273,6 +273,9 @@ enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
if lhsPresentationData !== rhsPresentationData {
|
||||
return false
|
||||
}
|
||||
if lhsCombinedPeerReadState != rhsCombinedPeerReadState {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -297,8 +300,8 @@ enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
case .message:
|
||||
return true
|
||||
}
|
||||
case let .message(lhsMessage, _):
|
||||
if case let .message(rhsMessage, _) = rhs {
|
||||
case let .message(lhsMessage, _, _):
|
||||
if case let .message(rhsMessage, _, _) = rhs {
|
||||
return MessageIndex(lhsMessage) < MessageIndex(rhsMessage)
|
||||
} else {
|
||||
return false
|
||||
@ -393,8 +396,8 @@ enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
return ContactsPeerItem(theme: theme, strings: strings, account: account, peerMode: .generalSearch, peer: .peer(peer: peer.peer, chatPeer: peer.peer), status: .addressName(suffixString), badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: ChatListSearchItemHeader(type: .globalPeers, theme: theme, strings: strings, actionTitle: nil, action: nil), action: { _ in
|
||||
interaction.peerSelected(peer.peer)
|
||||
})
|
||||
case let .message(message, presentationData):
|
||||
return ChatListItem(presentationData: presentationData, account: account, peerGroupId: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(message)), content: .peer(message: message, peer: RenderedPeer(message: message), combinedReadState: nil, notificationSettings: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, isAd: false), editing: false, hasActiveRevealControls: false, header: enableHeaders ? ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) : nil, enableContextActions: false, interaction: interaction)
|
||||
case let .message(message, readState, presentationData):
|
||||
return ChatListItem(presentationData: presentationData, account: account, peerGroupId: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(message)), content: .peer(message: message, peer: RenderedPeer(message: message), combinedReadState: readState, notificationSettings: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, isAd: false), editing: false, hasActiveRevealControls: false, header: enableHeaders ? ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) : nil, enableContextActions: false, interaction: interaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -566,7 +569,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
} else {
|
||||
location = .general
|
||||
}
|
||||
let foundRemoteMessages: Signal<([Message], Bool), NoError> = .single(([], true)) |> then(searchMessages(account: account, location: location, query: query)
|
||||
let foundRemoteMessages: Signal<(([Message], [PeerId : CombinedPeerReadState]), Bool), NoError> = .single((([], [:]), true)) |> then(searchMessages(account: account, location: location, query: query)
|
||||
|> map { ($0, false) }
|
||||
|> delay(0.2, queue: Queue.concurrentDefaultQueue()))
|
||||
|
||||
@ -619,8 +622,8 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
|
||||
if !foundRemotePeers.2 {
|
||||
index = 0
|
||||
for message in foundRemoteMessages.0 {
|
||||
entries.append(.message(message, presentationData))
|
||||
for message in foundRemoteMessages.0.0 {
|
||||
entries.append(.message(message, foundRemoteMessages.0.1[message.id.peerId], presentationData))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -546,6 +546,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
if j != 0 && index + j >= 0 && index + j < sortedIndices.count {
|
||||
if abs(labelRects[index + j].width - labelRects[index].width) < 40.0 {
|
||||
labelRects[index + j].size.width = max(labelRects[index + j].width, labelRects[index].width)
|
||||
labelRects[index].size.width = labelRects[index + j].size.width
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -557,6 +558,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
labelRects[i].origin.x = floor((labelLayout.size.width - labelRects[i].width) / 2.0)
|
||||
}
|
||||
|
||||
|
||||
let backgroundApply = backgroundLayout(item.presentationData.theme.theme.chat.serviceMessage.serviceMessageFillColor, labelRects, 10.0, 10.0, 0.0)
|
||||
|
||||
var backgroundSize = CGSize(width: labelLayout.size.width + 8.0 + 8.0, height: labelLayout.size.height + 4.0)
|
||||
@ -584,9 +586,9 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
let apply = imageNode.asyncLayout()(arguments)
|
||||
apply()
|
||||
|
||||
strongSelf.fetchDisposable.set(chatMessagePhotoInteractiveFetched(account: item.account, photoReference: .message(message: MessageReference(item.message), media: image)).start())
|
||||
}
|
||||
let updateImageSignal = chatMessagePhoto(postbox: item.account.postbox, photoReference: ImageMediaReference.message(message: MessageReference(item.message), media: image))
|
||||
strongSelf.fetchDisposable.set(chatMessagePhotoInteractiveFetched(account: item.account, photoReference: .message(message: MessageReference(item.message), media: image)).start())
|
||||
let updateImageSignal = chatMessagePhoto(postbox: item.account.postbox, photoReference: .message(message: MessageReference(item.message), media: image))
|
||||
|
||||
imageNode.setSignal(updateImageSignal)
|
||||
|
||||
|
||||
@ -264,7 +264,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
}
|
||||
for media in item.content.firstMessage.media {
|
||||
if let media = media as? TelegramMediaAction, case .phoneCall(_, _, _) = media.action {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,7 +214,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: strings.Conversation_PinnedMessage, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0)))
|
||||
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: descriptionStringForMessage(message, strings: strings, accountPeerId: accountPeerId).0, font: Font.regular(15.0), textColor: theme.chat.inputPanel.primaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0)))
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: descriptionStringForMessage(message, strings: strings, accountPeerId: accountPeerId).0, font: Font.regular(15.0), textColor: !message.media.isEmpty ? theme.chat.inputPanel.secondaryTextColor : theme.chat.inputPanel.primaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0)))
|
||||
|
||||
Queue.mainQueue().async {
|
||||
if let strongSelf = self {
|
||||
|
||||
@ -167,27 +167,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}, callPeer: { peerId in
|
||||
self?.controllerInteraction?.callPeer(peerId)
|
||||
}, enqueueMessage: { _ in
|
||||
}, sendSticker: nil, setupTemporaryHiddenMedia: { signal, centralIndex, galleryMedia in
|
||||
if let strongSelf = self {
|
||||
/*strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).start(next: { entry in
|
||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
||||
|
||||
if let entry = entry, entry.index == centralIndex {
|
||||
messageIdAndMedia[message.id] = [galleryMedia]
|
||||
}
|
||||
|
||||
controllerInteraction.hiddenMedia = messageIdAndMedia
|
||||
|
||||
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
itemNode.updateHiddenMedia()
|
||||
}
|
||||
}
|
||||
}
|
||||
}))*/
|
||||
}
|
||||
})
|
||||
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in})
|
||||
}
|
||||
return false
|
||||
}, openPeer: { [weak self] peerId, _, message in
|
||||
|
||||
@ -63,6 +63,7 @@ final class GalleryThumbnailContainerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func updateItems(_ items: [GalleryThumbnailItem], centralIndex: Int, progress: CGFloat) {
|
||||
var items: [GalleryThumbnailItem] = items.count <= 1 ? [] : items
|
||||
var updated = false
|
||||
if self.items.count == items.count {
|
||||
for i in 0 ..< self.items.count {
|
||||
|
||||
@ -5,6 +5,42 @@ import TelegramCore
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
|
||||
private let videoAccessoryFont: UIFont = Font.regular(11)
|
||||
|
||||
private final class GridMessageVideoAccessoryNode : ASDisplayNode {
|
||||
|
||||
private let textNode: ImmediateTextNode = ImmediateTextNode()
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.maximumNumberOfLines = 1
|
||||
self.textNode.isLayerBacked = true
|
||||
self.textNode.textAlignment = .left
|
||||
self.textNode.lineSpacing = 0.1
|
||||
addSubnode(self.textNode)
|
||||
backgroundColor = UIColor(white: 0.0, alpha: 0.6)
|
||||
}
|
||||
|
||||
var contentSize: CGSize {
|
||||
return CGSize(width: textSize.width + 10, height: 16)
|
||||
}
|
||||
private var textSize: CGSize = CGSize()
|
||||
|
||||
func setup(_ duration: String) {
|
||||
textNode.attributedText = NSAttributedString(string: duration, font: videoAccessoryFont, textColor: .white, paragraphAlignment: nil)
|
||||
textSize = self.textNode.updateLayout(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude))
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
if let _ = self.textNode.attributedText {
|
||||
self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((frame.width - textSize.width) / 2.0), y: floorToScreenPixels((frame.height - textSize.height) / 2.0) + 0.5), size: textSize)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private func mediaForMessage(_ message: Message) -> Media? {
|
||||
for media in message.media {
|
||||
if let media = media as? TelegramMediaImage {
|
||||
@ -111,7 +147,6 @@ final class GridMessageItem: GridItem {
|
||||
private let account: Account
|
||||
fileprivate let message: Message
|
||||
private let controllerInteraction: ChatControllerInteraction
|
||||
|
||||
let section: GridSection?
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, account: Account, message: Message, controllerInteraction: ChatControllerInteraction) {
|
||||
@ -145,11 +180,12 @@ final class GridMessageItem: GridItem {
|
||||
final class GridMessageItemNode: GridItemNode {
|
||||
private var currentState: (Account, Media, CGSize)?
|
||||
private let imageNode: TransformImageNode
|
||||
private var messageId: MessageId?
|
||||
private(set) var messageId: MessageId?
|
||||
private var item: GridMessageItem?
|
||||
private var controllerInteraction: ChatControllerInteraction?
|
||||
private var statusNode: RadialStatusNode
|
||||
|
||||
private let videoAccessoryNode = GridMessageVideoAccessoryNode()
|
||||
|
||||
private var selectionNode: GridMessageSelectionNode?
|
||||
|
||||
private let fetchStatusDisposable = MetaDisposable()
|
||||
@ -164,6 +200,7 @@ final class GridMessageItemNode: GridItemNode {
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.imageNode)
|
||||
self.imageNode.addSubnode(videoAccessoryNode)
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -193,11 +230,20 @@ final class GridMessageItemNode: GridItemNode {
|
||||
self.statusNode.transitionToState(.none, completion: { [weak self] in
|
||||
self?.statusNode.isHidden = true
|
||||
})
|
||||
videoAccessoryNode.isHidden = true
|
||||
self.resourceStatus = nil
|
||||
} else if let file = media as? TelegramMediaFile, file.isVideo {
|
||||
mediaDimensions = file.dimensions
|
||||
self.imageNode.setSignal(mediaGridMessageVideo(postbox: account.postbox, videoReference: .message(message: MessageReference(item.message), media: file)))
|
||||
|
||||
if let duration = file.duration {
|
||||
videoAccessoryNode.setup(String(format: "%d:%02d", duration / 60, duration % 60))
|
||||
videoAccessoryNode.isHidden = false
|
||||
} else {
|
||||
videoAccessoryNode.isHidden = true
|
||||
}
|
||||
|
||||
|
||||
self.resourceStatus = nil
|
||||
self.fetchStatusDisposable.set((messageMediaFileStatus(account: account, messageId: messageId, file: file) |> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
if let strongSelf = self {
|
||||
@ -233,6 +279,8 @@ final class GridMessageItemNode: GridItemNode {
|
||||
if self.statusNode.supernode == nil {
|
||||
self.imageNode.addSubnode(self.statusNode)
|
||||
}
|
||||
} else {
|
||||
videoAccessoryNode.isHidden = true
|
||||
}
|
||||
|
||||
if let mediaDimensions = mediaDimensions {
|
||||
@ -263,6 +311,8 @@ final class GridMessageItemNode: GridItemNode {
|
||||
self.selectionNode?.frame = CGRect(origin: CGPoint(), size: self.bounds.size)
|
||||
let progressDiameter: CGFloat = 40.0
|
||||
self.statusNode.frame = CGRect(origin: CGPoint(x: floor((imageFrame.size.width - progressDiameter) / 2.0), y: floor((imageFrame.size.height - progressDiameter) / 2.0)), size: CGSize(width: progressDiameter, height: progressDiameter))
|
||||
|
||||
videoAccessoryNode.frame = CGRect(origin: CGPoint(x: imageFrame.maxX - videoAccessoryNode.contentSize.width - 5, y: imageFrame.maxY - videoAccessoryNode.contentSize.height - 5), size: videoAccessoryNode.contentSize)
|
||||
}
|
||||
|
||||
func updateSelectionState(animated: Bool) {
|
||||
|
||||
@ -51,7 +51,7 @@ private final class ChannelMembersSearchItemNode: ItemListControllerSearchNode {
|
||||
private let containerNode: ChannelMembersSearchContainerNode
|
||||
|
||||
init(account: Account, peerId: PeerId, searchMode: ChannelMembersSearchMode, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, cancel: @escaping () -> Void) {
|
||||
self.containerNode = ChannelMembersSearchContainerNode(account: account, peerId: peerId, mode: searchMode, openPeer: { peer, participant in
|
||||
self.containerNode = ChannelMembersSearchContainerNode(account: account, peerId: peerId, mode: searchMode, filters: [], openPeer: { peer, participant in
|
||||
openPeer(peer, participant)
|
||||
})
|
||||
self.containerNode.cancel = {
|
||||
|
||||
@ -38,7 +38,9 @@ final class HashtagSearchController: TelegramController {
|
||||
let location: SearchMessagesLocation = .general
|
||||
let search = searchMessages(account: account, location: location, query: query)
|
||||
let foundMessages: Signal<[ChatListSearchEntry], NoError> = search
|
||||
|> map { return $0.map({ .message($0, chatListPresentationData) }) }
|
||||
|> map { result in
|
||||
return result.0.map({ .message($0, result.1[$0.id.peerId], chatListPresentationData) })
|
||||
}
|
||||
let interaction = ChatListNodeInteraction(activateSearch: {
|
||||
}, peerSelected: { peer in
|
||||
|
||||
|
||||
@ -77,11 +77,14 @@ private let iconTextBackgroundImage = generateStretchableFilledCircleImage(radiu
|
||||
final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode {
|
||||
private let imageNodeBackground: ASDisplayNode
|
||||
private let imageNode: TransformImageNode
|
||||
private var statusNode: RadialStatusNode?
|
||||
private var videoLayer: (SoftwareVideoThumbnailLayer, SoftwareVideoLayerFrameManager, SampleBufferLayer)?
|
||||
private var currentImageResource: TelegramMediaResource?
|
||||
private var currentVideoFile: TelegramMediaFile?
|
||||
|
||||
private var resourceStatus: MediaResourceStatus?
|
||||
private(set) var item: HorizontalListContextResultsChatInputPanelItem?
|
||||
private var statusDisposable = MetaDisposable()
|
||||
|
||||
|
||||
override var visibility: ListViewItemNodeVisibility {
|
||||
didSet {
|
||||
@ -165,6 +168,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
||||
displayLink.isPaused = true
|
||||
displayLink.invalidate()
|
||||
}
|
||||
statusDisposable.dispose()
|
||||
}
|
||||
|
||||
override public func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
|
||||
@ -189,7 +193,10 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
||||
let sideInset: CGFloat = 4.0
|
||||
|
||||
var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
|
||||
|
||||
var updatedStatusSignal: Signal<MediaResourceStatus, NoError>?
|
||||
//messageFileMediaResourceStatus(account: account, file: file, message: message, isRecentActions: isRecentActions)
|
||||
|
||||
|
||||
var imageResource: TelegramMediaResource?
|
||||
var stickerFile: TelegramMediaFile?
|
||||
var videoFile: TelegramMediaFile?
|
||||
@ -206,6 +213,12 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
||||
videoFile = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])])
|
||||
imageResource = nil
|
||||
}
|
||||
|
||||
if let file = videoFile {
|
||||
updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(file.resource)
|
||||
} else if let imageResource = imageResource {
|
||||
updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(imageResource)
|
||||
}
|
||||
case let .internalReference(_, _, _, title, _, image, file, _):
|
||||
if let image = image {
|
||||
if let largestRepresentation = largestImageRepresentation(image.representations) {
|
||||
@ -230,7 +243,12 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
||||
if file.isVideo && file.isAnimated {
|
||||
videoFile = file
|
||||
imageResource = nil
|
||||
updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(file.resource)
|
||||
} else if let imageResource = imageResource {
|
||||
updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(imageResource)
|
||||
}
|
||||
} else if let imageResource = imageResource {
|
||||
updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(imageResource)
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,6 +300,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: height, height: croppedImageDimensions.width + sideInset), insets: UIEdgeInsets())
|
||||
|
||||
return (nodeLayout, { _ in
|
||||
@ -333,6 +352,52 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
||||
}
|
||||
}
|
||||
|
||||
let progressFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((nodeLayout.contentSize.width - 37) / 2), y: floorToScreenPixels((nodeLayout.contentSize.height - 37) / 2)), size: CGSize(width: 37, height: 37))
|
||||
|
||||
|
||||
if let updatedStatusSignal = updatedStatusSignal {
|
||||
strongSelf.statusDisposable.set((updatedStatusSignal |> deliverOnMainQueue).start(next: { [weak strongSelf] status in
|
||||
displayLinkDispatcher.dispatch {
|
||||
if let strongSelf = strongSelf {
|
||||
strongSelf.resourceStatus = status
|
||||
|
||||
if strongSelf.statusNode == nil {
|
||||
let statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.5))
|
||||
strongSelf.statusNode = statusNode
|
||||
strongSelf.addSubnode(statusNode)
|
||||
}
|
||||
|
||||
strongSelf.statusNode?.frame = progressFrame
|
||||
|
||||
|
||||
let state: RadialStatusNodeState
|
||||
let statusForegroundColor: UIColor = .white
|
||||
|
||||
switch status {
|
||||
case let .Fetching(_, progress):
|
||||
state = RadialStatusNodeState.progress(color: statusForegroundColor, lineWidth: nil, value: CGFloat(progress), cancelEnabled: false)
|
||||
case .Remote:
|
||||
state = .download(statusForegroundColor)
|
||||
case .Local:
|
||||
state = .none
|
||||
}
|
||||
|
||||
|
||||
if let statusNode = strongSelf.statusNode {
|
||||
if state == .none {
|
||||
strongSelf.statusNode = nil
|
||||
}
|
||||
statusNode.transitionToState(state, completion: { [weak statusNode] in
|
||||
if state == .none {
|
||||
statusNode?.removeFromSupernode()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
if let (thumbnailLayer, _, layer) = strongSelf.videoLayer {
|
||||
thumbnailLayer.bounds = CGRect(origin: CGPoint(), size: CGSize(width: croppedImageDimensions.width, height: croppedImageDimensions.height))
|
||||
thumbnailLayer.position = CGPoint(x: height / 2.0, y: (nodeLayout.contentSize.height - sideInset) / 2.0 + sideInset)
|
||||
|
||||
@ -378,7 +378,7 @@ public enum InstalledStickerPacksControllerMode {
|
||||
case masks
|
||||
}
|
||||
|
||||
public func installedStickerPacksController(account: Account, mode: InstalledStickerPacksControllerMode, archivedPacks:[ArchivedStickerPackItem]? = nil) -> ViewController {
|
||||
public func installedStickerPacksController(account: Account, mode: InstalledStickerPacksControllerMode, archivedPacks:[ArchivedStickerPackItem]? = nil, updatedPacks: @escaping([ArchivedStickerPackItem]?) -> Void = { _ in}) -> ViewController {
|
||||
let initialState = InstalledStickerPacksControllerState().withUpdatedEditing(mode == .modal)
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
@ -396,6 +396,9 @@ public func installedStickerPacksController(account: Account, mode: InstalledSti
|
||||
let resolveDisposable = MetaDisposable()
|
||||
actionsDisposable.add(resolveDisposable)
|
||||
|
||||
let archivedPromise = Promise<[ArchivedStickerPackItem]?>()
|
||||
|
||||
|
||||
var presentStickerPackController: ((StickerPackCollectionInfo) -> Void)?
|
||||
|
||||
let arguments = InstalledStickerPacksControllerArguments(account: account, openStickerPack: { info in
|
||||
@ -436,11 +439,14 @@ public func installedStickerPacksController(account: Account, mode: InstalledSti
|
||||
}
|
||||
}))
|
||||
}, openMasks: {
|
||||
pushControllerImpl?(installedStickerPacksController(account: account, mode: .masks, archivedPacks: archivedPacks))
|
||||
pushControllerImpl?(installedStickerPacksController(account: account, mode: .masks, archivedPacks: archivedPacks, updatedPacks: { _ in}))
|
||||
}, openFeatured: {
|
||||
pushControllerImpl?(featuredStickerPacksController(account: account))
|
||||
}, openArchived: { archived in
|
||||
pushControllerImpl?(archivedStickerPacksController(account: account, archived: archived))
|
||||
pushControllerImpl?(archivedStickerPacksController(account: account, archived: archived, updatedPacks: { packs in
|
||||
archivedPromise.set(.single(packs))
|
||||
updatedPacks(packs)
|
||||
}))
|
||||
}, openSuggestOptions: {
|
||||
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
let controller = ActionSheetController(presentationTheme: presentationData.theme)
|
||||
@ -484,7 +490,6 @@ public func installedStickerPacksController(account: Account, mode: InstalledSti
|
||||
stickerPacks.set(account.postbox.combinedView(keys: [.itemCollectionInfos(namespaces: [namespaceForMode(mode)])]))
|
||||
|
||||
let featured = Promise<[FeaturedStickerPackItem]>()
|
||||
let archivedPromise = Promise<[ArchivedStickerPackItem]?>()
|
||||
|
||||
switch mode {
|
||||
case .general, .modal:
|
||||
|
||||
@ -151,7 +151,7 @@ func chatMessagePreviewControllerData(account: Account, message: Message, standa
|
||||
return nil
|
||||
}
|
||||
|
||||
func openChatMessage(account: Account, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, navigationController: NavigationController?, modal: Bool = false, dismissInput: @escaping () -> Void, present: @escaping (ViewController, Any?) -> Void, transitionNode: @escaping (MessageId, Media) -> (ASDisplayNode, () -> UIView?)?, addToTransitionSurface: @escaping (UIView) -> Void, openUrl: @escaping (String) -> Void, openPeer: @escaping (Peer, ChatControllerInteractionNavigateToPeer) -> Void, callPeer: @escaping (PeerId) -> Void, enqueueMessage: @escaping (EnqueueMessage) -> Void, sendSticker: ((FileMediaReference) -> Void)?, setupTemporaryHiddenMedia: @escaping (Signal<InstantPageGalleryEntry?, NoError>, Int, Media) -> Void) -> Bool {
|
||||
func openChatMessage(account: Account, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, navigationController: NavigationController?, modal: Bool = false, dismissInput: @escaping () -> Void, present: @escaping (ViewController, Any?) -> Void, transitionNode: @escaping (MessageId, Media) -> (ASDisplayNode, () -> UIView?)?, addToTransitionSurface: @escaping (UIView) -> Void, openUrl: @escaping (String) -> Void, openPeer: @escaping (Peer, ChatControllerInteractionNavigateToPeer) -> Void, callPeer: @escaping (PeerId) -> Void, enqueueMessage: @escaping (EnqueueMessage) -> Void, sendSticker: ((FileMediaReference) -> Void)?, setupTemporaryHiddenMedia: @escaping (Signal<InstantPageGalleryEntry?, NoError>, Int, Media) -> Void, chatAvatarHiddenMedia: @escaping (Signal<MessageId?, NoError>, Media) -> Void) -> Bool {
|
||||
if let mediaData = chatMessageGalleryControllerData(account: account, message: message, navigationController: navigationController, standalone: standalone, reverseMessageGalleryOrder: reverseMessageGalleryOrder, synchronousLoad: false) {
|
||||
switch mediaData {
|
||||
case let .url(url):
|
||||
@ -327,7 +327,14 @@ func openChatMessage(account: Account, message: Message, standalone: Bool, rever
|
||||
return true
|
||||
}
|
||||
case let .chatAvatars(controller, media):
|
||||
|
||||
chatAvatarHiddenMedia(controller.hiddenMedia |> map { value -> MessageId? in
|
||||
if value != nil {
|
||||
return message.id
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}, media)
|
||||
|
||||
present(controller, AvatarGalleryControllerPresentationArguments(transitionArguments: { entry in
|
||||
if let selectedTransitionNode = transitionNode(message.id, media) {
|
||||
return GalleryTransitionArguments(transitionNode: selectedTransitionNode, addToTransitionSurface: addToTransitionSurface)
|
||||
|
||||
@ -156,7 +156,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec
|
||||
|
||||
openMessageImpl = { [weak self] id in
|
||||
if let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.historyNode.messageInCurrentHistoryView(id) {
|
||||
return openChatMessage(account: strongSelf.account, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: nil, dismissInput: { }, present: { _, _ in }, transitionNode: { _, _ in return nil }, addToTransitionSurface: { _ in }, openUrl: { _ in }, openPeer: { _, _ in }, callPeer: { _ in }, enqueueMessage: { _ in }, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in })
|
||||
return openChatMessage(account: strongSelf.account, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: nil, dismissInput: { }, present: { _, _ in }, transitionNode: { _, _ in return nil }, addToTransitionSurface: { _ in }, openUrl: { _ in }, openPeer: { _, _ in }, callPeer: { _ in }, enqueueMessage: { _ in }, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in})
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -104,8 +104,7 @@ public class PeerMediaCollectionController: TelegramController {
|
||||
}, callPeer: { peerId in
|
||||
self?.controllerInteraction?.callPeer(peerId)
|
||||
}, enqueueMessage: { _ in
|
||||
}, sendSticker: nil, setupTemporaryHiddenMedia: { signal, centralIndex, galleryMedia in
|
||||
})
|
||||
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in})
|
||||
}
|
||||
return false
|
||||
}, openPeer: { [weak self] id, navigation, _ in
|
||||
@ -517,6 +516,7 @@ public class PeerMediaCollectionController: TelegramController {
|
||||
func updateInterfaceState(animated: Bool = true, _ f: (PeerMediaCollectionInterfaceState) -> PeerMediaCollectionInterfaceState) {
|
||||
let updatedInterfaceState = f(self.interfaceState)
|
||||
|
||||
|
||||
if self.isNodeLoaded {
|
||||
self.mediaCollectionDisplayNode.updateMediaCollectionInterfaceState(updatedInterfaceState, animated: animated)
|
||||
}
|
||||
@ -545,6 +545,7 @@ public class PeerMediaCollectionController: TelegramController {
|
||||
}
|
||||
|
||||
self.mediaCollectionDisplayNode.selectedMessages = updatedInterfaceState.selectionState?.selectedIds
|
||||
view.disablesInteractiveTransitionGestureRecognizer = updatedInterfaceState.selectionState != nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +45,7 @@ private struct SettingsItemArguments {
|
||||
let openSupport: () -> Void
|
||||
let openFaq: () -> Void
|
||||
let openEditing: () -> Void
|
||||
let updateArchivedPacks: ([ArchivedStickerPackItem]?) -> Void
|
||||
}
|
||||
|
||||
private enum SettingsSection: Int32 {
|
||||
@ -285,7 +286,9 @@ private enum SettingsEntry: ItemListNodeEntry {
|
||||
})
|
||||
case let .stickers(theme, image, text, value, archivedPacks):
|
||||
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: value, labelStyle: .badge, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
||||
arguments.pushController(installedStickerPacksController(account: arguments.account, mode: .general, archivedPacks: archivedPacks))
|
||||
arguments.pushController(installedStickerPacksController(account: arguments.account, mode: .general, archivedPacks: archivedPacks, updatedPacks: { packs in
|
||||
arguments.updateArchivedPacks(packs)
|
||||
}))
|
||||
})
|
||||
case let .notificationsAndSounds(theme, image, text):
|
||||
return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
||||
@ -426,6 +429,9 @@ public func settingsController(account: Account, accountManager: AccountManager)
|
||||
var changeProfilePhotoImpl: (() -> Void)?
|
||||
var openSavedMessagesImpl: (() -> Void)?
|
||||
|
||||
let archivedPacks = Promise<[ArchivedStickerPackItem]?>()
|
||||
|
||||
|
||||
let openFaq: () -> Void = {
|
||||
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
var faqUrl = presentationData.strings.Settings_FAQ_URL
|
||||
@ -517,6 +523,8 @@ public func settingsController(account: Account, accountManager: AccountManager)
|
||||
pushControllerImpl?(editSettingsController(account: account, currentName: .personName(firstName: peer.firstName ?? "", lastName: peer.lastName ?? ""), currentBioText: cachedData.about ?? "", accountManager: accountManager))
|
||||
}
|
||||
})
|
||||
}, updateArchivedPacks: { packs in
|
||||
archivedPacks.set(.single(packs))
|
||||
})
|
||||
|
||||
changeProfilePhotoImpl = {
|
||||
@ -601,7 +609,6 @@ public func settingsController(account: Account, accountManager: AccountManager)
|
||||
|
||||
let peerView = account.viewTracker.peerView(account.peerId)
|
||||
|
||||
let archivedPacks = Promise<[ArchivedStickerPackItem]?>()
|
||||
archivedPacks.set(.single(nil) |> then(archivedStickerPacks(account: account) |> map(Optional.init)))
|
||||
|
||||
let signal = combineLatest((account.applicationContext as! TelegramApplicationContext).presentationData, statePromise.get(), peerView, account.postbox.preferencesView(keys: [PreferencesKeys.proxySettings]), combineLatest(account.viewTracker.featuredStickerPacks(), archivedPacks.get()))
|
||||
|
||||
@ -27,8 +27,7 @@ private func presentLiveLocationController(account: Account, peerId: PeerId, con
|
||||
}, openPeer: { peer, navigation in
|
||||
}, callPeer: { _ in
|
||||
}, enqueueMessage: { _ in
|
||||
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in
|
||||
})
|
||||
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user