mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-17 11:50:56 +00:00
Support for chat list editing actions
This commit is contained in:
parent
394b204f29
commit
e9da154ac3
@ -408,6 +408,7 @@
|
|||||||
D0C27B3D1F4B454800A4E170 /* InstantPagePlayableVideoNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C27B3C1F4B454800A4E170 /* InstantPagePlayableVideoNode.swift */; };
|
D0C27B3D1F4B454800A4E170 /* InstantPagePlayableVideoNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C27B3C1F4B454800A4E170 /* InstantPagePlayableVideoNode.swift */; };
|
||||||
D0C44B641FC64D0500227BE0 /* SwipeToDismissGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C44B631FC64D0500227BE0 /* SwipeToDismissGestureRecognizer.swift */; };
|
D0C44B641FC64D0500227BE0 /* SwipeToDismissGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C44B631FC64D0500227BE0 /* SwipeToDismissGestureRecognizer.swift */; };
|
||||||
D0C45E9F213FFAFD00988156 /* Lottie.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C45E9E213FFAFD00988156 /* Lottie.framework */; };
|
D0C45E9F213FFAFD00988156 /* Lottie.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C45E9E213FFAFD00988156 /* Lottie.framework */; };
|
||||||
|
D0C683FC21AD797F00A6CAD5 /* ChatListSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C683FB21AD797F00A6CAD5 /* ChatListSelection.swift */; };
|
||||||
D0CAD8FB20AE1D1B00ACD96E /* ChannelMemberCategoryListContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CAD8FA20AE1D1B00ACD96E /* ChannelMemberCategoryListContext.swift */; };
|
D0CAD8FB20AE1D1B00ACD96E /* ChannelMemberCategoryListContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CAD8FA20AE1D1B00ACD96E /* ChannelMemberCategoryListContext.swift */; };
|
||||||
D0CAD8FD20AE467D00ACD96E /* PeerChannelMemberCategoriesContextsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CAD8FC20AE467D00ACD96E /* PeerChannelMemberCategoriesContextsManager.swift */; };
|
D0CAD8FD20AE467D00ACD96E /* PeerChannelMemberCategoriesContextsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CAD8FC20AE467D00ACD96E /* PeerChannelMemberCategoriesContextsManager.swift */; };
|
||||||
D0CAD90120AEECAC00ACD96E /* ChatEditInterfaceMessageState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CAD90020AEECAC00ACD96E /* ChatEditInterfaceMessageState.swift */; };
|
D0CAD90120AEECAC00ACD96E /* ChatEditInterfaceMessageState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CAD90020AEECAC00ACD96E /* ChatEditInterfaceMessageState.swift */; };
|
||||||
@ -1808,6 +1809,7 @@
|
|||||||
D0C50E3D1E93D09200F62E39 /* NotificationItemContainerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationItemContainerNode.swift; sourceTree = "<group>"; };
|
D0C50E3D1E93D09200F62E39 /* NotificationItemContainerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationItemContainerNode.swift; sourceTree = "<group>"; };
|
||||||
D0C50E3F1E93D3B000F62E39 /* ChatMessageNotificationItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatMessageNotificationItem.swift; sourceTree = "<group>"; };
|
D0C50E3F1E93D3B000F62E39 /* ChatMessageNotificationItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatMessageNotificationItem.swift; sourceTree = "<group>"; };
|
||||||
D0C50E431E93FCD200F62E39 /* notification.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = notification.caf; path = TelegramUI/Sounds/notification.caf; sourceTree = "<group>"; };
|
D0C50E431E93FCD200F62E39 /* notification.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = notification.caf; path = TelegramUI/Sounds/notification.caf; sourceTree = "<group>"; };
|
||||||
|
D0C683FB21AD797F00A6CAD5 /* ChatListSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListSelection.swift; sourceTree = "<group>"; };
|
||||||
D0C932351E0988C60074F044 /* ChatButtonKeyboardInputNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatButtonKeyboardInputNode.swift; sourceTree = "<group>"; };
|
D0C932351E0988C60074F044 /* ChatButtonKeyboardInputNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatButtonKeyboardInputNode.swift; sourceTree = "<group>"; };
|
||||||
D0C932371E09E0EA0074F044 /* ChatBotInfoItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatBotInfoItem.swift; sourceTree = "<group>"; };
|
D0C932371E09E0EA0074F044 /* ChatBotInfoItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatBotInfoItem.swift; sourceTree = "<group>"; };
|
||||||
D0C9323B1E0B4AE90074F044 /* DataAndStorageSettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataAndStorageSettingsController.swift; sourceTree = "<group>"; };
|
D0C9323B1E0B4AE90074F044 /* DataAndStorageSettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataAndStorageSettingsController.swift; sourceTree = "<group>"; };
|
||||||
@ -4219,6 +4221,7 @@
|
|||||||
D0575AEA1E9FD579006F2541 /* ChatListTitleLockView.swift */,
|
D0575AEA1E9FD579006F2541 /* ChatListTitleLockView.swift */,
|
||||||
D07E413A208A432100FCA8F0 /* ChatListTitleProxyNode.swift */,
|
D07E413A208A432100FCA8F0 /* ChatListTitleProxyNode.swift */,
|
||||||
D06E4C302134910400088087 /* ChatListEmptyNode.swift */,
|
D06E4C302134910400088087 /* ChatListEmptyNode.swift */,
|
||||||
|
D0C683FB21AD797F00A6CAD5 /* ChatListSelection.swift */,
|
||||||
D0F69E051D6B8A8B0046BCD6 /* Search */,
|
D0F69E051D6B8A8B0046BCD6 /* Search */,
|
||||||
);
|
);
|
||||||
name = "Chat List";
|
name = "Chat List";
|
||||||
@ -5283,6 +5286,7 @@
|
|||||||
D0EC6D681EB9F58800EBF1C3 /* AuthorizationSequenceController.swift in Sources */,
|
D0EC6D681EB9F58800EBF1C3 /* AuthorizationSequenceController.swift in Sources */,
|
||||||
D0EC6D691EB9F58800EBF1C3 /* AuthorizationSequenceSplashController.swift in Sources */,
|
D0EC6D691EB9F58800EBF1C3 /* AuthorizationSequenceSplashController.swift in Sources */,
|
||||||
D0EC6D6A1EB9F58800EBF1C3 /* AuthorizationSequenceSplashControllerNode.swift in Sources */,
|
D0EC6D6A1EB9F58800EBF1C3 /* AuthorizationSequenceSplashControllerNode.swift in Sources */,
|
||||||
|
D0C683FC21AD797F00A6CAD5 /* ChatListSelection.swift in Sources */,
|
||||||
D0EC6D6B1EB9F58800EBF1C3 /* AuthorizationSequenceCountrySelectionController.swift in Sources */,
|
D0EC6D6B1EB9F58800EBF1C3 /* AuthorizationSequenceCountrySelectionController.swift in Sources */,
|
||||||
D0EC6D6C1EB9F58800EBF1C3 /* AuthorizationSequenceCountrySelectionControllerNode.swift in Sources */,
|
D0EC6D6C1EB9F58800EBF1C3 /* AuthorizationSequenceCountrySelectionControllerNode.swift in Sources */,
|
||||||
D0BFAE5D20AB426300793CF2 /* PeerTitle.swift in Sources */,
|
D0BFAE5D20AB426300793CF2 /* PeerTitle.swift in Sources */,
|
||||||
|
|||||||
@ -274,7 +274,7 @@ public func archivedStickerPacksController(account: Account, archived: [Archived
|
|||||||
if !add {
|
if !add {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = (loadedStickerPack(postbox: account.postbox, network: account.network, reference: .id(id: info.id.id, accessHash: info.accessHash))
|
let _ = (loadedStickerPack(postbox: account.postbox, network: account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false)
|
||||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||||
switch result {
|
switch result {
|
||||||
case let .result(info, items, installed):
|
case let .result(info, items, installed):
|
||||||
|
|||||||
@ -590,7 +590,17 @@ func chatAvailableMessageActions(postbox: Postbox, accountPeerId: PeerId, messag
|
|||||||
case .creator, .admin:
|
case .creator, .admin:
|
||||||
optionsMap[id]!.insert(.deleteGlobally)
|
optionsMap[id]!.insert(.deleteGlobally)
|
||||||
case .member:
|
case .member:
|
||||||
break
|
var hasMediaToReport = false
|
||||||
|
for media in message.media {
|
||||||
|
if let _ = media as? TelegramMediaImage {
|
||||||
|
hasMediaToReport = true
|
||||||
|
} else if let file = media as? TelegramMediaFile, file.isVideo {
|
||||||
|
hasMediaToReport = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hasMediaToReport {
|
||||||
|
optionsMap[id]!.insert(.report)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let _ = peer as? TelegramUser {
|
} else if let _ = peer as? TelegramUser {
|
||||||
|
|||||||
@ -37,8 +37,11 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
|||||||
private var didSuggestLocalization = false
|
private var didSuggestLocalization = false
|
||||||
|
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
|
private let presentationDataValue = Promise<PresentationData>()
|
||||||
private var presentationDataDisposable: Disposable?
|
private var presentationDataDisposable: Disposable?
|
||||||
|
|
||||||
|
private let stateDisposable = MetaDisposable()
|
||||||
|
|
||||||
public init(account: Account, groupId: PeerGroupId?, controlsHistoryPreload: Bool) {
|
public init(account: Account, groupId: PeerGroupId?, controlsHistoryPreload: Bool) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.controlsHistoryPreload = controlsHistoryPreload
|
self.controlsHistoryPreload = controlsHistoryPreload
|
||||||
@ -46,6 +49,7 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
|||||||
self.groupId = groupId
|
self.groupId = groupId
|
||||||
|
|
||||||
self.presentationData = (account.telegramApplicationContext.currentPresentationData.with { $0 })
|
self.presentationData = (account.telegramApplicationContext.currentPresentationData.with { $0 })
|
||||||
|
self.presentationDataValue.set(.single(self.presentationData))
|
||||||
|
|
||||||
self.titleView = NetworkStatusTitleView(theme: self.presentationData.theme)
|
self.titleView = NetworkStatusTitleView(theme: self.presentationData.theme)
|
||||||
|
|
||||||
@ -207,18 +211,19 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.presentationDataDisposable = (account.telegramApplicationContext.presentationData
|
self.presentationDataDisposable = (account.telegramApplicationContext.presentationData
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let previousTheme = strongSelf.presentationData.theme
|
let previousTheme = strongSelf.presentationData.theme
|
||||||
let previousStrings = strongSelf.presentationData.strings
|
let previousStrings = strongSelf.presentationData.strings
|
||||||
|
|
||||||
strongSelf.presentationData = presentationData
|
strongSelf.presentationData = presentationData
|
||||||
|
strongSelf.presentationDataValue.set(.single(presentationData))
|
||||||
|
|
||||||
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
|
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
|
||||||
strongSelf.updateThemeAndStrings()
|
strongSelf.updateThemeAndStrings()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init(coder aDecoder: NSCoder) {
|
required public init(coder aDecoder: NSCoder) {
|
||||||
@ -233,6 +238,7 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
|||||||
self.passcodeLockTooltipDisposable.dispose()
|
self.passcodeLockTooltipDisposable.dispose()
|
||||||
self.suggestLocalizationDisposable.dispose()
|
self.suggestLocalizationDisposable.dispose()
|
||||||
self.presentationDataDisposable?.dispose()
|
self.presentationDataDisposable?.dispose()
|
||||||
|
self.stateDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateThemeAndStrings() {
|
private func updateThemeAndStrings() {
|
||||||
@ -469,6 +475,41 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let account = self.account
|
||||||
|
let peerIdsAndOptions: Signal<(ChatListSelectionOptions, Set<PeerId>)?, NoError> = self.chatListDisplayNode.chatListNode.state
|
||||||
|
|> map { state -> Set<PeerId>? in
|
||||||
|
if !state.editing {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return state.selectedPeerIds
|
||||||
|
}
|
||||||
|
|> distinctUntilChanged
|
||||||
|
|> mapToSignal { selectedPeerIds -> Signal<(ChatListSelectionOptions, Set<PeerId>)?, NoError> in
|
||||||
|
if let selectedPeerIds = selectedPeerIds {
|
||||||
|
return chatListSelectionOptions(postbox: account.postbox, peerIds: selectedPeerIds)
|
||||||
|
|> map { options -> (ChatListSelectionOptions, Set<PeerId>)? in
|
||||||
|
return (options, selectedPeerIds)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.stateDisposable.set(combineLatest(queue: .mainQueue(), self.presentationDataValue.get(), peerIdsAndOptions).start(next: { [weak self] presentationData, peerIdsAndOptions in
|
||||||
|
var toolbar: Toolbar?
|
||||||
|
if let (options, _) = peerIdsAndOptions {
|
||||||
|
let leftAction: ToolbarAction
|
||||||
|
switch options.read {
|
||||||
|
case let .all(enabled):
|
||||||
|
leftAction = ToolbarAction(title: presentationData.strings.ChatList_ReadAll, isEnabled: enabled)
|
||||||
|
case let .selective(enabled):
|
||||||
|
leftAction = ToolbarAction(title: presentationData.strings.ChatList_Read, isEnabled: enabled)
|
||||||
|
}
|
||||||
|
toolbar = Toolbar(leftAction: leftAction, rightAction: ToolbarAction(title: presentationData.strings.Common_Delete, isEnabled: options.delete))
|
||||||
|
}
|
||||||
|
self?.setToolbar(toolbar, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||||
|
}))
|
||||||
|
|
||||||
/*self.badgeIconDisposable = (self.chatListDisplayNode.chatListNode.scrollToTopOption
|
/*self.badgeIconDisposable = (self.chatListDisplayNode.chatListNode.scrollToTopOption
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] option in
|
|> deliverOnMainQueue).start(next: { [weak self] option in
|
||||||
@ -616,7 +657,9 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
|||||||
self.navigationItem.rightBarButtonItem = editItem
|
self.navigationItem.rightBarButtonItem = editItem
|
||||||
}
|
}
|
||||||
self.chatListDisplayNode.chatListNode.updateState { state in
|
self.chatListDisplayNode.chatListNode.updateState { state in
|
||||||
return state.withUpdatedEditing(true)
|
var state = state
|
||||||
|
state.editing = true
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,7 +671,11 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
|||||||
self.navigationItem.rightBarButtonItem = editItem
|
self.navigationItem.rightBarButtonItem = editItem
|
||||||
}
|
}
|
||||||
self.chatListDisplayNode.chatListNode.updateState { state in
|
self.chatListDisplayNode.chatListNode.updateState { state in
|
||||||
return state.withUpdatedEditing(false).withUpdatedPeerIdWithRevealedOptions(nil)
|
var state = state
|
||||||
|
state.editing = false
|
||||||
|
state.peerIdWithRevealedOptions = nil
|
||||||
|
state.selectedPeerIds.removeAll()
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -817,4 +864,75 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
|||||||
KeyShortcut(input: UIKeyInputEscape, modifiers: [], action: toggleSearch)
|
KeyShortcut(input: UIKeyInputEscape, modifiers: [], action: toggleSearch)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override public func toolbarActionSelected(left: Bool) {
|
||||||
|
let peerIds = self.chatListDisplayNode.chatListNode.currentState.selectedPeerIds
|
||||||
|
if left {
|
||||||
|
let signal: Signal<Void, NoError>
|
||||||
|
let account = self.account
|
||||||
|
if !peerIds.isEmpty {
|
||||||
|
signal = self.account.postbox.transaction { transaction -> Void in
|
||||||
|
for peerId in peerIds {
|
||||||
|
togglePeerUnreadMarkInteractively(transaction: transaction, viewTracker: account.viewTracker, peerId: peerId, setToValue: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
signal = self.account.postbox.transaction { transaction -> Void in
|
||||||
|
markAllChatsAsReadInteractively(transaction: transaction, viewTracker: account.viewTracker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = signal.start(completed: { [weak self] in
|
||||||
|
self?.donePressed()
|
||||||
|
})
|
||||||
|
} else if !peerIds.isEmpty {
|
||||||
|
let actionSheet = ActionSheetController(presentationTheme: self.presentationData.theme)
|
||||||
|
var items: [ActionSheetItem] = []
|
||||||
|
items.append(ActionSheetButtonItem(title: self.presentationData.strings.Common_Delete, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||||
|
actionSheet?.dismissAnimated()
|
||||||
|
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let account = strongSelf.account
|
||||||
|
let presentationData = strongSelf.presentationData
|
||||||
|
let progressSignal = Signal<Never, NoError> { subscriber in
|
||||||
|
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil))
|
||||||
|
self?.present(controller, in: .window(.root))
|
||||||
|
return ActionDisposable { [weak controller] in
|
||||||
|
Queue.mainQueue().async() {
|
||||||
|
controller?.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> runOn(Queue.mainQueue())
|
||||||
|
|> delay(0.8, queue: Queue.mainQueue())
|
||||||
|
let progressDisposable = progressSignal.start()
|
||||||
|
|
||||||
|
let signal: Signal<Void, NoError> = strongSelf.account.postbox.transaction { transaction -> Void in
|
||||||
|
for peerId in peerIds {
|
||||||
|
removePeerChat(transaction: transaction, mediaBox: account.postbox.mediaBox, peerId: peerId, reportChatSpam: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> afterDisposed {
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
progressDisposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = signal.start(completed: {
|
||||||
|
self?.donePressed()
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
|
actionSheet.setItemGroups([
|
||||||
|
ActionSheetItemGroup(items: items),
|
||||||
|
ActionSheetItemGroup(items: [
|
||||||
|
ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in
|
||||||
|
actionSheet?.dismissAnimated()
|
||||||
|
})
|
||||||
|
])
|
||||||
|
])
|
||||||
|
self.present(actionSheet, in: .window(.root))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,7 @@ class ChatListItem: ListViewItem {
|
|||||||
let content: ChatListItemContent
|
let content: ChatListItemContent
|
||||||
let editing: Bool
|
let editing: Bool
|
||||||
let hasActiveRevealControls: Bool
|
let hasActiveRevealControls: Bool
|
||||||
|
let selected: Bool
|
||||||
let enableContextActions: Bool
|
let enableContextActions: Bool
|
||||||
let interaction: ChatListNodeInteraction
|
let interaction: ChatListNodeInteraction
|
||||||
|
|
||||||
@ -35,7 +36,7 @@ class ChatListItem: ListViewItem {
|
|||||||
|
|
||||||
let header: ListViewItemHeader?
|
let header: ListViewItemHeader?
|
||||||
|
|
||||||
init(presentationData: ChatListPresentationData, account: Account, peerGroupId: PeerGroupId?, index: ChatListIndex, content: ChatListItemContent, editing: Bool, hasActiveRevealControls: Bool, header: ListViewItemHeader?, enableContextActions: Bool, interaction: ChatListNodeInteraction) {
|
init(presentationData: ChatListPresentationData, account: Account, peerGroupId: PeerGroupId?, index: ChatListIndex, content: ChatListItemContent, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, header: ListViewItemHeader?, enableContextActions: Bool, interaction: ChatListNodeInteraction) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.peerGroupId = peerGroupId
|
self.peerGroupId = peerGroupId
|
||||||
self.account = account
|
self.account = account
|
||||||
@ -43,6 +44,7 @@ class ChatListItem: ListViewItem {
|
|||||||
self.content = content
|
self.content = content
|
||||||
self.editing = editing
|
self.editing = editing
|
||||||
self.hasActiveRevealControls = hasActiveRevealControls
|
self.hasActiveRevealControls = hasActiveRevealControls
|
||||||
|
self.selected = selected
|
||||||
self.header = header
|
self.header = header
|
||||||
self.enableContextActions = enableContextActions
|
self.enableContextActions = enableContextActions
|
||||||
self.interaction = interaction
|
self.interaction = interaction
|
||||||
@ -51,7 +53,6 @@ class ChatListItem: ListViewItem {
|
|||||||
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, () -> Void)) -> Void) {
|
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, () -> Void)) -> Void) {
|
||||||
async {
|
async {
|
||||||
let node = ChatListItemNode()
|
let node = ChatListItemNode()
|
||||||
node.setupItem(item: self)
|
|
||||||
let (first, last, firstWithHeader, nextIsPinned) = ChatListItem.mergeType(item: self, previousItem: previousItem, nextItem: nextItem)
|
let (first, last, firstWithHeader, nextIsPinned) = ChatListItem.mergeType(item: self, previousItem: previousItem, nextItem: nextItem)
|
||||||
node.insets = ChatListItemNode.insets(first: first, last: last, firstWithHeader: firstWithHeader)
|
node.insets = ChatListItemNode.insets(first: first, last: last, firstWithHeader: firstWithHeader)
|
||||||
|
|
||||||
@ -63,6 +64,7 @@ class ChatListItem: ListViewItem {
|
|||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
completion(node, {
|
completion(node, {
|
||||||
return (nil, {
|
return (nil, {
|
||||||
|
node.setupItem(item: self)
|
||||||
apply(false)
|
apply(false)
|
||||||
node.updateIsHighlighted(transition: .immediate)
|
node.updateIsHighlighted(transition: .immediate)
|
||||||
})
|
})
|
||||||
@ -231,7 +233,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
var verificationIconNode: ASImageNode?
|
var verificationIconNode: ASImageNode?
|
||||||
let mutedIconNode: ASImageNode
|
let mutedIconNode: ASImageNode
|
||||||
|
|
||||||
var editableControlNode: ItemListEditableControlNode?
|
var selectableControlNode: ItemListSelectableControlNode?
|
||||||
var reorderControlNode: ItemListEditableReorderControlNode?
|
var reorderControlNode: ItemListEditableReorderControlNode?
|
||||||
|
|
||||||
var layoutParams: (ChatListItem, first: Bool, last: Bool, firstWithHeader: Bool, nextIsPinned: Bool, ListViewItemLayoutParams)?
|
var layoutParams: (ChatListItem, first: Bool, last: Bool, firstWithHeader: Bool, nextIsPinned: Bool, ListViewItemLayoutParams)?
|
||||||
@ -239,7 +241,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
private var isHighlighted: Bool = false
|
private var isHighlighted: Bool = false
|
||||||
|
|
||||||
override var canBeSelected: Bool {
|
override var canBeSelected: Bool {
|
||||||
if self.editableControlNode != nil {
|
if self.selectableControlNode != nil {
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
return super.canBeSelected
|
return super.canBeSelected
|
||||||
@ -393,6 +395,13 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func tapped() {
|
||||||
|
guard let item = self.item, item.editing else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
item.interaction.togglePeerSelected(item.index.messageIndex.id.peerId)
|
||||||
|
}
|
||||||
|
|
||||||
func asyncLayout() -> (_ item: ChatListItem, _ params: ListViewItemLayoutParams, _ first: Bool, _ last: Bool, _ firstWithHeader: Bool, _ nextIsPinned: Bool) -> (ListViewItemNodeLayout, (Bool) -> Void) {
|
func asyncLayout() -> (_ item: ChatListItem, _ params: ListViewItemLayoutParams, _ first: Bool, _ last: Bool, _ firstWithHeader: Bool, _ nextIsPinned: Bool) -> (ListViewItemNodeLayout, (Bool) -> Void) {
|
||||||
let dateLayout = TextNode.asyncLayout(self.dateNode)
|
let dateLayout = TextNode.asyncLayout(self.dateNode)
|
||||||
let textLayout = TextNode.asyncLayout(self.textNode)
|
let textLayout = TextNode.asyncLayout(self.textNode)
|
||||||
@ -400,7 +409,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
let authorLayout = TextNode.asyncLayout(self.authorNode)
|
let authorLayout = TextNode.asyncLayout(self.authorNode)
|
||||||
let inputActivitiesLayout = self.inputActivitiesNode.asyncLayout()
|
let inputActivitiesLayout = self.inputActivitiesNode.asyncLayout()
|
||||||
let badgeTextLayout = TextNode.asyncLayout(self.badgeTextNode)
|
let badgeTextLayout = TextNode.asyncLayout(self.badgeTextNode)
|
||||||
let editableControlLayout = ItemListEditableControlNode.asyncLayout(self.editableControlNode)
|
let selectableControlLayout = ItemListSelectableControlNode.asyncLayout(self.selectableControlNode)
|
||||||
let reorderControlLayout = ItemListEditableReorderControlNode.asyncLayout(self.reorderControlNode)
|
let reorderControlLayout = ItemListEditableReorderControlNode.asyncLayout(self.reorderControlNode)
|
||||||
|
|
||||||
let currentItem = self.layoutParams?.0
|
let currentItem = self.layoutParams?.0
|
||||||
@ -488,17 +497,17 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
var currentVerificationIconImage: UIImage?
|
var currentVerificationIconImage: UIImage?
|
||||||
var currentSecretIconImage: UIImage?
|
var currentSecretIconImage: UIImage?
|
||||||
|
|
||||||
var editableControlSizeAndApply: (CGSize, () -> ItemListEditableControlNode)?
|
var selectableControlSizeAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)?
|
||||||
var reorderControlSizeAndApply: (CGSize, () -> ItemListEditableReorderControlNode)?
|
var reorderControlSizeAndApply: (CGSize, () -> ItemListEditableReorderControlNode)?
|
||||||
|
|
||||||
let editingOffset: CGFloat
|
let editingOffset: CGFloat
|
||||||
var reorderInset: CGFloat = 0.0
|
var reorderInset: CGFloat = 0.0
|
||||||
if item.editing {
|
if item.editing {
|
||||||
let sizeAndApply = editableControlLayout(itemHeight, item.presentationData.theme, isPeerGroup)
|
let sizeAndApply = selectableControlLayout(item.presentationData.theme.list.itemCheckColors.strokeColor, item.presentationData.theme.list.itemCheckColors.fillColor, item.presentationData.theme.list.itemCheckColors.foregroundColor, item.selected, true)
|
||||||
if !isAd {
|
if !isAd {
|
||||||
editableControlSizeAndApply = sizeAndApply
|
selectableControlSizeAndApply = sizeAndApply
|
||||||
}
|
}
|
||||||
editingOffset = sizeAndApply.0.width
|
editingOffset = sizeAndApply.0
|
||||||
|
|
||||||
if item.index.pinningIndex != nil && !isAd {
|
if item.index.pinningIndex != nil && !isAd {
|
||||||
let sizeAndApply = reorderControlLayout(itemHeight, item.presentationData.theme)
|
let sizeAndApply = reorderControlLayout(itemHeight, item.presentationData.theme)
|
||||||
@ -758,32 +767,30 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var crossfadeContent = false
|
var crossfadeContent = false
|
||||||
if let editableControlSizeAndApply = editableControlSizeAndApply {
|
if let selectableControlSizeAndApply = selectableControlSizeAndApply {
|
||||||
if strongSelf.editableControlNode == nil {
|
let selectableControlSize = CGSize(width: selectableControlSizeAndApply.0, height: layout.contentSize.height)
|
||||||
|
let selectableControlFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset, y: 0.0), size: selectableControlSize)
|
||||||
|
if strongSelf.selectableControlNode == nil {
|
||||||
crossfadeContent = true
|
crossfadeContent = true
|
||||||
let editableControlNode = editableControlSizeAndApply.1()
|
let selectableControlNode = selectableControlSizeAndApply.1(selectableControlSize, false)
|
||||||
editableControlNode.tapped = {
|
strongSelf.selectableControlNode = selectableControlNode
|
||||||
if let strongSelf = self {
|
strongSelf.addSubnode(selectableControlNode)
|
||||||
strongSelf.setRevealOptionsOpened(true, animated: true)
|
selectableControlNode.frame = selectableControlFrame
|
||||||
strongSelf.revealOptionsInteractivelyOpened()
|
transition.animatePosition(node: selectableControlNode, from: CGPoint(x: -selectableControlFrame.size.width / 2.0, y: selectableControlFrame.midY))
|
||||||
}
|
selectableControlNode.alpha = 0.0
|
||||||
}
|
transition.updateAlpha(node: selectableControlNode, alpha: 1.0)
|
||||||
strongSelf.editableControlNode = editableControlNode
|
} else if let selectableControlNode = strongSelf.selectableControlNode {
|
||||||
strongSelf.addSubnode(editableControlNode)
|
transition.updateFrame(node: selectableControlNode, frame: selectableControlFrame)
|
||||||
let editableControlFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset, y: 0.0), size: editableControlSizeAndApply.0)
|
let _ = selectableControlSizeAndApply.1(selectableControlSize, transition.isAnimated)
|
||||||
editableControlNode.frame = editableControlFrame
|
|
||||||
transition.animatePosition(node: editableControlNode, from: CGPoint(x: -editableControlFrame.size.width / 2.0, y: editableControlFrame.midY))
|
|
||||||
editableControlNode.alpha = 0.0
|
|
||||||
transition.updateAlpha(node: editableControlNode, alpha: 1.0)
|
|
||||||
}
|
}
|
||||||
} else if let editableControlNode = strongSelf.editableControlNode {
|
} else if let selectableControlNode = strongSelf.selectableControlNode {
|
||||||
crossfadeContent = true
|
crossfadeContent = true
|
||||||
var editableControlFrame = editableControlNode.frame
|
var selectableControlFrame = selectableControlNode.frame
|
||||||
editableControlFrame.origin.x = -editableControlFrame.size.width
|
selectableControlFrame.origin.x = -selectableControlFrame.size.width
|
||||||
strongSelf.editableControlNode = nil
|
strongSelf.selectableControlNode = nil
|
||||||
transition.updateAlpha(node: editableControlNode, alpha: 0.0)
|
transition.updateAlpha(node: selectableControlNode, alpha: 0.0)
|
||||||
transition.updateFrame(node: editableControlNode, frame: editableControlFrame, completion: { [weak editableControlNode] _ in
|
transition.updateFrame(node: selectableControlNode, frame: selectableControlFrame, completion: { [weak selectableControlNode] _ in
|
||||||
editableControlNode?.removeFromSupernode()
|
selectableControlNode?.removeFromSupernode()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1010,6 +1017,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
transition.animatePosition(node: strongSelf.authorNode, from: CGPoint(x: authorPosition.x - contentDeltaX, y: authorPosition.y))
|
transition.animatePosition(node: strongSelf.authorNode, from: CGPoint(x: authorPosition.x - contentDeltaX, y: authorPosition.y))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if crossfadeContent {
|
||||||
|
strongSelf.authorNode.recursivelyEnsureDisplaySynchronously(true)
|
||||||
|
strongSelf.titleNode.recursivelyEnsureDisplaySynchronously(true)
|
||||||
|
strongSelf.textNode.recursivelyEnsureDisplaySynchronously(true)
|
||||||
|
}
|
||||||
|
|
||||||
let separatorInset: CGFloat
|
let separatorInset: CGFloat
|
||||||
if (!nextIsPinned && item.index.pinningIndex != nil) || last {
|
if (!nextIsPinned && item.index.pinningIndex != nil) || last {
|
||||||
separatorInset = 0.0
|
separatorInset = 0.0
|
||||||
@ -1020,7 +1033,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: separatorInset, y: itemHeight - separatorHeight), size: CGSize(width: params.width - separatorInset, height: separatorHeight)))
|
transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: separatorInset, y: itemHeight - separatorHeight), size: CGSize(width: params.width - separatorInset, height: separatorHeight)))
|
||||||
|
|
||||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||||
if item.index.pinningIndex != nil {
|
if item.selected {
|
||||||
|
strongSelf.backgroundNode.backgroundColor = theme.itemSelectedBackgroundColor
|
||||||
|
} else if item.index.pinningIndex != nil {
|
||||||
strongSelf.backgroundNode.backgroundColor = theme.pinnedItemBackgroundColor
|
strongSelf.backgroundNode.backgroundColor = theme.pinnedItemBackgroundColor
|
||||||
} else {
|
} else {
|
||||||
strongSelf.backgroundNode.backgroundColor = theme.itemBackgroundColor
|
strongSelf.backgroundNode.backgroundColor = theme.itemBackgroundColor
|
||||||
@ -1059,11 +1074,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
if let _ = self.item, let params = self.layoutParams?.5 {
|
if let _ = self.item, let params = self.layoutParams?.5 {
|
||||||
let editingOffset: CGFloat
|
let editingOffset: CGFloat
|
||||||
if let editableControlNode = self.editableControlNode {
|
if let selectableControlNode = self.selectableControlNode {
|
||||||
editingOffset = editableControlNode.bounds.size.width
|
editingOffset = selectableControlNode.bounds.size.width
|
||||||
var editableControlFrame = editableControlNode.frame
|
var selectableControlFrame = selectableControlNode.frame
|
||||||
editableControlFrame.origin.x = params.leftInset + offset
|
selectableControlFrame.origin.x = params.leftInset + offset
|
||||||
transition.updateFrame(node: editableControlNode, frame: editableControlFrame)
|
transition.updateFrame(node: selectableControlNode, frame: selectableControlFrame)
|
||||||
} else {
|
} else {
|
||||||
editingOffset = 0.0
|
editingOffset = 0.0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,7 +75,9 @@ public func chatListItemStrings(strings: PresentationStrings, message: Message?,
|
|||||||
if message.text.isEmpty {
|
if message.text.isEmpty {
|
||||||
isVideo = true
|
isVideo = true
|
||||||
} else if #available(iOSApplicationExtension 9.0, *) {
|
} else if #available(iOSApplicationExtension 9.0, *) {
|
||||||
messageText = "📹 \(messageText)"
|
if !fileMedia.isAnimated {
|
||||||
|
messageText = "📹 \(messageText)"
|
||||||
|
}
|
||||||
break inner
|
break inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,6 +59,7 @@ final class ChatListHighlightedLocation {
|
|||||||
final class ChatListNodeInteraction {
|
final class ChatListNodeInteraction {
|
||||||
let activateSearch: () -> Void
|
let activateSearch: () -> Void
|
||||||
let peerSelected: (Peer) -> Void
|
let peerSelected: (Peer) -> Void
|
||||||
|
let togglePeerSelected: (PeerId) -> Void
|
||||||
let messageSelected: (Message, Bool) -> Void
|
let messageSelected: (Message, Bool) -> Void
|
||||||
let groupSelected: (PeerGroupId) -> Void
|
let groupSelected: (PeerGroupId) -> Void
|
||||||
let addContact: (String) -> Void
|
let addContact: (String) -> Void
|
||||||
@ -71,9 +72,10 @@ final class ChatListNodeInteraction {
|
|||||||
|
|
||||||
var highlightedChatLocation: ChatListHighlightedLocation?
|
var highlightedChatLocation: ChatListHighlightedLocation?
|
||||||
|
|
||||||
init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer) -> Void, messageSelected: @escaping (Message, Bool) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void) {
|
init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, messageSelected: @escaping (Message, Bool) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void) {
|
||||||
self.activateSearch = activateSearch
|
self.activateSearch = activateSearch
|
||||||
self.peerSelected = peerSelected
|
self.peerSelected = peerSelected
|
||||||
|
self.togglePeerSelected = togglePeerSelected
|
||||||
self.messageSelected = messageSelected
|
self.messageSelected = messageSelected
|
||||||
self.groupSelected = groupSelected
|
self.groupSelected = groupSelected
|
||||||
self.addContact = addContact
|
self.addContact = addContact
|
||||||
@ -95,26 +97,11 @@ final class ChatListNodePeerInputActivities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ChatListNodeState: Equatable {
|
struct ChatListNodeState: Equatable {
|
||||||
let presentationData: ChatListPresentationData
|
var presentationData: ChatListPresentationData
|
||||||
let editing: Bool
|
var editing: Bool
|
||||||
let peerIdWithRevealedOptions: PeerId?
|
var peerIdWithRevealedOptions: PeerId?
|
||||||
let peerInputActivities: ChatListNodePeerInputActivities?
|
var selectedPeerIds: Set<PeerId>
|
||||||
|
var peerInputActivities: ChatListNodePeerInputActivities?
|
||||||
func withUpdatedPresentationData(_ presentationData: ChatListPresentationData) -> ChatListNodeState {
|
|
||||||
return ChatListNodeState(presentationData: presentationData, editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, peerInputActivities: self.peerInputActivities)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withUpdatedEditing(_ editing: Bool) -> ChatListNodeState {
|
|
||||||
return ChatListNodeState(presentationData: self.presentationData, editing: editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, peerInputActivities: self.peerInputActivities)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withUpdatedPeerIdWithRevealedOptions(_ peerIdWithRevealedOptions: PeerId?) -> ChatListNodeState {
|
|
||||||
return ChatListNodeState(presentationData: self.presentationData, editing: self.editing, peerIdWithRevealedOptions: peerIdWithRevealedOptions, peerInputActivities: self.peerInputActivities)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withUpdatedPeerInputActivities(_ peerInputActivities: ChatListNodePeerInputActivities?) -> ChatListNodeState {
|
|
||||||
return ChatListNodeState(presentationData: self.presentationData, editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, peerInputActivities: peerInputActivities)
|
|
||||||
}
|
|
||||||
|
|
||||||
static func ==(lhs: ChatListNodeState, rhs: ChatListNodeState) -> Bool {
|
static func ==(lhs: ChatListNodeState, rhs: ChatListNodeState) -> Bool {
|
||||||
if lhs.presentationData !== rhs.presentationData {
|
if lhs.presentationData !== rhs.presentationData {
|
||||||
@ -126,6 +113,9 @@ struct ChatListNodeState: Equatable {
|
|||||||
if lhs.peerIdWithRevealedOptions != rhs.peerIdWithRevealedOptions {
|
if lhs.peerIdWithRevealedOptions != rhs.peerIdWithRevealedOptions {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.selectedPeerIds != rhs.selectedPeerIds {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.peerInputActivities !== rhs.peerInputActivities {
|
if lhs.peerInputActivities !== rhs.peerInputActivities {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -136,14 +126,14 @@ struct ChatListNodeState: Equatable {
|
|||||||
private func mappedInsertEntries(account: Account, nodeInteraction: ChatListNodeInteraction, peerGroupId: PeerGroupId?, mode: ChatListNodeMode, entries: [ChatListNodeViewTransitionInsertEntry]) -> [ListViewInsertItem] {
|
private func mappedInsertEntries(account: Account, nodeInteraction: ChatListNodeInteraction, peerGroupId: PeerGroupId?, mode: ChatListNodeMode, entries: [ChatListNodeViewTransitionInsertEntry]) -> [ListViewInsertItem] {
|
||||||
return entries.map { entry -> ListViewInsertItem in
|
return entries.map { entry -> ListViewInsertItem in
|
||||||
switch entry.entry {
|
switch entry.entry {
|
||||||
case let .SearchEntry(theme, text):
|
case let .SearchEntry(theme, text, isEnabled):
|
||||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: text, activate: {
|
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, isEnabled: isEnabled, placeholder: text, activate: {
|
||||||
nodeInteraction.activateSearch()
|
nodeInteraction.activateSearch()
|
||||||
}), directionHint: entry.directionHint)
|
}), directionHint: entry.directionHint)
|
||||||
case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, summaryInfo, editing, hasActiveRevealControls, inputActivities, isAd):
|
case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, isAd):
|
||||||
switch mode {
|
switch mode {
|
||||||
case .chatList:
|
case .chatList:
|
||||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false), editing: editing, hasActiveRevealControls: hasActiveRevealControls, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint)
|
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint)
|
||||||
case let .peers(filter):
|
case let .peers(filter):
|
||||||
let itemPeer = peer.chatMainPeer
|
let itemPeer = peer.chatMainPeer
|
||||||
var chatPeer: Peer?
|
var chatPeer: Peer?
|
||||||
@ -201,7 +191,7 @@ private func mappedInsertEntries(account: Account, nodeInteraction: ChatListNode
|
|||||||
case let .HoleEntry(_, theme):
|
case let .HoleEntry(_, theme):
|
||||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListHoleItem(theme: theme), directionHint: entry.directionHint)
|
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListHoleItem(theme: theme), directionHint: entry.directionHint)
|
||||||
case let .GroupReferenceEntry(index, presentationData, groupId, message, topPeers, counters, editing):
|
case let .GroupReferenceEntry(index, presentationData, groupId, message, topPeers, counters, editing):
|
||||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .groupReference(groupId: groupId, message: message, topPeers: topPeers, counters: counters), editing: editing, hasActiveRevealControls: false, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint)
|
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .groupReference(groupId: groupId, message: message, topPeers: topPeers, counters: counters), editing: editing, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,14 +199,14 @@ private func mappedInsertEntries(account: Account, nodeInteraction: ChatListNode
|
|||||||
private func mappedUpdateEntries(account: Account, nodeInteraction: ChatListNodeInteraction, peerGroupId: PeerGroupId?, mode: ChatListNodeMode, entries: [ChatListNodeViewTransitionUpdateEntry]) -> [ListViewUpdateItem] {
|
private func mappedUpdateEntries(account: Account, nodeInteraction: ChatListNodeInteraction, peerGroupId: PeerGroupId?, mode: ChatListNodeMode, entries: [ChatListNodeViewTransitionUpdateEntry]) -> [ListViewUpdateItem] {
|
||||||
return entries.map { entry -> ListViewUpdateItem in
|
return entries.map { entry -> ListViewUpdateItem in
|
||||||
switch entry.entry {
|
switch entry.entry {
|
||||||
case let .SearchEntry(theme, text):
|
case let .SearchEntry(theme, text, isEnabled):
|
||||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: text, activate: {
|
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, isEnabled: isEnabled, placeholder: text, activate: {
|
||||||
nodeInteraction.activateSearch()
|
nodeInteraction.activateSearch()
|
||||||
}), directionHint: entry.directionHint)
|
}), directionHint: entry.directionHint)
|
||||||
case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, summaryInfo, editing, hasActiveRevealControls, inputActivities, isAd):
|
case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, isAd):
|
||||||
switch mode {
|
switch mode {
|
||||||
case .chatList:
|
case .chatList:
|
||||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false), editing: editing, hasActiveRevealControls: hasActiveRevealControls, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint)
|
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint)
|
||||||
case let .peers(filter):
|
case let .peers(filter):
|
||||||
let itemPeer = peer.chatMainPeer
|
let itemPeer = peer.chatMainPeer
|
||||||
var chatPeer: Peer?
|
var chatPeer: Peer?
|
||||||
@ -242,7 +232,7 @@ private func mappedUpdateEntries(account: Account, nodeInteraction: ChatListNode
|
|||||||
case let .HoleEntry(_, theme):
|
case let .HoleEntry(_, theme):
|
||||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListHoleItem(theme: theme), directionHint: entry.directionHint)
|
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListHoleItem(theme: theme), directionHint: entry.directionHint)
|
||||||
case let .GroupReferenceEntry(index, presentationData, groupId, message, topPeers, counters, editing):
|
case let .GroupReferenceEntry(index, presentationData, groupId, message, topPeers, counters, editing):
|
||||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .groupReference(groupId: groupId, message: message, topPeers: topPeers, counters: counters), editing: editing, hasActiveRevealControls: false, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint)
|
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .groupReference(groupId: groupId, message: message, topPeers: topPeers, counters: counters), editing: editing, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -309,8 +299,11 @@ final class ChatListNode: ListView {
|
|||||||
private var dequeuedInitialTransitionOnLayout = false
|
private var dequeuedInitialTransitionOnLayout = false
|
||||||
private var enqueuedTransition: (ChatListNodeListViewTransition, () -> Void)?
|
private var enqueuedTransition: (ChatListNodeListViewTransition, () -> Void)?
|
||||||
|
|
||||||
private var currentState: ChatListNodeState
|
private(set) var currentState: ChatListNodeState
|
||||||
private let statePromise: ValuePromise<ChatListNodeState>
|
private let statePromise: ValuePromise<ChatListNodeState>
|
||||||
|
var state: Signal<ChatListNodeState, NoError> {
|
||||||
|
return self.statePromise.get()
|
||||||
|
}
|
||||||
|
|
||||||
private var currentLocation: ChatListNodeLocation?
|
private var currentLocation: ChatListNodeLocation?
|
||||||
private let chatListLocation = ValuePromise<ChatListNodeLocation>()
|
private let chatListLocation = ValuePromise<ChatListNodeLocation>()
|
||||||
@ -348,7 +341,7 @@ final class ChatListNode: ListView {
|
|||||||
self.controlsHistoryPreload = controlsHistoryPreload
|
self.controlsHistoryPreload = controlsHistoryPreload
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
|
||||||
self.currentState = ChatListNodeState(presentationData: ChatListPresentationData(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations), editing: false, peerIdWithRevealedOptions: nil, peerInputActivities: nil)
|
self.currentState = ChatListNodeState(presentationData: ChatListPresentationData(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations), editing: false, peerIdWithRevealedOptions: nil, selectedPeerIds: Set(), peerInputActivities: nil)
|
||||||
self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true)
|
self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true)
|
||||||
|
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
@ -365,6 +358,16 @@ final class ChatListNode: ListView {
|
|||||||
if let strongSelf = self, let peerSelected = strongSelf.peerSelected {
|
if let strongSelf = self, let peerSelected = strongSelf.peerSelected {
|
||||||
peerSelected(peer.id, true, false)
|
peerSelected(peer.id, true, false)
|
||||||
}
|
}
|
||||||
|
}, togglePeerSelected: { [weak self] peerId in
|
||||||
|
self?.updateState { state in
|
||||||
|
var state = state
|
||||||
|
if state.selectedPeerIds.contains(peerId) {
|
||||||
|
state.selectedPeerIds.remove(peerId)
|
||||||
|
} else {
|
||||||
|
state.selectedPeerIds.insert(peerId)
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
}, messageSelected: { [weak self] message, isAd in
|
}, messageSelected: { [weak self] message, isAd in
|
||||||
if let strongSelf = self, let peerSelected = strongSelf.peerSelected {
|
if let strongSelf = self, let peerSelected = strongSelf.peerSelected {
|
||||||
peerSelected(message.id.peerId, true, isAd)
|
peerSelected(message.id.peerId, true, isAd)
|
||||||
@ -378,7 +381,9 @@ final class ChatListNode: ListView {
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.updateState { state in
|
strongSelf.updateState { state in
|
||||||
if (peerId == nil && fromPeerId == state.peerIdWithRevealedOptions) || (peerId != nil && fromPeerId == nil) {
|
if (peerId == nil && fromPeerId == state.peerIdWithRevealedOptions) || (peerId != nil && fromPeerId == nil) {
|
||||||
return state.withUpdatedPeerIdWithRevealedOptions(peerId)
|
var state = state
|
||||||
|
state.peerIdWithRevealedOptions = peerId
|
||||||
|
return state
|
||||||
} else {
|
} else {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
@ -398,8 +403,10 @@ final class ChatListNode: ListView {
|
|||||||
}, setPeerMuted: { [weak self] peerId, _ in
|
}, setPeerMuted: { [weak self] peerId, _ in
|
||||||
let _ = (togglePeerMuted(account: account, peerId: peerId)
|
let _ = (togglePeerMuted(account: account, peerId: peerId)
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
self?.updateState {
|
self?.updateState { state in
|
||||||
return $0.withUpdatedPeerIdWithRevealedOptions(nil)
|
var state = state
|
||||||
|
state.peerIdWithRevealedOptions = nil
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, deletePeer: { [weak self] peerId in
|
}, deletePeer: { [weak self] peerId in
|
||||||
@ -413,8 +420,10 @@ final class ChatListNode: ListView {
|
|||||||
|
|
||||||
let _ = (togglePeerUnreadMarkInteractively(postbox: account.postbox, viewTracker: account.viewTracker, peerId: peerId)
|
let _ = (togglePeerUnreadMarkInteractively(postbox: account.postbox, viewTracker: account.viewTracker, peerId: peerId)
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
self?.updateState {
|
self?.updateState { state in
|
||||||
return $0.withUpdatedPeerIdWithRevealedOptions(nil)
|
var state = state
|
||||||
|
state.peerIdWithRevealedOptions = nil
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -443,13 +452,12 @@ final class ChatListNode: ListView {
|
|||||||
|
|
||||||
let entries = chatListNodeEntriesForView(update.view, state: state, savedMessagesPeer: savedMessagesPeer, mode: mode).filter { entry in
|
let entries = chatListNodeEntriesForView(update.view, state: state, savedMessagesPeer: savedMessagesPeer, mode: mode).filter { entry in
|
||||||
switch entry {
|
switch entry {
|
||||||
case let .PeerEntry(_, _, _, _, _, _, peer, _, _, _, _, _):
|
case let .PeerEntry(_, _, _, _, _, _, peer, _, _, _, _, _, _):
|
||||||
//ChatListNodePeersFilter
|
//ChatListNodePeersFilter
|
||||||
switch mode {
|
switch mode {
|
||||||
case .chatList:
|
case .chatList:
|
||||||
return true
|
return true
|
||||||
case let .peers(filter):
|
case let .peers(filter):
|
||||||
|
|
||||||
guard !filter.contains(.excludeSavedMessages) || peer.peerId != currentPeerId else { return false }
|
guard !filter.contains(.excludeSavedMessages) || peer.peerId != currentPeerId else { return false }
|
||||||
guard !filter.contains(.excludeSecretChats) || peer.peerId.namespace != Namespaces.Peer.SecretChat else { return false }
|
guard !filter.contains(.excludeSecretChats) || peer.peerId.namespace != Namespaces.Peer.SecretChat else { return false }
|
||||||
guard !filter.contains(.onlyPrivateChats) || peer.peerId.namespace == Namespaces.Peer.CloudUser else { return false }
|
guard !filter.contains(.onlyPrivateChats) || peer.peerId.namespace == Namespaces.Peer.CloudUser else { return false }
|
||||||
@ -533,7 +541,7 @@ final class ChatListNode: ListView {
|
|||||||
var updatedPinnedCount = 0
|
var updatedPinnedCount = 0
|
||||||
if let previous = previousView {
|
if let previous = previousView {
|
||||||
for entry in previous.filteredEntries {
|
for entry in previous.filteredEntries {
|
||||||
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _) = entry {
|
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _) = entry {
|
||||||
if index.pinningIndex != nil {
|
if index.pinningIndex != nil {
|
||||||
previousPinnedCount += 1
|
previousPinnedCount += 1
|
||||||
}
|
}
|
||||||
@ -541,7 +549,7 @@ final class ChatListNode: ListView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for entry in processedView.filteredEntries {
|
for entry in processedView.filteredEntries {
|
||||||
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _) = entry {
|
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _) = entry {
|
||||||
if index.pinningIndex != nil {
|
if index.pinningIndex != nil {
|
||||||
updatedPinnedCount += 1
|
updatedPinnedCount += 1
|
||||||
}
|
}
|
||||||
@ -550,6 +558,9 @@ final class ChatListNode: ListView {
|
|||||||
if previousPinnedCount != updatedPinnedCount {
|
if previousPinnedCount != updatedPinnedCount {
|
||||||
disableAnimations = false
|
disableAnimations = false
|
||||||
}
|
}
|
||||||
|
if previousState.selectedPeerIds != state.selectedPeerIds {
|
||||||
|
disableAnimations = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return preparedChatListNodeViewTransition(from: previousView, to: processedView, reason: reason, disableAnimations: disableAnimations, account: account, scrollPosition: updatedScrollPosition)
|
return preparedChatListNodeViewTransition(from: previousView, to: processedView, reason: reason, disableAnimations: disableAnimations, account: account, scrollPosition: updatedScrollPosition)
|
||||||
@ -593,7 +604,7 @@ final class ChatListNode: ListView {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch chatListView.filteredEntries[entryCount - i - 1] {
|
switch chatListView.filteredEntries[entryCount - i - 1] {
|
||||||
case let .PeerEntry(_, _, _, readState, notificationSettings, _, _, _, _, _, _, _):
|
case let .PeerEntry(_, _, _, readState, notificationSettings, _, _, _, _, _, _, _, _):
|
||||||
if let readState = readState {
|
if let readState = readState {
|
||||||
let count = readState.count
|
let count = readState.count
|
||||||
rawUnreadCount += count
|
rawUnreadCount += count
|
||||||
@ -716,8 +727,10 @@ final class ChatListNode: ListView {
|
|||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] activities in
|
|> deliverOnMainQueue).start(next: { [weak self] activities in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.updateState { current in
|
strongSelf.updateState { state in
|
||||||
return current.withUpdatedPeerInputActivities(activities)
|
var state = state
|
||||||
|
state.peerInputActivities = activities
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -725,8 +738,10 @@ final class ChatListNode: ListView {
|
|||||||
self.beganInteractiveDragging = { [weak self] in
|
self.beganInteractiveDragging = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if strongSelf.currentState.peerIdWithRevealedOptions != nil {
|
if strongSelf.currentState.peerIdWithRevealedOptions != nil {
|
||||||
strongSelf.updateState {
|
strongSelf.updateState { state in
|
||||||
return $0.withUpdatedPeerIdWithRevealedOptions(nil)
|
var state = state
|
||||||
|
state.peerIdWithRevealedOptions = nil
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -740,7 +755,7 @@ final class ChatListNode: ListView {
|
|||||||
var referenceId: PinnedItemId?
|
var referenceId: PinnedItemId?
|
||||||
var beforeAll = false
|
var beforeAll = false
|
||||||
switch toEntry {
|
switch toEntry {
|
||||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, isAd):
|
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, isAd):
|
||||||
if isAd {
|
if isAd {
|
||||||
beforeAll = true
|
beforeAll = true
|
||||||
} else {
|
} else {
|
||||||
@ -760,7 +775,7 @@ final class ChatListNode: ListView {
|
|||||||
|
|
||||||
var itemId: PinnedItemId?
|
var itemId: PinnedItemId?
|
||||||
switch fromEntry {
|
switch fromEntry {
|
||||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _):
|
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
itemId = .peer(index.messageIndex.id.peerId)
|
itemId = .peer(index.messageIndex.id.peerId)
|
||||||
case let .GroupReferenceEntry(_, _, groupId, _, _, _, _):
|
case let .GroupReferenceEntry(_, _, groupId, _, _, _, _):
|
||||||
itemId = .group(groupId)
|
itemId = .group(groupId)
|
||||||
@ -860,8 +875,10 @@ final class ChatListNode: ListView {
|
|||||||
self.keepTopItemOverscrollBackground = ListViewKeepTopItemOverscrollBackground(color: theme.chatList.pinnedItemBackgroundColor, direction: true)
|
self.keepTopItemOverscrollBackground = ListViewKeepTopItemOverscrollBackground(color: theme.chatList.pinnedItemBackgroundColor, direction: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateState {
|
self.updateState { state in
|
||||||
return $0.withUpdatedPresentationData(ChatListPresentationData(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations))
|
var state = state
|
||||||
|
state.presentationData = ChatListPresentationData(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations)
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1080,7 +1097,7 @@ final class ChatListNode: ListView {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch chatListView.filteredEntries[entryCount - i - 1] {
|
switch chatListView.filteredEntries[entryCount - i - 1] {
|
||||||
case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _):
|
case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _):
|
||||||
if interaction.highlightedChatLocation?.location == ChatLocation.peer(peer.peerId) {
|
if interaction.highlightedChatLocation?.location == ChatLocation.peer(peer.peerId) {
|
||||||
current = (index, peer.peerId, entryCount - i - 1)
|
current = (index, peer.peerId, entryCount - i - 1)
|
||||||
break outer
|
break outer
|
||||||
@ -1113,12 +1130,12 @@ final class ChatListNode: ListView {
|
|||||||
break
|
break
|
||||||
case .previous(unread: false), .next(unread: false):
|
case .previous(unread: false), .next(unread: false):
|
||||||
if current.2 != entryCount - range.firstIndex - 1 && entryCount > 2 {
|
if current.2 != entryCount - range.firstIndex - 1 && entryCount > 2 {
|
||||||
if case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _) = chatListView.filteredEntries[current.2 - 1] {
|
if case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 - 1] {
|
||||||
next = (index, peer.peerId)
|
next = (index, peer.peerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if current.2 != entryCount - range.lastIndex - 2 && entryCount > 2 {
|
if current.2 != entryCount - range.lastIndex - 2 && entryCount > 2 {
|
||||||
if case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _) = chatListView.filteredEntries[current.2 + 1] {
|
if case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 + 1] {
|
||||||
previous = (index, peer.peerId)
|
previous = (index, peer.peerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1177,7 +1194,7 @@ final class ChatListNode: ListView {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch chatListView.filteredEntries[entryCount - i - 1] {
|
switch chatListView.filteredEntries[entryCount - i - 1] {
|
||||||
case let .PeerEntry(index, _, _, readState, notificationSettings, _, _, _, _, _, _, _):
|
case let .PeerEntry(index, _, _, readState, notificationSettings, _, _, _, _, _, _, _, _):
|
||||||
return index
|
return index
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
|||||||
@ -52,8 +52,8 @@ enum ChatListNodeEntryId: Hashable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum ChatListNodeEntry: Comparable, Identifiable {
|
enum ChatListNodeEntry: Comparable, Identifiable {
|
||||||
case SearchEntry(theme: PresentationTheme, text: String)
|
case SearchEntry(theme: PresentationTheme, text: String, isEnabled: Bool)
|
||||||
case PeerEntry(index: ChatListIndex, presentationData: ChatListPresentationData, message: Message?, readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, peer: RenderedPeer, summaryInfo: ChatListMessageTagSummaryInfo, editing: Bool, hasActiveRevealControls: Bool, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool)
|
case PeerEntry(index: ChatListIndex, presentationData: ChatListPresentationData, message: Message?, readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, peer: RenderedPeer, summaryInfo: ChatListMessageTagSummaryInfo, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool)
|
||||||
case HoleEntry(ChatListHole, theme: PresentationTheme)
|
case HoleEntry(ChatListHole, theme: PresentationTheme)
|
||||||
case GroupReferenceEntry(index: ChatListIndex, presentationData: ChatListPresentationData, groupId: PeerGroupId, message: Message?, topPeers: [Peer], counters: GroupReferenceUnreadCounters, editing: Bool)
|
case GroupReferenceEntry(index: ChatListIndex, presentationData: ChatListPresentationData, groupId: PeerGroupId, message: Message?, topPeers: [Peer], counters: GroupReferenceUnreadCounters, editing: Bool)
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
|||||||
switch self {
|
switch self {
|
||||||
case .SearchEntry:
|
case .SearchEntry:
|
||||||
return ChatListIndex.absoluteUpperBound
|
return ChatListIndex.absoluteUpperBound
|
||||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _):
|
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
return index
|
return index
|
||||||
case let .HoleEntry(hole, _):
|
case let .HoleEntry(hole, _):
|
||||||
return ChatListIndex(pinningIndex: nil, messageIndex: hole.index)
|
return ChatListIndex(pinningIndex: nil, messageIndex: hole.index)
|
||||||
@ -74,7 +74,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
|||||||
switch self {
|
switch self {
|
||||||
case .SearchEntry:
|
case .SearchEntry:
|
||||||
return .Search
|
return .Search
|
||||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _):
|
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
return .PeerId(index.messageIndex.id.peerId.toInt64())
|
return .PeerId(index.messageIndex.id.peerId.toInt64())
|
||||||
case let .HoleEntry(hole, _):
|
case let .HoleEntry(hole, _):
|
||||||
return .Hole(Int64(hole.index.id.id))
|
return .Hole(Int64(hole.index.id.id))
|
||||||
@ -89,15 +89,15 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
|||||||
|
|
||||||
static func ==(lhs: ChatListNodeEntry, rhs: ChatListNodeEntry) -> Bool {
|
static func ==(lhs: ChatListNodeEntry, rhs: ChatListNodeEntry) -> Bool {
|
||||||
switch lhs {
|
switch lhs {
|
||||||
case let .SearchEntry(lhsTheme, lhsText):
|
case let .SearchEntry(lhsTheme, lhsText, lhsEnabled):
|
||||||
if case let .SearchEntry(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .SearchEntry(rhsTheme, rhsText, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsEnabled == rhsEnabled {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .PeerEntry(lhsIndex, lhsPresentationData, lhsMessage, lhsUnreadCount, lhsNotificationSettings, lhsEmbeddedState, lhsPeer, lhsSummaryInfo, lhsEditing, lhsHasRevealControls, lhsInputActivities, lhsAd):
|
case let .PeerEntry(lhsIndex, lhsPresentationData, lhsMessage, lhsUnreadCount, lhsNotificationSettings, lhsEmbeddedState, lhsPeer, lhsSummaryInfo, lhsEditing, lhsHasRevealControls, lhsSelected, lhsInputActivities, lhsAd):
|
||||||
switch rhs {
|
switch rhs {
|
||||||
case let .PeerEntry(rhsIndex, rhsPresentationData, rhsMessage, rhsUnreadCount, rhsNotificationSettings, rhsEmbeddedState, rhsPeer, rhsSummaryInfo, rhsEditing, rhsHasRevealControls, rhsInputActivities, rhsAd):
|
case let .PeerEntry(rhsIndex, rhsPresentationData, rhsMessage, rhsUnreadCount, rhsNotificationSettings, rhsEmbeddedState, rhsPeer, rhsSummaryInfo, rhsEditing, rhsHasRevealControls, rhsSelected, rhsInputActivities, rhsAd):
|
||||||
if lhsIndex != rhsIndex {
|
if lhsIndex != rhsIndex {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -130,6 +130,9 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
|||||||
if lhsHasRevealControls != rhsHasRevealControls {
|
if lhsHasRevealControls != rhsHasRevealControls {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhsSelected != rhsSelected {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhsPeer != rhsPeer {
|
if lhsPeer != rhsPeer {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -221,15 +224,13 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
|
|||||||
pinnedIndexOffset = UInt16(view.additionalItemEntries.count)
|
pinnedIndexOffset = UInt16(view.additionalItemEntries.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
loop: for entry in view.entries {
|
loop: for entry in view.entries {
|
||||||
switch entry {
|
switch entry {
|
||||||
case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, summaryInfo):
|
case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, summaryInfo):
|
||||||
if let savedMessagesPeer = savedMessagesPeer, savedMessagesPeer.id == index.messageIndex.id.peerId {
|
if let savedMessagesPeer = savedMessagesPeer, savedMessagesPeer.id == index.messageIndex.id.peerId {
|
||||||
continue loop
|
continue loop
|
||||||
}
|
}
|
||||||
result.append(.PeerEntry(index: offsetPinnedIndex(index, offset: pinnedIndexOffset), presentationData: state.presentationData, message: message, readState: combinedReadState, notificationSettings: notificationSettings, embeddedInterfaceState: embeddedState, peer: peer, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], isAd: false))
|
result.append(.PeerEntry(index: offsetPinnedIndex(index, offset: pinnedIndexOffset), presentationData: state.presentationData, message: message, readState: combinedReadState, notificationSettings: notificationSettings, embeddedInterfaceState: embeddedState, peer: peer, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], isAd: false))
|
||||||
case let .HoleEntry(hole):
|
case let .HoleEntry(hole):
|
||||||
result.append(.HoleEntry(hole, theme: state.presentationData.theme))
|
result.append(.HoleEntry(hole, theme: state.presentationData.theme))
|
||||||
case let .GroupReferenceEntry(groupId, index, message, topPeers, counters):
|
case let .GroupReferenceEntry(groupId, index, message, topPeers, counters):
|
||||||
@ -240,14 +241,14 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
|
|||||||
}
|
}
|
||||||
if view.laterIndex == nil {
|
if view.laterIndex == nil {
|
||||||
if let savedMessagesPeer = savedMessagesPeer {
|
if let savedMessagesPeer = savedMessagesPeer {
|
||||||
result.append(.PeerEntry(index: ChatListIndex.absoluteUpperBound.predecessor, presentationData: state.presentationData, message: nil, readState: nil, notificationSettings: nil, embeddedInterfaceState: nil, peer: RenderedPeer(peerId: savedMessagesPeer.id, peers: SimpleDictionary([savedMessagesPeer.id: savedMessagesPeer])), summaryInfo: ChatListMessageTagSummaryInfo(), editing: state.editing, hasActiveRevealControls: false, inputActivities: nil, isAd: false))
|
result.append(.PeerEntry(index: ChatListIndex.absoluteUpperBound.predecessor, presentationData: state.presentationData, message: nil, readState: nil, notificationSettings: nil, embeddedInterfaceState: nil, peer: RenderedPeer(peerId: savedMessagesPeer.id, peers: SimpleDictionary([savedMessagesPeer.id: savedMessagesPeer])), summaryInfo: ChatListMessageTagSummaryInfo(), editing: state.editing, hasActiveRevealControls: false, selected: false, inputActivities: nil, isAd: false))
|
||||||
} else {
|
} else {
|
||||||
if !view.additionalItemEntries.isEmpty {
|
if !view.additionalItemEntries.isEmpty {
|
||||||
var pinningIndex: UInt16 = UInt16(view.additionalItemEntries.count - 1)
|
var pinningIndex: UInt16 = UInt16(view.additionalItemEntries.count - 1)
|
||||||
for entry in view.additionalItemEntries.reversed() {
|
for entry in view.additionalItemEntries.reversed() {
|
||||||
switch entry {
|
switch entry {
|
||||||
case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, summaryInfo):
|
case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, summaryInfo):
|
||||||
result.append(.PeerEntry(index: ChatListIndex(pinningIndex: pinningIndex, messageIndex: index.messageIndex), presentationData: state.presentationData, message: message, readState: combinedReadState, notificationSettings: notificationSettings, embeddedInterfaceState: embeddedState, peer: peer, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], isAd: true))
|
result.append(.PeerEntry(index: ChatListIndex(pinningIndex: pinningIndex, messageIndex: index.messageIndex), presentationData: state.presentationData, message: message, readState: combinedReadState, notificationSettings: notificationSettings, embeddedInterfaceState: embeddedState, peer: peer, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], isAd: true))
|
||||||
if pinningIndex != 0 {
|
if pinningIndex != 0 {
|
||||||
pinningIndex -= 1
|
pinningIndex -= 1
|
||||||
}
|
}
|
||||||
@ -258,10 +259,10 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch mode {
|
switch mode {
|
||||||
case .chatList:
|
case .chatList:
|
||||||
result.append(.SearchEntry(theme: state.presentationData.theme, text: view.groupId == nil ? state.presentationData.strings.DialogList_SearchLabel : "Search this feed"))
|
result.append(.SearchEntry(theme: state.presentationData.theme, text: view.groupId == nil ? state.presentationData.strings.DialogList_SearchLabel : "Search this feed", isEnabled: !state.editing))
|
||||||
case .peers:
|
case .peers:
|
||||||
result.append(.SearchEntry(theme: state.presentationData.theme, text: state.presentationData.strings.Common_Search))
|
result.append(.SearchEntry(theme: state.presentationData.theme, text: state.presentationData.strings.Common_Search, isEnabled: !state.editing))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if result.count >= 2, case .SearchEntry = result[result.count - 1], case .HoleEntry = result[result.count - 2] {
|
if result.count >= 2, case .SearchEntry = result[result.count - 1], case .HoleEntry = result[result.count - 2] {
|
||||||
|
|||||||
@ -443,7 +443,7 @@ enum ChatListSearchEntry: Comparable, Identifiable {
|
|||||||
interaction.peerSelected(peer.peer)
|
interaction.peerSelected(peer.peer)
|
||||||
})
|
})
|
||||||
case let .message(message, readState, presentationData):
|
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, ignoreUnreadBadge: true), editing: false, hasActiveRevealControls: false, header: enableHeaders ? ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) : nil, enableContextActions: false, interaction: interaction)
|
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, ignoreUnreadBadge: true), editing: false, hasActiveRevealControls: false, selected: false, header: enableHeaders ? ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) : nil, enableContextActions: false, interaction: interaction)
|
||||||
case let .addContact(phoneNumber, theme, strings):
|
case let .addContact(phoneNumber, theme, strings):
|
||||||
return ContactsAddItem(theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: {
|
return ContactsAddItem(theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: {
|
||||||
interaction.addContact(phoneNumber)
|
interaction.addContact(phoneNumber)
|
||||||
@ -743,6 +743,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
|||||||
openPeer(peer, false)
|
openPeer(peer, false)
|
||||||
let _ = addRecentlySearchedPeer(postbox: account.postbox, peerId: peer.id).start()
|
let _ = addRecentlySearchedPeer(postbox: account.postbox, peerId: peer.id).start()
|
||||||
self?.listNode.clearHighlightAnimated(true)
|
self?.listNode.clearHighlightAnimated(true)
|
||||||
|
}, togglePeerSelected: { _ in
|
||||||
}, messageSelected: { [weak self] message, _ in
|
}, messageSelected: { [weak self] message, _ in
|
||||||
if let peer = message.peers[message.id.peerId] {
|
if let peer = message.peers[message.id.peerId] {
|
||||||
openMessage(peer, message.id)
|
openMessage(peer, message.id)
|
||||||
|
|||||||
@ -11,11 +11,13 @@ class ChatListSearchItem: ListViewItem {
|
|||||||
let selectable: Bool = false
|
let selectable: Bool = false
|
||||||
|
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
|
let isEnabled: Bool
|
||||||
private let placeholder: String
|
private let placeholder: String
|
||||||
private let activate: () -> Void
|
private let activate: () -> Void
|
||||||
|
|
||||||
init(theme: PresentationTheme, placeholder: String, activate: @escaping () -> Void) {
|
init(theme: PresentationTheme, isEnabled: Bool = true, placeholder: String, activate: @escaping () -> Void) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
self.isEnabled = isEnabled
|
||||||
self.placeholder = placeholder
|
self.placeholder = placeholder
|
||||||
self.activate = activate
|
self.activate = activate
|
||||||
}
|
}
|
||||||
@ -30,7 +32,7 @@ class ChatListSearchItem: ListViewItem {
|
|||||||
if let nextItem = nextItem as? ChatListItem, nextItem.index.pinningIndex != nil {
|
if let nextItem = nextItem as? ChatListItem, nextItem.index.pinningIndex != nil {
|
||||||
nextIsPinned = true
|
nextIsPinned = true
|
||||||
}
|
}
|
||||||
let (layout, apply) = makeLayout(self, params, nextIsPinned)
|
let (layout, apply) = makeLayout(self, params, nextIsPinned, self.isEnabled)
|
||||||
|
|
||||||
node.contentSize = layout.contentSize
|
node.contentSize = layout.contentSize
|
||||||
node.insets = layout.insets
|
node.insets = layout.insets
|
||||||
@ -56,7 +58,7 @@ class ChatListSearchItem: ListViewItem {
|
|||||||
if let nextItem = nextItem as? ChatListItem, nextItem.index.pinningIndex != nil {
|
if let nextItem = nextItem as? ChatListItem, nextItem.index.pinningIndex != nil {
|
||||||
nextIsPinned = true
|
nextIsPinned = true
|
||||||
}
|
}
|
||||||
let (nodeLayout, apply) = layout(self, params, nextIsPinned)
|
let (nodeLayout, apply) = layout(self, params, nextIsPinned, self.isEnabled)
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
completion(nodeLayout, {
|
completion(nodeLayout, {
|
||||||
apply(animation.isAnimated)
|
apply(animation.isAnimated)
|
||||||
@ -70,6 +72,7 @@ class ChatListSearchItem: ListViewItem {
|
|||||||
|
|
||||||
class ChatListSearchItemNode: ListViewItemNode {
|
class ChatListSearchItemNode: ListViewItemNode {
|
||||||
let searchBarNode: SearchBarPlaceholderNode
|
let searchBarNode: SearchBarPlaceholderNode
|
||||||
|
private var disabledOverlay: ASDisplayNode?
|
||||||
var placeholder: String?
|
var placeholder: String?
|
||||||
|
|
||||||
fileprivate var activate: (() -> Void)? {
|
fileprivate var activate: (() -> Void)? {
|
||||||
@ -92,17 +95,17 @@ class ChatListSearchItemNode: ListViewItemNode {
|
|||||||
if let nextItem = nextItem as? ChatListItem, nextItem.index.pinningIndex != nil {
|
if let nextItem = nextItem as? ChatListItem, nextItem.index.pinningIndex != nil {
|
||||||
nextIsPinned = true
|
nextIsPinned = true
|
||||||
}
|
}
|
||||||
let (layout, apply) = makeLayout(item as! ChatListSearchItem, params, nextIsPinned)
|
let (layout, apply) = makeLayout(item as! ChatListSearchItem, params, nextIsPinned, (item as! ChatListSearchItem).isEnabled)
|
||||||
apply(false)
|
apply(false)
|
||||||
self.contentSize = layout.contentSize
|
self.contentSize = layout.contentSize
|
||||||
self.insets = layout.insets
|
self.insets = layout.insets
|
||||||
}
|
}
|
||||||
|
|
||||||
func asyncLayout() -> (_ item: ChatListSearchItem, _ params: ListViewItemLayoutParams, _ nextIsPinned: Bool) -> (ListViewItemNodeLayout, (Bool) -> Void) {
|
func asyncLayout() -> (_ item: ChatListSearchItem, _ params: ListViewItemLayoutParams, _ nextIsPinned: Bool, _ isEnabled: Bool) -> (ListViewItemNodeLayout, (Bool) -> Void) {
|
||||||
let searchBarNodeLayout = self.searchBarNode.asyncLayout()
|
let searchBarNodeLayout = self.searchBarNode.asyncLayout()
|
||||||
let placeholder = self.placeholder
|
let placeholder = self.placeholder
|
||||||
|
|
||||||
return { item, params, nextIsPinned in
|
return { item, params, nextIsPinned, isEnabled in
|
||||||
let baseWidth = params.width - params.leftInset - params.rightInset
|
let baseWidth = params.width - params.leftInset - params.rightInset
|
||||||
|
|
||||||
let backgroundColor = nextIsPinned ? item.theme.chatList.pinnedItemBackgroundColor : item.theme.chatList.itemBackgroundColor
|
let backgroundColor = nextIsPinned ? item.theme.chatList.pinnedItemBackgroundColor : item.theme.chatList.itemBackgroundColor
|
||||||
@ -120,11 +123,36 @@ class ChatListSearchItemNode: ListViewItemNode {
|
|||||||
transition = .immediate
|
transition = .immediate
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.searchBarNode.frame = CGRect(origin: CGPoint(x: params.leftInset + 8.0, y: 8.0), size: CGSize(width: baseWidth - 16.0, height: 28.0))
|
let searchBarFrame = CGRect(origin: CGPoint(x: params.leftInset + 8.0, y: 8.0), size: CGSize(width: baseWidth - 16.0, height: 28.0))
|
||||||
|
strongSelf.searchBarNode.frame = searchBarFrame
|
||||||
searchBarApply()
|
searchBarApply()
|
||||||
|
|
||||||
strongSelf.searchBarNode.bounds = CGRect(origin: CGPoint(), size: CGSize(width: baseWidth - 16.0, height: 28.0))
|
strongSelf.searchBarNode.bounds = CGRect(origin: CGPoint(), size: CGSize(width: baseWidth - 16.0, height: 28.0))
|
||||||
|
|
||||||
|
if !item.isEnabled {
|
||||||
|
if strongSelf.disabledOverlay == nil {
|
||||||
|
let disabledOverlay = ASDisplayNode()
|
||||||
|
strongSelf.addSubnode(disabledOverlay)
|
||||||
|
strongSelf.disabledOverlay = disabledOverlay
|
||||||
|
if animated {
|
||||||
|
disabledOverlay.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let disabledOverlay = strongSelf.disabledOverlay {
|
||||||
|
disabledOverlay.backgroundColor = backgroundColor.withAlphaComponent(0.4)
|
||||||
|
disabledOverlay.frame = searchBarFrame
|
||||||
|
}
|
||||||
|
} else if let disabledOverlay = strongSelf.disabledOverlay {
|
||||||
|
strongSelf.disabledOverlay = nil
|
||||||
|
if animated {
|
||||||
|
disabledOverlay.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak disabledOverlay] _ in
|
||||||
|
disabledOverlay?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
disabledOverlay.removeFromSupernode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
transition.updateBackgroundColor(node: strongSelf, color: backgroundColor)
|
transition.updateBackgroundColor(node: strongSelf, color: backgroundColor)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
56
TelegramUI/ChatListSelection.swift
Normal file
56
TelegramUI/ChatListSelection.swift
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
|
||||||
|
enum ChatListSelectionReadOption: Equatable {
|
||||||
|
case all(enabled: Bool)
|
||||||
|
case selective(enabled: Bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ChatListSelectionOptions: Equatable {
|
||||||
|
let read: ChatListSelectionReadOption
|
||||||
|
let delete: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func chatListSelectionOptions(postbox: Postbox, peerIds: Set<PeerId>) -> Signal<ChatListSelectionOptions, NoError> {
|
||||||
|
if peerIds.isEmpty {
|
||||||
|
let key = PostboxViewKey.unreadCounts(items: [.total(nil)])
|
||||||
|
return postbox.combinedView(keys: [key])
|
||||||
|
|> map { view -> ChatListSelectionOptions in
|
||||||
|
var hasUnread = false
|
||||||
|
if let unreadCounts = view.views[key] as? UnreadMessageCountsView, let total = unreadCounts.total() {
|
||||||
|
for (_, counter) in total.1.absoluteCounters {
|
||||||
|
if counter.messageCount != 0 {
|
||||||
|
hasUnread = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ChatListSelectionOptions(read: .all(enabled: hasUnread), delete: false)
|
||||||
|
}
|
||||||
|
|> distinctUntilChanged
|
||||||
|
} else {
|
||||||
|
let items: [UnreadMessageCountsItem] = peerIds.map(UnreadMessageCountsItem.peer)
|
||||||
|
let key = PostboxViewKey.unreadCounts(items: items)
|
||||||
|
return postbox.combinedView(keys: [key])
|
||||||
|
|> map { view -> ChatListSelectionOptions in
|
||||||
|
var hasUnread = false
|
||||||
|
if let unreadCounts = view.views[key] as? UnreadMessageCountsView {
|
||||||
|
loop: for entry in unreadCounts.entries {
|
||||||
|
switch entry {
|
||||||
|
case let .peer(_, count):
|
||||||
|
if count != 0 {
|
||||||
|
hasUnread = true
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ChatListSelectionOptions(read: .selective(enabled: hasUnread), delete: true)
|
||||||
|
}
|
||||||
|
|> distinctUntilChanged
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -137,7 +137,7 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane {
|
|||||||
|
|
||||||
let interaction = TrendingPaneInteraction(installPack: { [weak self] info in
|
let interaction = TrendingPaneInteraction(installPack: { [weak self] info in
|
||||||
if let strongSelf = self, let info = info as? StickerPackCollectionInfo {
|
if let strongSelf = self, let info = info as? StickerPackCollectionInfo {
|
||||||
let _ = (loadedStickerPack(postbox: strongSelf.account.postbox, network: strongSelf.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash))
|
let _ = (loadedStickerPack(postbox: strongSelf.account.postbox, network: strongSelf.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false)
|
||||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||||
switch result {
|
switch result {
|
||||||
case let .result(info, items, installed):
|
case let .result(info, items, installed):
|
||||||
|
|||||||
@ -178,7 +178,14 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
var unboundSize: CGSize
|
var unboundSize: CGSize
|
||||||
if let image = media as? TelegramMediaImage, let dimensions = largestImageRepresentation(image.representations)?.dimensions {
|
if let image = media as? TelegramMediaImage, let dimensions = largestImageRepresentation(image.representations)?.dimensions {
|
||||||
unboundSize = CGSize(width: floor(dimensions.width * 0.5), height: floor(dimensions.height * 0.5))
|
unboundSize = CGSize(width: floor(dimensions.width * 0.5), height: floor(dimensions.height * 0.5))
|
||||||
} else if let file = media as? TelegramMediaFile, let dimensions = file.dimensions {
|
} else if let file = media as? TelegramMediaFile, var dimensions = file.dimensions {
|
||||||
|
if let thumbnail = file.previewRepresentations.first {
|
||||||
|
let dimensionsVertical = dimensions.width < dimensions.height
|
||||||
|
let thumbnailVertical = thumbnail.dimensions.width < thumbnail.dimensions.height
|
||||||
|
if dimensionsVertical != thumbnailVertical {
|
||||||
|
dimensions = CGSize(width: dimensions.height, height: dimensions.width)
|
||||||
|
}
|
||||||
|
}
|
||||||
unboundSize = CGSize(width: floor(dimensions.width * 0.5), height: floor(dimensions.height * 0.5))
|
unboundSize = CGSize(width: floor(dimensions.width * 0.5), height: floor(dimensions.height * 0.5))
|
||||||
if file.isAnimated {
|
if file.isAnimated {
|
||||||
unboundSize = unboundSize.aspectFilled(CGSize(width: 480.0, height: 480.0))
|
unboundSize = unboundSize.aspectFilled(CGSize(width: 480.0, height: 480.0))
|
||||||
|
|||||||
@ -109,7 +109,8 @@ private let chatList = PresentationThemeChatList(
|
|||||||
itemSeparatorColor: UIColor(rgb: 0x131A23),
|
itemSeparatorColor: UIColor(rgb: 0x131A23),
|
||||||
itemBackgroundColor: UIColor(rgb: 0x18222D),
|
itemBackgroundColor: UIColor(rgb: 0x18222D),
|
||||||
pinnedItemBackgroundColor: UIColor(rgb: 0x213040),
|
pinnedItemBackgroundColor: UIColor(rgb: 0x213040),
|
||||||
itemHighlightedBackgroundColor: UIColor(rgb: 0x10171F), //!!!
|
itemHighlightedBackgroundColor: UIColor(rgb: 0x10171F),
|
||||||
|
itemSelectedBackgroundColor: UIColor(rgb: 0x10171F),
|
||||||
titleColor: UIColor(rgb: 0xffffff),
|
titleColor: UIColor(rgb: 0xffffff),
|
||||||
secretTitleColor: secretColor,
|
secretTitleColor: secretColor,
|
||||||
dateTextColor: UIColor(rgb: 0xDBF5FF, alpha: 0.5),
|
dateTextColor: UIColor(rgb: 0xDBF5FF, alpha: 0.5),
|
||||||
|
|||||||
@ -110,6 +110,7 @@ private let chatList = PresentationThemeChatList(
|
|||||||
itemBackgroundColor: UIColor(rgb: 0x000000),
|
itemBackgroundColor: UIColor(rgb: 0x000000),
|
||||||
pinnedItemBackgroundColor: UIColor(rgb: 0x1c1c1d),
|
pinnedItemBackgroundColor: UIColor(rgb: 0x1c1c1d),
|
||||||
itemHighlightedBackgroundColor: UIColor(rgb: 0x191919),
|
itemHighlightedBackgroundColor: UIColor(rgb: 0x191919),
|
||||||
|
itemSelectedBackgroundColor: UIColor(rgb: 0x191919),
|
||||||
titleColor: UIColor(rgb: 0xffffff),
|
titleColor: UIColor(rgb: 0xffffff),
|
||||||
secretTitleColor: UIColor(rgb: 0xb2b2b2), //!!!
|
secretTitleColor: UIColor(rgb: 0xb2b2b2), //!!!
|
||||||
dateTextColor: UIColor(rgb: 0x8e8e93),
|
dateTextColor: UIColor(rgb: 0x8e8e93),
|
||||||
|
|||||||
@ -110,6 +110,7 @@ private func makeDefaultPresentationTheme(accentColor: UIColor, day: Bool) -> Pr
|
|||||||
itemBackgroundColor: .white,
|
itemBackgroundColor: .white,
|
||||||
pinnedItemBackgroundColor: UIColor(rgb: 0xf7f7f7),
|
pinnedItemBackgroundColor: UIColor(rgb: 0xf7f7f7),
|
||||||
itemHighlightedBackgroundColor: UIColor(rgb: 0xd9d9d9),
|
itemHighlightedBackgroundColor: UIColor(rgb: 0xd9d9d9),
|
||||||
|
itemSelectedBackgroundColor: UIColor(rgb: 0xe9f0fa),
|
||||||
titleColor: .black,
|
titleColor: .black,
|
||||||
secretTitleColor: secretColor,
|
secretTitleColor: secretColor,
|
||||||
dateTextColor: UIColor(rgb: 0x8e8e93),
|
dateTextColor: UIColor(rgb: 0x8e8e93),
|
||||||
@ -140,6 +141,7 @@ private func makeDefaultPresentationTheme(accentColor: UIColor, day: Bool) -> Pr
|
|||||||
itemBackgroundColor: .white,
|
itemBackgroundColor: .white,
|
||||||
pinnedItemBackgroundColor: UIColor(rgb: 0xf7f7f7),
|
pinnedItemBackgroundColor: UIColor(rgb: 0xf7f7f7),
|
||||||
itemHighlightedBackgroundColor: UIColor(rgb: 0xd9d9d9),
|
itemHighlightedBackgroundColor: UIColor(rgb: 0xd9d9d9),
|
||||||
|
itemSelectedBackgroundColor: UIColor(rgb: 0xe9f0fa),
|
||||||
titleColor: .black,
|
titleColor: .black,
|
||||||
secretTitleColor: secretColor,
|
secretTitleColor: secretColor,
|
||||||
dateTextColor: UIColor(rgb: 0x8e8e93),
|
dateTextColor: UIColor(rgb: 0x8e8e93),
|
||||||
|
|||||||
@ -171,7 +171,7 @@ public func featuredStickerPacksController(account: Account) -> ViewController {
|
|||||||
let arguments = FeaturedStickerPacksControllerArguments(account: account, openStickerPack: { info in
|
let arguments = FeaturedStickerPacksControllerArguments(account: account, openStickerPack: { info in
|
||||||
presentStickerPackController?(info)
|
presentStickerPackController?(info)
|
||||||
}, addPack: { info in
|
}, addPack: { info in
|
||||||
let _ = (loadedStickerPack(postbox: account.postbox, network: account.network, reference: .id(id: info.id.id, accessHash: info.accessHash))
|
let _ = (loadedStickerPack(postbox: account.postbox, network: account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false)
|
||||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||||
switch result {
|
switch result {
|
||||||
case let .result(info, items, installed):
|
case let .result(info, items, installed):
|
||||||
|
|||||||
@ -309,7 +309,7 @@ public func groupStickerPackSetupController(account: Account, peerId: PeerId, cu
|
|||||||
|
|
||||||
let initialData = Promise<InitialStickerPackData?>()
|
let initialData = Promise<InitialStickerPackData?>()
|
||||||
if let currentPackInfo = currentPackInfo {
|
if let currentPackInfo = currentPackInfo {
|
||||||
initialData.set(cachedStickerPack(postbox: account.postbox, network: account.network, reference: .id(id: currentPackInfo.id.id, accessHash: currentPackInfo.accessHash))
|
initialData.set(cachedStickerPack(postbox: account.postbox, network: account.network, reference: .id(id: currentPackInfo.id.id, accessHash: currentPackInfo.accessHash), forceRemote: false)
|
||||||
|> map { result -> InitialStickerPackData? in
|
|> map { result -> InitialStickerPackData? in
|
||||||
switch result {
|
switch result {
|
||||||
case .none:
|
case .none:
|
||||||
@ -349,7 +349,7 @@ public func groupStickerPackSetupController(account: Account, peerId: PeerId, cu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return .single((searchText, .searching))
|
return .single((searchText, .searching))
|
||||||
|> then((loadedStickerPack(postbox: account.postbox, network: account.network, reference: .name(searchText.lowercased())) |> delay(0.3, queue: Queue.concurrentDefaultQueue()))
|
|> then((loadedStickerPack(postbox: account.postbox, network: account.network, reference: .name(searchText.lowercased()), forceActualized: false) |> delay(0.3, queue: Queue.concurrentDefaultQueue()))
|
||||||
|> mapToSignal { value -> Signal<(String, GroupStickerPackSearchState), NoError> in
|
|> mapToSignal { value -> Signal<(String, GroupStickerPackSearchState), NoError> in
|
||||||
switch value {
|
switch value {
|
||||||
case .fetching:
|
case .fetching:
|
||||||
|
|||||||
@ -43,7 +43,7 @@ final class HashtagSearchController: TelegramController {
|
|||||||
}
|
}
|
||||||
let interaction = ChatListNodeInteraction(activateSearch: {
|
let interaction = ChatListNodeInteraction(activateSearch: {
|
||||||
}, peerSelected: { peer in
|
}, peerSelected: { peer in
|
||||||
|
}, togglePeerSelected: { _ in
|
||||||
}, messageSelected: { [weak self] message, _ in
|
}, messageSelected: { [weak self] message, _ in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if let peer = message.peers[message.id.peerId] {
|
if let peer = message.peers[message.id.peerId] {
|
||||||
|
|||||||
@ -14,8 +14,8 @@ final class ItemListSelectableControlNode: ASDisplayNode {
|
|||||||
self.addSubnode(self.checkNode)
|
self.addSubnode(self.checkNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func asyncLayout(_ node: ItemListSelectableControlNode?) -> (_ strokeColor: UIColor, _ fillColor: UIColor, _ foregroundColor: UIColor, _ selected: Bool) -> (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode) {
|
static func asyncLayout(_ node: ItemListSelectableControlNode?) -> (_ strokeColor: UIColor, _ fillColor: UIColor, _ foregroundColor: UIColor, _ selected: Bool, _ compact: Bool) -> (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode) {
|
||||||
return { strokeColor, fillColor, foregroundColor, selected in
|
return { strokeColor, fillColor, foregroundColor, selected, compact in
|
||||||
let resultNode: ItemListSelectableControlNode
|
let resultNode: ItemListSelectableControlNode
|
||||||
if let node = node {
|
if let node = node {
|
||||||
resultNode = node
|
resultNode = node
|
||||||
@ -23,10 +23,9 @@ final class ItemListSelectableControlNode: ASDisplayNode {
|
|||||||
resultNode = ItemListSelectableControlNode(strokeColor: strokeColor, fillColor: fillColor, foregroundColor: foregroundColor)
|
resultNode = ItemListSelectableControlNode(strokeColor: strokeColor, fillColor: fillColor, foregroundColor: foregroundColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (45.0, { size, animated in
|
return (compact ? 38.0 : 45.0, { size, animated in
|
||||||
|
|
||||||
let checkSize = CGSize(width: 32.0, height: 32.0)
|
let checkSize = CGSize(width: 32.0, height: 32.0)
|
||||||
resultNode.checkNode.frame = CGRect(origin: CGPoint(x: 12.0, y: floor((size.height - checkSize.height) / 2.0)), size: checkSize)
|
resultNode.checkNode.frame = CGRect(origin: CGPoint(x: compact ? 9 : 12.0, y: floor((size.height - checkSize.height) / 2.0)), size: checkSize)
|
||||||
resultNode.checkNode.setIsChecked(selected, animated: animated)
|
resultNode.checkNode.setIsChecked(selected, animated: animated)
|
||||||
return resultNode
|
return resultNode
|
||||||
})
|
})
|
||||||
|
|||||||
@ -176,7 +176,7 @@ class ItemListTextWithLabelItemNode: ListViewItemNode {
|
|||||||
var leftOffset: CGFloat = 0.0
|
var leftOffset: CGFloat = 0.0
|
||||||
var selectionNodeWidthAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)?
|
var selectionNodeWidthAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)?
|
||||||
if let selected = item.selected {
|
if let selected = item.selected {
|
||||||
let (selectionWidth, selectionApply) = selectionNodeLayout(item.theme.list.itemCheckColors.strokeColor, item.theme.list.itemCheckColors.fillColor, item.theme.list.itemCheckColors.foregroundColor, selected)
|
let (selectionWidth, selectionApply) = selectionNodeLayout(item.theme.list.itemCheckColors.strokeColor, item.theme.list.itemCheckColors.fillColor, item.theme.list.itemCheckColors.foregroundColor, selected, false)
|
||||||
selectionNodeWidthAndApply = (selectionWidth, selectionApply)
|
selectionNodeWidthAndApply = (selectionWidth, selectionApply)
|
||||||
leftOffset += selectionWidth - 8.0
|
leftOffset += selectionWidth - 8.0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -304,7 +304,7 @@ final class ListMessageFileItemNode: ListMessageNode {
|
|||||||
var leftOffset: CGFloat = 0.0
|
var leftOffset: CGFloat = 0.0
|
||||||
var selectionNodeWidthAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)?
|
var selectionNodeWidthAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)?
|
||||||
if case let .selectable(selected) = item.selection {
|
if case let .selectable(selected) = item.selection {
|
||||||
let (selectionWidth, selectionApply) = selectionNodeLayout(item.theme.list.itemCheckColors.strokeColor, item.theme.list.itemCheckColors.fillColor, item.theme.list.itemCheckColors.foregroundColor, selected)
|
let (selectionWidth, selectionApply) = selectionNodeLayout(item.theme.list.itemCheckColors.strokeColor, item.theme.list.itemCheckColors.fillColor, item.theme.list.itemCheckColors.foregroundColor, selected, false)
|
||||||
selectionNodeWidthAndApply = (selectionWidth, selectionApply)
|
selectionNodeWidthAndApply = (selectionWidth, selectionApply)
|
||||||
leftOffset += selectionWidth
|
leftOffset += selectionWidth
|
||||||
}
|
}
|
||||||
|
|||||||
@ -153,7 +153,7 @@ final class ListMessageSnippetItemNode: ListMessageNode {
|
|||||||
var leftOffset: CGFloat = 0.0
|
var leftOffset: CGFloat = 0.0
|
||||||
var selectionNodeWidthAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)?
|
var selectionNodeWidthAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)?
|
||||||
if case let .selectable(selected) = item.selection {
|
if case let .selectable(selected) = item.selection {
|
||||||
let (selectionWidth, selectionApply) = selectionNodeLayout(item.theme.list.itemCheckColors.strokeColor, item.theme.list.itemCheckColors.fillColor, item.theme.list.itemCheckColors.foregroundColor, selected)
|
let (selectionWidth, selectionApply) = selectionNodeLayout(item.theme.list.itemCheckColors.strokeColor, item.theme.list.itemCheckColors.fillColor, item.theme.list.itemCheckColors.foregroundColor, selected, false)
|
||||||
selectionNodeWidthAndApply = (selectionWidth, selectionApply)
|
selectionNodeWidthAndApply = (selectionWidth, selectionApply)
|
||||||
leftOffset += selectionWidth
|
leftOffset += selectionWidth
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,7 +55,19 @@ final class NativeVideoContent: UniversalVideoContent {
|
|||||||
self.nativeId = id
|
self.nativeId = id
|
||||||
self.fileReference = fileReference
|
self.fileReference = fileReference
|
||||||
self.imageReference = imageReference
|
self.imageReference = imageReference
|
||||||
self.dimensions = fileReference.media.dimensions ?? CGSize(width: 128.0, height: 128.0)
|
if var dimensions = fileReference.media.dimensions {
|
||||||
|
if let thumbnail = fileReference.media.previewRepresentations.first {
|
||||||
|
let dimensionsVertical = dimensions.width < dimensions.height
|
||||||
|
let thumbnailVertical = thumbnail.dimensions.width < thumbnail.dimensions.height
|
||||||
|
if dimensionsVertical != thumbnailVertical {
|
||||||
|
dimensions = CGSize(width: dimensions.height, height: dimensions.width)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.dimensions = dimensions
|
||||||
|
} else {
|
||||||
|
self.dimensions = CGSize(width: 128.0, height: 128.0)
|
||||||
|
}
|
||||||
|
|
||||||
self.duration = fileReference.media.duration ?? 0
|
self.duration = fileReference.media.duration ?? 0
|
||||||
self.streamVideo = streamVideo
|
self.streamVideo = streamVideo
|
||||||
self.loopVideo = loopVideo
|
self.loopVideo = loopVideo
|
||||||
|
|||||||
@ -71,7 +71,7 @@ final class OverlayInstantVideoDecoration: UniversalVideoDecoration {
|
|||||||
if let snapshot = snapshot {
|
if let snapshot = snapshot {
|
||||||
self.contentContainerNode.view.addSubview(snapshot)
|
self.contentContainerNode.view.addSubview(snapshot)
|
||||||
if let validLayoutSize = self.validLayoutSize {
|
if let validLayoutSize = self.validLayoutSize {
|
||||||
snapshot.frame = CGRect(origin: CGPoint(), size: validLayoutSize)
|
snapshot.frame = CGRect(origin: CGPoint(), size: snapshot.frame.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -129,7 +129,9 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.scrubberNode = MediaPlayerScrubbingNode(content: .standard(lineHeight: 3.0, lineCap: .round, scrubberHandle: .circle, backgroundColor: theme.list.controlSecondaryColor, foregroundColor: theme.list.itemAccentColor))
|
self.scrubberNode = MediaPlayerScrubbingNode(content: .standard(lineHeight: 3.0, lineCap: .round, scrubberHandle: .circle, backgroundColor: theme.list.controlSecondaryColor, foregroundColor: theme.list.itemAccentColor))
|
||||||
self.leftDurationLabel = MediaPlayerTimeTextNode(textColor: theme.list.itemSecondaryTextColor)
|
self.leftDurationLabel = MediaPlayerTimeTextNode(textColor: theme.list.itemSecondaryTextColor)
|
||||||
|
self.leftDurationLabel.displaysAsynchronously = false
|
||||||
self.rightDurationLabel = MediaPlayerTimeTextNode(textColor: theme.list.itemSecondaryTextColor)
|
self.rightDurationLabel = MediaPlayerTimeTextNode(textColor: theme.list.itemSecondaryTextColor)
|
||||||
|
self.rightDurationLabel.displaysAsynchronously = false
|
||||||
self.rightDurationLabel.mode = .reversed
|
self.rightDurationLabel.mode = .reversed
|
||||||
self.rightDurationLabel.alignment = .right
|
self.rightDurationLabel.alignment = .right
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@ private enum PeerReportOption {
|
|||||||
case violence
|
case violence
|
||||||
case copyright
|
case copyright
|
||||||
case pornoghraphy
|
case pornoghraphy
|
||||||
|
case childAbuse
|
||||||
case other
|
case other
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,6 +26,7 @@ func peerReportOptionsController(account: Account, subject: PeerReportSubject, p
|
|||||||
.spam,
|
.spam,
|
||||||
.violence,
|
.violence,
|
||||||
.pornoghraphy,
|
.pornoghraphy,
|
||||||
|
.childAbuse,
|
||||||
.copyright,
|
.copyright,
|
||||||
.other
|
.other
|
||||||
]
|
]
|
||||||
@ -32,6 +34,7 @@ func peerReportOptionsController(account: Account, subject: PeerReportSubject, p
|
|||||||
var items: [ActionSheetItem] = []
|
var items: [ActionSheetItem] = []
|
||||||
for option in options {
|
for option in options {
|
||||||
let title: String
|
let title: String
|
||||||
|
var color: ActionSheetButtonColor = .accent
|
||||||
switch option {
|
switch option {
|
||||||
case .spam:
|
case .spam:
|
||||||
title = presentationData.strings.ReportPeer_ReasonSpam
|
title = presentationData.strings.ReportPeer_ReasonSpam
|
||||||
@ -39,13 +42,15 @@ func peerReportOptionsController(account: Account, subject: PeerReportSubject, p
|
|||||||
title = presentationData.strings.ReportPeer_ReasonViolence
|
title = presentationData.strings.ReportPeer_ReasonViolence
|
||||||
case .pornoghraphy:
|
case .pornoghraphy:
|
||||||
title = presentationData.strings.ReportPeer_ReasonPornography
|
title = presentationData.strings.ReportPeer_ReasonPornography
|
||||||
|
case .childAbuse:
|
||||||
|
title = presentationData.strings.ReportPeer_ReasonChildAbuse
|
||||||
|
color = .destructive
|
||||||
case .copyright:
|
case .copyright:
|
||||||
title = presentationData.strings.ReportPeer_ReasonCopyright
|
title = presentationData.strings.ReportPeer_ReasonCopyright
|
||||||
case .other:
|
case .other:
|
||||||
title = presentationData.strings.ReportPeer_ReasonOther
|
title = presentationData.strings.ReportPeer_ReasonOther
|
||||||
}
|
}
|
||||||
items.append(ActionSheetButtonItem(title: title, action: { [weak controller] in
|
items.append(ActionSheetButtonItem(title: title, color: color, action: { [weak controller] in
|
||||||
//account.reportPeer#ae189d5f peer:InputPeer reason:ReportReason = Bool;
|
|
||||||
var reportReason: ReportReason?
|
var reportReason: ReportReason?
|
||||||
switch option {
|
switch option {
|
||||||
case .spam:
|
case .spam:
|
||||||
@ -54,8 +59,10 @@ func peerReportOptionsController(account: Account, subject: PeerReportSubject, p
|
|||||||
reportReason = .violence
|
reportReason = .violence
|
||||||
case .pornoghraphy:
|
case .pornoghraphy:
|
||||||
reportReason = .porno
|
reportReason = .porno
|
||||||
case .copyright:
|
case .childAbuse:
|
||||||
reportReason = .copyright
|
reportReason = .childAbuse
|
||||||
|
case .copyright:
|
||||||
|
reportReason = .copyright
|
||||||
case .other:
|
case .other:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@ -312,6 +312,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func animateOut(completion: (() -> Void)? = nil) {
|
func animateOut(completion: (() -> Void)? = nil) {
|
||||||
|
self.clipsToBounds = true
|
||||||
self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.layer.bounds.size.height), duration: 0.2, timingFunction: kCAMediaTimingFunctionEaseInEaseOut, removeOnCompletion: false, additive: true, completion: { [weak self] _ in
|
self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.layer.bounds.size.height), duration: 0.2, timingFunction: kCAMediaTimingFunctionEaseInEaseOut, removeOnCompletion: false, additive: true, completion: { [weak self] _ in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.dismiss()
|
strongSelf.dismiss()
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -333,6 +333,7 @@ public final class PresentationThemeChatList {
|
|||||||
public let itemBackgroundColor: UIColor
|
public let itemBackgroundColor: UIColor
|
||||||
public let pinnedItemBackgroundColor: UIColor
|
public let pinnedItemBackgroundColor: UIColor
|
||||||
public let itemHighlightedBackgroundColor: UIColor
|
public let itemHighlightedBackgroundColor: UIColor
|
||||||
|
public let itemSelectedBackgroundColor: UIColor
|
||||||
public let titleColor: UIColor
|
public let titleColor: UIColor
|
||||||
public let secretTitleColor: UIColor
|
public let secretTitleColor: UIColor
|
||||||
public let dateTextColor: UIColor
|
public let dateTextColor: UIColor
|
||||||
@ -356,12 +357,13 @@ public final class PresentationThemeChatList {
|
|||||||
public let verifiedIconForegroundColor: UIColor
|
public let verifiedIconForegroundColor: UIColor
|
||||||
public let secretIconColor: UIColor
|
public let secretIconColor: UIColor
|
||||||
|
|
||||||
init(backgroundColor: UIColor, itemSeparatorColor: UIColor, itemBackgroundColor: UIColor, pinnedItemBackgroundColor: UIColor, itemHighlightedBackgroundColor: UIColor, titleColor: UIColor, secretTitleColor: UIColor, dateTextColor: UIColor, authorNameColor: UIColor, messageTextColor: UIColor, messageDraftTextColor: UIColor, checkmarkColor: UIColor, pendingIndicatorColor: UIColor, muteIconColor: UIColor, unreadBadgeActiveBackgroundColor: UIColor, unreadBadgeActiveTextColor: UIColor, unreadBadgeInactiveBackgroundColor: UIColor, unreadBadgeInactiveTextColor: UIColor, pinnedBadgeColor: UIColor, pinnedSearchBarColor: UIColor, regularSearchBarColor: UIColor, sectionHeaderFillColor: UIColor, sectionHeaderTextColor: UIColor, searchBarKeyboardColor: PresentationThemeKeyboardColor, verifiedIconFillColor: UIColor, verifiedIconForegroundColor: UIColor, secretIconColor: UIColor) {
|
init(backgroundColor: UIColor, itemSeparatorColor: UIColor, itemBackgroundColor: UIColor, pinnedItemBackgroundColor: UIColor, itemHighlightedBackgroundColor: UIColor, itemSelectedBackgroundColor: UIColor, titleColor: UIColor, secretTitleColor: UIColor, dateTextColor: UIColor, authorNameColor: UIColor, messageTextColor: UIColor, messageDraftTextColor: UIColor, checkmarkColor: UIColor, pendingIndicatorColor: UIColor, muteIconColor: UIColor, unreadBadgeActiveBackgroundColor: UIColor, unreadBadgeActiveTextColor: UIColor, unreadBadgeInactiveBackgroundColor: UIColor, unreadBadgeInactiveTextColor: UIColor, pinnedBadgeColor: UIColor, pinnedSearchBarColor: UIColor, regularSearchBarColor: UIColor, sectionHeaderFillColor: UIColor, sectionHeaderTextColor: UIColor, searchBarKeyboardColor: PresentationThemeKeyboardColor, verifiedIconFillColor: UIColor, verifiedIconForegroundColor: UIColor, secretIconColor: UIColor) {
|
||||||
self.backgroundColor = backgroundColor
|
self.backgroundColor = backgroundColor
|
||||||
self.itemSeparatorColor = itemSeparatorColor
|
self.itemSeparatorColor = itemSeparatorColor
|
||||||
self.itemBackgroundColor = itemBackgroundColor
|
self.itemBackgroundColor = itemBackgroundColor
|
||||||
self.pinnedItemBackgroundColor = pinnedItemBackgroundColor
|
self.pinnedItemBackgroundColor = pinnedItemBackgroundColor
|
||||||
self.itemHighlightedBackgroundColor = itemHighlightedBackgroundColor
|
self.itemHighlightedBackgroundColor = itemHighlightedBackgroundColor
|
||||||
|
self.itemSelectedBackgroundColor = itemSelectedBackgroundColor
|
||||||
self.titleColor = titleColor
|
self.titleColor = titleColor
|
||||||
self.secretTitleColor = secretTitleColor
|
self.secretTitleColor = secretTitleColor
|
||||||
self.dateTextColor = dateTextColor
|
self.dateTextColor = dateTextColor
|
||||||
|
|||||||
Binary file not shown.
@ -56,7 +56,7 @@ final class StickerPackPreviewController: ViewController {
|
|||||||
|
|
||||||
self.statusBar.statusBarStyle = .Ignore
|
self.statusBar.statusBarStyle = .Ignore
|
||||||
|
|
||||||
self.stickerPackContents.set(loadedStickerPack(postbox: account.postbox, network: account.network, reference: stickerPack))
|
self.stickerPackContents.set(loadedStickerPack(postbox: account.postbox, network: account.network, reference: stickerPack, forceActualized: true))
|
||||||
}
|
}
|
||||||
|
|
||||||
required init(coder aDecoder: NSCoder) {
|
required init(coder aDecoder: NSCoder) {
|
||||||
|
|||||||
@ -5,6 +5,37 @@ import SwiftSignalKit
|
|||||||
import Postbox
|
import Postbox
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
|
|
||||||
|
private struct StickerPackPreviewGridEntry: Comparable, Identifiable {
|
||||||
|
let index: Int
|
||||||
|
let stickerItem: StickerPackItem
|
||||||
|
|
||||||
|
var stableId: MediaId {
|
||||||
|
return self.stickerItem.file.fileId
|
||||||
|
}
|
||||||
|
|
||||||
|
static func <(lhs: StickerPackPreviewGridEntry, rhs: StickerPackPreviewGridEntry) -> Bool {
|
||||||
|
return lhs.index < rhs.index
|
||||||
|
}
|
||||||
|
|
||||||
|
func item(account: Account, interaction: StickerPackPreviewInteraction) -> StickerPackPreviewGridItem {
|
||||||
|
return StickerPackPreviewGridItem(account: account, stickerItem: self.stickerItem, interaction: interaction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct StickerPackPreviewGridTransaction {
|
||||||
|
let deletions: [Int]
|
||||||
|
let insertions: [GridNodeInsertItem]
|
||||||
|
let updates: [GridNodeUpdateItem]
|
||||||
|
|
||||||
|
init(previousList: [StickerPackPreviewGridEntry], list: [StickerPackPreviewGridEntry], account: Account, interaction: StickerPackPreviewInteraction) {
|
||||||
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: previousList, rightList: list)
|
||||||
|
|
||||||
|
self.deletions = deleteIndices
|
||||||
|
self.insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(account: account, interaction: interaction), previousIndex: $0.2) }
|
||||||
|
self.updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
|
final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
|
||||||
private let account: Account
|
private let account: Account
|
||||||
private let openShare: () -> Void
|
private let openShare: () -> Void
|
||||||
@ -42,7 +73,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
|
|||||||
private var stickerPackUpdated = false
|
private var stickerPackUpdated = false
|
||||||
private var stickerPackInitiallyInstalled : Bool?
|
private var stickerPackInitiallyInstalled : Bool?
|
||||||
|
|
||||||
private var didSetItems = false
|
private var currentItems: [StickerPackPreviewGridEntry] = []
|
||||||
|
|
||||||
private var hapticFeedback: HapticFeedback?
|
private var hapticFeedback: HapticFeedback?
|
||||||
|
|
||||||
@ -267,7 +298,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
|
|||||||
let contentContainerFrame = CGRect(origin: CGPoint(x: sideInset, y: insets.top), size: CGSize(width: width, height: maximumContentHeight))
|
let contentContainerFrame = CGRect(origin: CGPoint(x: sideInset, y: insets.top), size: CGSize(width: width, height: maximumContentHeight))
|
||||||
let contentFrame = contentContainerFrame.insetBy(dx: 12.0, dy: 0.0)
|
let contentFrame = contentContainerFrame.insetBy(dx: 12.0, dy: 0.0)
|
||||||
|
|
||||||
var insertItems: [GridNodeInsertItem] = []
|
var transaction: StickerPackPreviewGridTransaction?
|
||||||
|
|
||||||
var itemCount = 0
|
var itemCount = 0
|
||||||
var animateIn = false
|
var animateIn = false
|
||||||
@ -286,16 +317,21 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
|
|||||||
self.activityIndicator = nil
|
self.activityIndicator = nil
|
||||||
}
|
}
|
||||||
itemCount = items.count
|
itemCount = items.count
|
||||||
if !self.didSetItems {
|
|
||||||
let entities = generateTextEntities(info.title, enabledTypes: [.mention])
|
|
||||||
self.contentTitleNode.attributedText = stringWithAppliedEntities(info.title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: Font.medium(20.0), linkFont: Font.medium(20.0), boldFont: Font.medium(20.0), italicFont: Font.medium(20.0), fixedFont: Font.medium(20.0))
|
|
||||||
|
|
||||||
self.didSetItems = true
|
var updatedItems: [StickerPackPreviewGridEntry] = []
|
||||||
animateIn = true
|
for item in items {
|
||||||
for i in 0 ..< items.count {
|
if let item = item as? StickerPackItem {
|
||||||
insertItems.append(GridNodeInsertItem(index: i, item: StickerPackPreviewGridItem(account: self.account, stickerItem: items[i] as! StickerPackItem, interaction: self.interaction), previousIndex: nil))
|
updatedItems.append(StickerPackPreviewGridEntry(index: updatedItems.count, stickerItem: item))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.currentItems.isEmpty && !updatedItems.isEmpty {
|
||||||
|
let entities = generateTextEntities(info.title, enabledTypes: [.mention])
|
||||||
|
self.contentTitleNode.attributedText = stringWithAppliedEntities(info.title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: Font.medium(20.0), linkFont: Font.medium(20.0), boldFont: Font.medium(20.0), italicFont: Font.medium(20.0), fixedFont: Font.medium(20.0))
|
||||||
|
animateIn = true
|
||||||
|
}
|
||||||
|
transaction = StickerPackPreviewGridTransaction(previousList: self.currentItems, list: updatedItems, account: self.account, interaction: self.interaction)
|
||||||
|
self.currentItems = updatedItems
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,7 +371,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
|
|||||||
|
|
||||||
let gridSize = CGSize(width: contentFrame.size.width, height: max(32.0, contentFrame.size.height - titleAreaHeight))
|
let gridSize = CGSize(width: contentFrame.size.width, height: max(32.0, contentFrame.size.height - titleAreaHeight))
|
||||||
|
|
||||||
self.contentGridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: insertItems, updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: gridSize, insets: UIEdgeInsets(top: topInset, left: 0.0, bottom: bottomGridInset, right: 0.0), preloadSize: 80.0, type: .fixed(itemSize: CGSize(width: itemWidth, height: itemWidth), lineSpacing: 0.0)), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
self.contentGridNode.transaction(GridNodeTransaction(deleteItems: transaction?.deletions ?? [], insertItems: transaction?.insertions ?? [], updateItems: transaction?.updates ?? [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: gridSize, insets: UIEdgeInsets(top: topInset, left: 0.0, bottom: bottomGridInset, right: 0.0), preloadSize: 80.0, type: .fixed(itemSize: CGSize(width: itemWidth, height: itemWidth), lineSpacing: 0.0)), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
||||||
transition.updateFrame(node: self.contentGridNode, frame: CGRect(origin: CGPoint(x: floor((contentContainerFrame.size.width - contentFrame.size.width) / 2.0), y: titleAreaHeight), size: gridSize))
|
transition.updateFrame(node: self.contentGridNode, frame: CGRect(origin: CGPoint(x: floor((contentContainerFrame.size.width - contentFrame.size.width) / 2.0), y: titleAreaHeight), size: gridSize))
|
||||||
|
|
||||||
if animateIn {
|
if animateIn {
|
||||||
|
|||||||
@ -217,7 +217,7 @@ final class StickerPaneSearchContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}, install: { [weak self] info in
|
}, install: { [weak self] info in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let _ = (loadedStickerPack(postbox: strongSelf.account.postbox, network: strongSelf.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash))
|
let _ = (loadedStickerPack(postbox: strongSelf.account.postbox, network: strongSelf.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false)
|
||||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||||
switch result {
|
switch result {
|
||||||
case let .result(info, items, installed):
|
case let .result(info, items, installed):
|
||||||
|
|||||||
@ -147,19 +147,32 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig
|
|||||||
case let .peerName(name, parameter):
|
case let .peerName(name, parameter):
|
||||||
return resolvePeerByName(account: account, name: name)
|
return resolvePeerByName(account: account, name: name)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> map { peerId -> ResolvedUrl? in
|
|> mapToSignal { peerId -> Signal<Peer?, NoError> in
|
||||||
if let peerId = peerId {
|
return account.postbox.transaction { transaction -> Peer? in
|
||||||
|
if let peerId = peerId {
|
||||||
|
return transaction.getPeer(peerId)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> map { peer -> ResolvedUrl? in
|
||||||
|
if let peer = peer {
|
||||||
if let parameter = parameter {
|
if let parameter = parameter {
|
||||||
switch parameter {
|
switch parameter {
|
||||||
case let .botStart(payload):
|
case let .botStart(payload):
|
||||||
return .botStart(peerId: peerId, payload: payload)
|
return .botStart(peerId: peer.id, payload: payload)
|
||||||
case let .groupBotStart(payload):
|
case let .groupBotStart(payload):
|
||||||
return .groupBotStart(peerId: peerId, payload: payload)
|
return .groupBotStart(peerId: peer.id, payload: payload)
|
||||||
case let .channelMessage(id):
|
case let .channelMessage(id):
|
||||||
return .channelMessage(peerId: peerId, messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id))
|
return .channelMessage(peerId: peer.id, messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return .peer(peerId, .chat(textInputState: nil, messageId: nil))
|
if let peer = peer as? TelegramUser, peer.botInfo != nil {
|
||||||
|
return .peer(peer.id, .info)
|
||||||
|
} else {
|
||||||
|
return .peer(peer.id, .chat(textInputState: nil, messageId: nil))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user