mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-06 17:00:13 +00:00
Refactoring
This commit is contained in:
parent
54788f60de
commit
85a0b3b4d0
@ -2,6 +2,7 @@ import Foundation
|
||||
import UIKit
|
||||
import Postbox
|
||||
import Display
|
||||
import TelegramCore
|
||||
|
||||
public protocol ChatListController: ViewController {
|
||||
var context: AccountContext { get }
|
||||
@ -11,5 +12,5 @@ public protocol ChatListController: ViewController {
|
||||
func activateSearch(filter: ChatListSearchFilter, query: String?)
|
||||
func deactivateSearch(animated: Bool)
|
||||
func activateCompose()
|
||||
func maybeAskForPeerChatRemoval(peer: RenderedPeer, joined: Bool, deleteGloballyIfPossible: Bool, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void)
|
||||
func maybeAskForPeerChatRemoval(peer: EngineRenderedPeer, joined: Bool, deleteGloballyIfPossible: Bool, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void)
|
||||
}
|
||||
|
||||
@ -1428,7 +1428,7 @@ public final class CalendarMessageScreen: ViewController {
|
||||
struct ClearInfo {
|
||||
var canClearForMyself: ClearType?
|
||||
var canClearForEveryone: ClearType?
|
||||
var mainPeer: Peer
|
||||
var mainPeer: EnginePeer
|
||||
}
|
||||
|
||||
let peerId = self.peerId
|
||||
@ -1436,8 +1436,10 @@ public final class CalendarMessageScreen: ViewController {
|
||||
} else {
|
||||
return
|
||||
}
|
||||
let _ = (self.context.account.postbox.transaction { transaction -> ClearInfo? in
|
||||
guard let chatPeer = transaction.getPeer(peerId) else {
|
||||
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> map { chatPeer -> ClearInfo? in
|
||||
guard let chatPeer = chatPeer else {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1447,10 +1449,10 @@ public final class CalendarMessageScreen: ViewController {
|
||||
if peerId == self.context.account.peerId {
|
||||
canClearForMyself = .savedMessages
|
||||
canClearForEveryone = nil
|
||||
} else if chatPeer is TelegramSecretChat {
|
||||
} else if case .secretChat = chatPeer {
|
||||
canClearForMyself = .secretChat
|
||||
canClearForEveryone = nil
|
||||
} else if let group = chatPeer as? TelegramGroup {
|
||||
} else if case let .legacyGroup(group) = chatPeer {
|
||||
switch group.role {
|
||||
case .creator:
|
||||
canClearForMyself = .group
|
||||
@ -1459,7 +1461,7 @@ public final class CalendarMessageScreen: ViewController {
|
||||
canClearForMyself = .group
|
||||
canClearForEveryone = nil
|
||||
}
|
||||
} else if let channel = chatPeer as? TelegramChannel {
|
||||
} else if case let .channel(channel) = chatPeer {
|
||||
if channel.hasPermission(.deleteAllMessages) {
|
||||
if case .group = channel.info {
|
||||
canClearForEveryone = .group
|
||||
@ -1473,7 +1475,7 @@ public final class CalendarMessageScreen: ViewController {
|
||||
} else {
|
||||
canClearForMyself = .user
|
||||
|
||||
if let user = chatPeer as? TelegramUser, user.botInfo != nil {
|
||||
if case let .user(user) = chatPeer, user.botInfo != nil {
|
||||
canClearForEveryone = nil
|
||||
} else {
|
||||
canClearForEveryone = .user
|
||||
@ -1511,7 +1513,7 @@ public final class CalendarMessageScreen: ViewController {
|
||||
let confirmationText: String
|
||||
switch canClearForEveryone {
|
||||
case .user:
|
||||
text = strongSelf.presentationData.strings.ChatList_DeleteForEveryone(EnginePeer(info.mainPeer).compactDisplayTitle).string
|
||||
text = strongSelf.presentationData.strings.ChatList_DeleteForEveryone(info.mainPeer.compactDisplayTitle).string
|
||||
confirmationText = strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationText
|
||||
default:
|
||||
text = strongSelf.presentationData.strings.Conversation_DeleteMessagesForEveryone
|
||||
|
||||
@ -973,7 +973,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
|
||||
self.chatListDisplayNode.requestOpenMessageFromSearch = { [weak self] peer, messageId, deactivateOnAction in
|
||||
if let strongSelf = self {
|
||||
strongSelf.openMessageFromSearchDisposable.set((storedMessageFromSearchPeer(account: strongSelf.context.account, peer: peer._asPeer())
|
||||
strongSelf.openMessageFromSearchDisposable.set((strongSelf.context.engine.peers.ensurePeerIsLocallyAvailable(peer: peer)
|
||||
|> deliverOnMainQueue).start(next: { [weak strongSelf] actualPeerId in
|
||||
if let strongSelf = strongSelf {
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
@ -995,13 +995,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
|
||||
self.chatListDisplayNode.requestOpenPeerFromSearch = { [weak self] peer, dismissSearch in
|
||||
if let strongSelf = self {
|
||||
let storedPeer = strongSelf.context.account.postbox.transaction { transaction -> Void in
|
||||
if transaction.getPeer(peer.id) == nil {
|
||||
updatePeers(transaction: transaction, peers: [peer._asPeer()], update: { previousPeer, updatedPeer in
|
||||
return updatedPeer
|
||||
})
|
||||
}
|
||||
}
|
||||
let storedPeer = strongSelf.context.engine.peers.ensurePeerIsLocallyAvailable(peer: peer) |> map { _ -> Void in return Void() }
|
||||
strongSelf.openMessageFromSearchDisposable.set((storedPeer |> deliverOnMainQueue).start(completed: { [weak strongSelf] in
|
||||
if let strongSelf = strongSelf {
|
||||
if dismissSearch {
|
||||
@ -1528,7 +1522,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
self.didSuggestLocalization = true
|
||||
|
||||
let context = self.context
|
||||
let signal = combineLatest(self.context.sharedContext.accountManager.transaction { transaction -> String in
|
||||
|
||||
let suggestedLocalization = self.context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.SuggestedLocalization())
|
||||
|
||||
let signal = combineLatest(
|
||||
self.context.sharedContext.accountManager.transaction { transaction -> String in
|
||||
let languageCode: String
|
||||
if let current = transaction.getSharedData(SharedDataKeys.localizationSettings)?.get(LocalizationSettings.self) {
|
||||
let code = current.primaryComponent.languageCode
|
||||
@ -1542,13 +1540,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
languageCode = "en"
|
||||
}
|
||||
return languageCode
|
||||
}, self.context.account.postbox.transaction { transaction -> SuggestedLocalizationEntry? in
|
||||
var suggestedLocalization: SuggestedLocalizationEntry?
|
||||
if let localization = transaction.getPreferencesEntry(key: PreferencesKeys.suggestedLocalization)?.get(SuggestedLocalizationEntry.self) {
|
||||
suggestedLocalization = localization
|
||||
}
|
||||
return suggestedLocalization
|
||||
})
|
||||
},
|
||||
suggestedLocalization
|
||||
)
|
||||
|> mapToSignal({ value -> Signal<(String, SuggestedLocalizationInfo)?, NoError> in
|
||||
guard let suggestedLocalization = value.1, !suggestedLocalization.isSeen && suggestedLocalization.languageCode != "en" && suggestedLocalization.languageCode != value.0 else {
|
||||
return .single(nil)
|
||||
@ -1604,14 +1598,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}))
|
||||
|
||||
Queue.mainQueue().after(1.0, {
|
||||
let _ = (self.context.account.postbox.transaction { transaction -> Int32? in
|
||||
if let value = transaction.getNoticeEntry(key: ApplicationSpecificNotice.forcedPasswordSetupKey())?.get(ApplicationSpecificCounterNotice.self) {
|
||||
return value.value
|
||||
} else {
|
||||
return nil
|
||||
let _ = (
|
||||
self.context.engine.data.get(TelegramEngine.EngineData.Item.Notices.Notice(key: ApplicationSpecificNotice.forcedPasswordSetupKey()))
|
||||
|> map { entry -> Int32? in
|
||||
return entry?.get(ApplicationSpecificCounterNotice.self)?.value
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
|> deliverOnMainQueue
|
||||
).start(next: { [weak self] value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -2534,20 +2527,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
|
||||
func deletePeerChat(peerId: PeerId, joined: Bool) {
|
||||
let _ = (self.context.account.postbox.transaction { transaction -> RenderedPeer? in
|
||||
guard let peer = transaction.getPeer(peerId) else {
|
||||
return nil
|
||||
}
|
||||
if let associatedPeerId = peer.associatedPeerId {
|
||||
if let associatedPeer = transaction.getPeer(associatedPeerId) {
|
||||
return RenderedPeer(peerId: peerId, peers: SimpleDictionary([peer.id: peer, associatedPeer.id: associatedPeer]))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return RenderedPeer(peer: peer)
|
||||
}
|
||||
}
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.RenderedPeer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let strongSelf = self, let peer = peer, let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else {
|
||||
return
|
||||
@ -2564,7 +2544,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
canRemoveGlobally = true
|
||||
}
|
||||
|
||||
if let user = chatPeer as? TelegramUser, user.botInfo == nil, canRemoveGlobally {
|
||||
if case let .user(user) = chatPeer, user.botInfo == nil, canRemoveGlobally {
|
||||
strongSelf.maybeAskForPeerChatRemoval(peer: peer, joined: joined, completion: { _ in }, removed: {})
|
||||
} else {
|
||||
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
|
||||
@ -2574,7 +2554,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
var canRemoveGlobally = false
|
||||
|
||||
var deleteTitle = strongSelf.presentationData.strings.Common_Delete
|
||||
if let channel = chatPeer as? TelegramChannel {
|
||||
if case let .channel(channel) = chatPeer {
|
||||
if case .broadcast = channel.info {
|
||||
canClear = false
|
||||
deleteTitle = strongSelf.presentationData.strings.Channel_LeaveChannel
|
||||
@ -2590,30 +2570,38 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
if let addressName = channel.addressName, !addressName.isEmpty {
|
||||
canClear = false
|
||||
}
|
||||
} else if let group = chatPeer as? TelegramGroup {
|
||||
} else if case let .legacyGroup(group) = chatPeer {
|
||||
if case .creator = group.role {
|
||||
canRemoveGlobally = true
|
||||
}
|
||||
} else if let user = chatPeer as? TelegramUser, user.botInfo != nil {
|
||||
} else if case let .user(user) = chatPeer, user.botInfo != nil {
|
||||
canStop = !user.flags.contains(.isSupport)
|
||||
canClear = user.botInfo == nil
|
||||
deleteTitle = strongSelf.presentationData.strings.ChatList_DeleteChat
|
||||
} else if let _ = chatPeer as? TelegramSecretChat {
|
||||
} else if case .secretChat = chatPeer {
|
||||
canClear = true
|
||||
deleteTitle = strongSelf.presentationData.strings.ChatList_DeleteChat
|
||||
}
|
||||
|
||||
let limitsConfiguration = strongSelf.context.currentLimitsConfiguration.with { $0 }
|
||||
if chatPeer is TelegramUser && chatPeer.id != strongSelf.context.account.peerId {
|
||||
if case .user = chatPeer, chatPeer.id != strongSelf.context.account.peerId {
|
||||
if limitsConfiguration.maxMessageRevokeIntervalInPrivateChats == LimitsConfiguration.timeIntervalForever {
|
||||
canRemoveGlobally = true
|
||||
}
|
||||
} else if chatPeer is TelegramSecretChat {
|
||||
} else if case .secretChat = chatPeer {
|
||||
canRemoveGlobally = true
|
||||
}
|
||||
|
||||
if canRemoveGlobally, (mainPeer is TelegramGroup || mainPeer is TelegramChannel) {
|
||||
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .deleteAndLeave, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder))
|
||||
var isGroupOrChannel = false
|
||||
switch mainPeer {
|
||||
case .legacyGroup, .channel:
|
||||
isGroupOrChannel = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if canRemoveGlobally && isGroupOrChannel {
|
||||
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .deleteAndLeave, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder))
|
||||
|
||||
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
@ -2622,7 +2610,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}))
|
||||
|
||||
let deleteForAllText: String
|
||||
if let channel = mainPeer as? TelegramChannel, case .broadcast = channel.info {
|
||||
if case let .channel(channel) = mainPeer, case .broadcast = channel.info {
|
||||
deleteForAllText = strongSelf.presentationData.strings.ChatList_DeleteForAllSubscribers
|
||||
} else {
|
||||
deleteForAllText = strongSelf.presentationData.strings.ChatList_DeleteForAllMembers
|
||||
@ -2635,7 +2623,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
|
||||
let deleteForAllConfirmation: String
|
||||
if let channel = mainPeer as? TelegramChannel, case .broadcast = channel.info {
|
||||
if case let .channel(channel) = mainPeer, case .broadcast = channel.info {
|
||||
deleteForAllConfirmation = strongSelf.presentationData.strings.ChannelInfo_DeleteChannelConfirmation
|
||||
} else {
|
||||
deleteForAllConfirmation = strongSelf.presentationData.strings.ChannelInfo_DeleteGroupConfirmation
|
||||
@ -2651,7 +2639,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
}))
|
||||
} else {
|
||||
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .delete, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder))
|
||||
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .delete, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder))
|
||||
|
||||
if canClear {
|
||||
let beginClear: (InteractiveHistoryClearingType) -> Void = { type in
|
||||
@ -2705,14 +2693,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
return
|
||||
}
|
||||
|
||||
if chatPeer is TelegramSecretChat {
|
||||
if case .secretChat = chatPeer {
|
||||
beginClear(.forEveryone)
|
||||
} else {
|
||||
if canRemoveGlobally {
|
||||
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
|
||||
var items: [ActionSheetItem] = []
|
||||
|
||||
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .clearHistory(canClearCache: false), strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder))
|
||||
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory(canClearCache: false), strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder))
|
||||
|
||||
if joined || mainPeer.isDeleted {
|
||||
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Delete, color: .destructive, action: { [weak actionSheet] in
|
||||
@ -2724,7 +2712,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
beginClear(.forLocalPeer)
|
||||
actionSheet?.dismissAnimated()
|
||||
}))
|
||||
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(EnginePeer(mainPeer).compactDisplayTitle).string, color: .destructive, action: { [weak actionSheet] in
|
||||
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).string, color: .destructive, action: { [weak actionSheet] in
|
||||
beginClear(.forEveryone)
|
||||
actionSheet?.dismissAnimated()
|
||||
}))
|
||||
@ -2752,8 +2740,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}))
|
||||
}
|
||||
|
||||
if chatPeer is TelegramSecretChat {
|
||||
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(EnginePeer(mainPeer).compactDisplayTitle).string, color: .destructive, action: { [weak actionSheet] in
|
||||
if case .secretChat = chatPeer {
|
||||
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).string, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -2768,11 +2756,19 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
return
|
||||
}
|
||||
|
||||
if canRemoveGlobally, (mainPeer is TelegramGroup || mainPeer is TelegramChannel) {
|
||||
var isGroupOrChannel = false
|
||||
switch mainPeer {
|
||||
case .legacyGroup, .channel:
|
||||
isGroupOrChannel = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if canRemoveGlobally && isGroupOrChannel {
|
||||
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
|
||||
var items: [ActionSheetItem] = []
|
||||
|
||||
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .deleteAndLeave, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder))
|
||||
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .deleteAndLeave, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder))
|
||||
|
||||
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
@ -2781,7 +2777,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}))
|
||||
|
||||
let deleteForAllText: String
|
||||
if let channel = mainPeer as? TelegramChannel, case .broadcast = channel.info {
|
||||
if case let .channel(channel) = mainPeer, case .broadcast = channel.info {
|
||||
deleteForAllText = strongSelf.presentationData.strings.ChatList_DeleteForAllSubscribers
|
||||
} else {
|
||||
deleteForAllText = strongSelf.presentationData.strings.ChatList_DeleteForAllMembers
|
||||
@ -2794,7 +2790,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
|
||||
let deleteForAllConfirmation: String
|
||||
if let channel = mainPeer as? TelegramChannel, case .broadcast = channel.info {
|
||||
if case let .channel(channel) = mainPeer, case .broadcast = channel.info {
|
||||
deleteForAllConfirmation = strongSelf.presentationData.strings.ChatList_DeleteForAllSubscribersConfirmationText
|
||||
} else {
|
||||
deleteForAllConfirmation = strongSelf.presentationData.strings.ChatList_DeleteForAllMembersConfirmationText
|
||||
@ -2854,7 +2850,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
})
|
||||
}
|
||||
|
||||
public func maybeAskForPeerChatRemoval(peer: RenderedPeer, joined: Bool = false, deleteGloballyIfPossible: Bool = false, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) {
|
||||
public func maybeAskForPeerChatRemoval(peer: EngineRenderedPeer, joined: Bool = false, deleteGloballyIfPossible: Bool = false, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) {
|
||||
guard let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else {
|
||||
completion(false)
|
||||
return
|
||||
@ -2866,10 +2862,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
canRemoveGlobally = true
|
||||
}
|
||||
}
|
||||
if let user = chatPeer as? TelegramUser, user.botInfo != nil {
|
||||
if case let .user(user) = chatPeer, user.botInfo != nil {
|
||||
canRemoveGlobally = false
|
||||
}
|
||||
if let _ = chatPeer as? TelegramSecretChat {
|
||||
if case .secretChat = chatPeer {
|
||||
canRemoveGlobally = true
|
||||
}
|
||||
|
||||
@ -2877,7 +2873,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
let actionSheet = ActionSheetController(presentationData: self.presentationData)
|
||||
var items: [ActionSheetItem] = []
|
||||
|
||||
items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .delete, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder))
|
||||
items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .delete, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder))
|
||||
|
||||
if joined || mainPeer.isDeleted {
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.Common_Delete, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
@ -2895,7 +2891,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
})
|
||||
completion(true)
|
||||
}))
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(EnginePeer(mainPeer).compactDisplayTitle).string, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).string, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -3016,7 +3012,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
})
|
||||
}
|
||||
|
||||
private func schedulePeerChatRemoval(peer: RenderedPeer, type: InteractiveMessagesDeletionType, deleteGloballyIfPossible: Bool, completion: @escaping () -> Void) {
|
||||
private func schedulePeerChatRemoval(peer: EngineRenderedPeer, type: InteractiveMessagesDeletionType, deleteGloballyIfPossible: Bool, completion: @escaping () -> Void) {
|
||||
guard let chatPeer = peer.peers[peer.peerId] else {
|
||||
return
|
||||
}
|
||||
@ -3035,7 +3031,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
})
|
||||
self.chatListDisplayNode.containerNode.currentItemNode.setCurrentRemovingPeerId(nil)
|
||||
let statusText: String
|
||||
if let channel = chatPeer as? TelegramChannel {
|
||||
if case let .channel(channel) = chatPeer {
|
||||
if deleteGloballyIfPossible {
|
||||
if case .broadcast = channel.info {
|
||||
statusText = self.presentationData.strings.Undo_DeletedChannel
|
||||
@ -3049,13 +3045,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
statusText = self.presentationData.strings.Undo_LeftGroup
|
||||
}
|
||||
}
|
||||
} else if let _ = chatPeer as? TelegramGroup {
|
||||
} else if case .legacyGroup = chatPeer {
|
||||
if deleteGloballyIfPossible {
|
||||
statusText = self.presentationData.strings.Undo_DeletedGroup
|
||||
} else {
|
||||
statusText = self.presentationData.strings.Undo_LeftGroup
|
||||
}
|
||||
} else if let _ = chatPeer as? TelegramSecretChat {
|
||||
} else if case .secretChat = chatPeer {
|
||||
statusText = self.presentationData.strings.Undo_SecretChatDeleted
|
||||
} else {
|
||||
if case .forEveryone = type {
|
||||
@ -3078,7 +3074,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
if value == .commit {
|
||||
strongSelf.chatListDisplayNode.containerNode.currentItemNode.setCurrentRemovingPeerId(peerId)
|
||||
if let channel = chatPeer as? TelegramChannel {
|
||||
if case let .channel(channel) = chatPeer {
|
||||
strongSelf.context.peerChannelMemberCategoriesContextsManager.externallyRemoved(peerId: channel.id, memberId: strongSelf.context.account.peerId)
|
||||
}
|
||||
let _ = strongSelf.context.engine.peers.removePeerChat(peerId: peerId, reportChatSpam: false, deleteGloballyIfPossible: deleteGloballyIfPossible).start(completed: {
|
||||
|
||||
@ -225,12 +225,12 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
case includePeersHeader(String)
|
||||
case addIncludePeer(title: String)
|
||||
case includeCategory(index: Int, category: ChatListFilterIncludeCategory, title: String, isRevealed: Bool)
|
||||
case includePeer(index: Int, peer: RenderedPeer, isRevealed: Bool)
|
||||
case includePeer(index: Int, peer: EngineRenderedPeer, isRevealed: Bool)
|
||||
case includePeerInfo(String)
|
||||
case excludePeersHeader(String)
|
||||
case addExcludePeer(title: String)
|
||||
case excludeCategory(index: Int, category: ChatListFilterExcludeCategory, title: String, isRevealed: Bool)
|
||||
case excludePeer(index: Int, peer: RenderedPeer, isRevealed: Bool)
|
||||
case excludePeer(index: Int, peer: EngineRenderedPeer, isRevealed: Bool)
|
||||
case excludePeerInfo(String)
|
||||
case includeExpand(String)
|
||||
case excludeExpand(String)
|
||||
@ -389,7 +389,7 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
}
|
||||
)
|
||||
case let .includePeer(_, peer, isRevealed):
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context, peer: EnginePeer(peer.chatMainPeer!), height: .peerList, aliasHandling: .threatSelfAsSaved, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: isRevealed), revealOptions: ItemListPeerItemRevealOptions(options: [ItemListPeerItemRevealOption(type: .destructive, title: presentationData.strings.Common_Delete, action: {
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context, peer: peer.chatMainPeer!, height: .peerList, aliasHandling: .threatSelfAsSaved, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: isRevealed), revealOptions: ItemListPeerItemRevealOptions(options: [ItemListPeerItemRevealOption(type: .destructive, title: presentationData.strings.Common_Delete, action: {
|
||||
arguments.deleteIncludePeer(peer.peerId)
|
||||
})]), switchValue: nil, enabled: true, selectable: false, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { lhs, rhs in
|
||||
arguments.setItemIdWithRevealedOptions(lhs.flatMap { .peer($0) }, rhs.flatMap { .peer($0) })
|
||||
@ -397,7 +397,7 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
arguments.deleteIncludePeer(id)
|
||||
})
|
||||
case let .excludePeer(_, peer, isRevealed):
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context, peer: EnginePeer(peer.chatMainPeer!), height: .peerList, aliasHandling: .threatSelfAsSaved, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: isRevealed), revealOptions: ItemListPeerItemRevealOptions(options: [ItemListPeerItemRevealOption(type: .destructive, title: presentationData.strings.Common_Delete, action: {
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context, peer: peer.chatMainPeer!, height: .peerList, aliasHandling: .threatSelfAsSaved, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: isRevealed), revealOptions: ItemListPeerItemRevealOptions(options: [ItemListPeerItemRevealOption(type: .destructive, title: presentationData.strings.Common_Delete, action: {
|
||||
arguments.deleteExcludePeer(peer.peerId)
|
||||
})]), switchValue: nil, enabled: true, selectable: false, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { lhs, rhs in
|
||||
arguments.setItemIdWithRevealedOptions(lhs.flatMap { .peer($0) }, rhs.flatMap { .peer($0) })
|
||||
@ -454,7 +454,7 @@ private struct ChatListFilterPresetControllerState: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
private func chatListFilterPresetControllerEntries(presentationData: PresentationData, isNewFilter: Bool, state: ChatListFilterPresetControllerState, includePeers: [RenderedPeer], excludePeers: [RenderedPeer]) -> [ChatListFilterPresetEntry] {
|
||||
private func chatListFilterPresetControllerEntries(presentationData: PresentationData, isNewFilter: Bool, state: ChatListFilterPresetControllerState, includePeers: [EngineRenderedPeer], excludePeers: [EngineRenderedPeer]) -> [ChatListFilterPresetEntry] {
|
||||
var entries: [ChatListFilterPresetEntry] = []
|
||||
|
||||
if isNewFilter {
|
||||
@ -933,12 +933,12 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
}
|
||||
)
|
||||
|
||||
let currentPeers = Atomic<[PeerId: RenderedPeer]>(value: [:])
|
||||
let currentPeers = Atomic<[PeerId: EngineRenderedPeer]>(value: [:])
|
||||
let stateWithPeers = statePromise.get()
|
||||
|> mapToSignal { state -> Signal<(ChatListFilterPresetControllerState, [RenderedPeer], [RenderedPeer]), NoError> in
|
||||
|> mapToSignal { state -> Signal<(ChatListFilterPresetControllerState, [EngineRenderedPeer], [EngineRenderedPeer]), NoError> in
|
||||
let currentPeersValue = currentPeers.with { $0 }
|
||||
var included: [RenderedPeer] = []
|
||||
var excluded: [RenderedPeer] = []
|
||||
var included: [EngineRenderedPeer] = []
|
||||
var excluded: [EngineRenderedPeer] = []
|
||||
var missingPeers = false
|
||||
for peerId in state.additionallyIncludePeers {
|
||||
if let peer = currentPeersValue[peerId] {
|
||||
@ -955,20 +955,30 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
}
|
||||
}
|
||||
if missingPeers {
|
||||
return context.account.postbox.transaction { transaction -> (ChatListFilterPresetControllerState, [RenderedPeer], [RenderedPeer]) in
|
||||
var included: [RenderedPeer] = []
|
||||
var excluded: [RenderedPeer] = []
|
||||
var allPeers: [PeerId: RenderedPeer] = [:]
|
||||
return context.engine.data.get(
|
||||
EngineDataMap(
|
||||
state.additionallyIncludePeers.map { peerId -> TelegramEngine.EngineData.Item.Peer.RenderedPeer in
|
||||
return TelegramEngine.EngineData.Item.Peer.RenderedPeer(id: peerId)
|
||||
}
|
||||
),
|
||||
EngineDataMap(
|
||||
state.additionallyExcludePeers.map { peerId -> TelegramEngine.EngineData.Item.Peer.RenderedPeer in
|
||||
return TelegramEngine.EngineData.Item.Peer.RenderedPeer(id: peerId)
|
||||
}
|
||||
)
|
||||
)
|
||||
|> map { additionallyIncludePeers, additionallyExcludePeers -> (ChatListFilterPresetControllerState, [EngineRenderedPeer], [EngineRenderedPeer]) in
|
||||
var included: [EngineRenderedPeer] = []
|
||||
var excluded: [EngineRenderedPeer] = []
|
||||
var allPeers: [EnginePeer.Id: EngineRenderedPeer] = [:]
|
||||
for peerId in state.additionallyIncludePeers {
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
let renderedPeer = RenderedPeer(peer: peer)
|
||||
if let renderedPeerValue = additionallyIncludePeers[peerId], let renderedPeer = renderedPeerValue {
|
||||
included.append(renderedPeer)
|
||||
allPeers[renderedPeer.peerId] = renderedPeer
|
||||
}
|
||||
}
|
||||
for peerId in state.additionallyExcludePeers {
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
let renderedPeer = RenderedPeer(peer: peer)
|
||||
if let renderedPeerValue = additionallyExcludePeers[peerId], let renderedPeer = renderedPeerValue {
|
||||
excluded.append(renderedPeer)
|
||||
allPeers[renderedPeer.peerId] = renderedPeer
|
||||
}
|
||||
|
||||
@ -614,11 +614,16 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
guard let strongSelf = self, let messageIds = strongSelf.stateValue.selectedMessageIds, !messageIds.isEmpty else {
|
||||
return
|
||||
}
|
||||
let _ = (strongSelf.context.account.postbox.transaction { transaction -> [EngineMessage] in
|
||||
let _ = (strongSelf.context.engine.data.get(EngineDataMap(
|
||||
messageIds.map { id -> TelegramEngine.EngineData.Item.Messages.Message in
|
||||
return TelegramEngine.EngineData.Item.Messages.Message(id: id)
|
||||
}
|
||||
))
|
||||
|> map { messageMap -> [EngineMessage] in
|
||||
var messages: [EngineMessage] = []
|
||||
for id in messageIds {
|
||||
if let message = transaction.getMessage(id) {
|
||||
messages.append(EngineMessage(message))
|
||||
if let messageValue = messageMap[id], let message = messageValue {
|
||||
messages.append(message)
|
||||
}
|
||||
}
|
||||
return messages
|
||||
@ -641,11 +646,16 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
guard let strongSelf = self, let messageIds = strongSelf.stateValue.selectedMessageIds, !messageIds.isEmpty else {
|
||||
return
|
||||
}
|
||||
let _ = (strongSelf.context.account.postbox.transaction { transaction -> [EngineMessage] in
|
||||
let _ = (strongSelf.context.engine.data.get(EngineDataMap(
|
||||
messageIds.map { id -> TelegramEngine.EngineData.Item.Messages.Message in
|
||||
return TelegramEngine.EngineData.Item.Messages.Message(id: id)
|
||||
}
|
||||
))
|
||||
|> map { messageMap -> [EngineMessage] in
|
||||
var messages: [EngineMessage] = []
|
||||
for id in messageIds {
|
||||
if let message = transaction.getMessage(id) {
|
||||
messages.append(EngineMessage(message))
|
||||
if let messageValue = messageMap[id], let message = messageValue {
|
||||
messages.append(message)
|
||||
}
|
||||
}
|
||||
return messages
|
||||
@ -898,43 +908,6 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
})))
|
||||
}
|
||||
|
||||
/*if !actions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty {
|
||||
if !items.isEmpty {
|
||||
items.append(.separator)
|
||||
}
|
||||
|
||||
if actions.options.contains(.deleteLocally) {
|
||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_DeleteMessagesForMe, textColor: .destructive, icon: { _ in
|
||||
return nil
|
||||
}, action: { controller, f in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: [message.id], type: .forLocalPeer).start()
|
||||
f(.dismissWithoutContent)
|
||||
})))
|
||||
}
|
||||
|
||||
if actions.options.contains(.deleteGlobally) {
|
||||
let text: String
|
||||
if let mainPeer = message.peers[message.id.peerId] {
|
||||
if mainPeer is TelegramUser {
|
||||
text = strongSelf.presentationData.strings.ChatList_DeleteForEveryone(EnginePeer(mainPeer).compactDisplayTitle).string
|
||||
} else {
|
||||
text = strongSelf.presentationData.strings.Conversation_DeleteMessagesForEveryone
|
||||
}
|
||||
} else {
|
||||
text = strongSelf.presentationData.strings.Conversation_DeleteMessagesForEveryone
|
||||
}
|
||||
items.append(.action(ContextMenuActionItem(text: text, textColor: .destructive, icon: { _ in
|
||||
return nil
|
||||
}, action: { controller, f in
|
||||
let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: [message.id], type: .forEveryone).start()
|
||||
f(.dismissWithoutContent)
|
||||
})))
|
||||
}
|
||||
}*/
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
@ -944,7 +917,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
return
|
||||
}
|
||||
|
||||
let _ = storedMessageFromSearch(account: self.context.account, message: message._asMessage()).start()
|
||||
self.context.engine.messages.ensureMessagesAreLocallyAvailable(messages: [message])
|
||||
|
||||
var linkForCopying: String?
|
||||
var currentSupernode: ASDisplayNode? = node
|
||||
@ -1093,8 +1066,19 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
|
||||
if let messageIds = messageIds ?? self.stateValue.selectedMessageIds, !messageIds.isEmpty {
|
||||
if isDownloads {
|
||||
let _ = (self.context.account.postbox.transaction { transaction -> [Message] in
|
||||
return messageIds.compactMap(transaction.getMessage)
|
||||
let _ = (self.context.engine.data.get(EngineDataMap(
|
||||
messageIds.map { id -> TelegramEngine.EngineData.Item.Messages.Message in
|
||||
return TelegramEngine.EngineData.Item.Messages.Message(id: id)
|
||||
}
|
||||
))
|
||||
|> map { messageMap -> [EngineMessage] in
|
||||
var messages: [EngineMessage] = []
|
||||
for id in messageIds {
|
||||
if let messageValue = messageMap[id], let message = messageValue {
|
||||
messages.append(message)
|
||||
}
|
||||
}
|
||||
return messages
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] messages in
|
||||
guard let strongSelf = self else {
|
||||
@ -1142,13 +1126,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
})
|
||||
} else {
|
||||
let (peers, messages) = self.currentMessages
|
||||
let _ = (self.context.account.postbox.transaction { transaction -> Void in
|
||||
for id in messageIds {
|
||||
if transaction.getMessage(id) == nil, let message = messages[id] {
|
||||
storeMessageFromSearch(transaction: transaction, message: message._asMessage())
|
||||
}
|
||||
}
|
||||
}).start()
|
||||
|
||||
self.context.engine.messages.ensureMessagesAreLocallyAvailable(messages: messages.values.filter { messageIds.contains($0.id) })
|
||||
|
||||
self.activeActionDisposable.set((self.context.sharedContext.chatAvailableMessageActions(postbox: self.context.account.postbox, accountPeerId: self.context.account.peerId, messageIds: messageIds, messages: messages, peers: peers)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] actions in
|
||||
@ -1211,13 +1190,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
let messageIds = messageIds ?? self.stateValue.selectedMessageIds
|
||||
if let messageIds = messageIds, !messageIds.isEmpty {
|
||||
let messages = self.paneContainerNode.allCurrentMessages()
|
||||
let _ = (self.context.account.postbox.transaction { transaction -> Void in
|
||||
for id in messageIds {
|
||||
if transaction.getMessage(id) == nil, let message = messages[id] {
|
||||
storeMessageFromSearch(transaction: transaction, message: message._asMessage())
|
||||
}
|
||||
}
|
||||
}).start()
|
||||
|
||||
self.context.engine.messages.ensureMessagesAreLocallyAvailable(messages: messages.values.filter { messageIds.contains($0.id) })
|
||||
|
||||
let peerSelectionController = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.onlyWriteable, .excludeDisabled], multipleSelection: true))
|
||||
peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, peerMap, messageText, mode, forwardOptions in
|
||||
|
||||
@ -799,7 +799,7 @@ public struct ChatListSearchOptions {
|
||||
|
||||
private struct DownloadItem: Equatable {
|
||||
let resourceId: MediaResourceId
|
||||
let message: Message
|
||||
let message: EngineMessage
|
||||
let priority: FetchManagerPriorityKey
|
||||
let isPaused: Bool
|
||||
|
||||
@ -1053,8 +1053,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
for entry in entries {
|
||||
switch entry.id.locationKey {
|
||||
case let .messageId(id):
|
||||
itemSignals.append(context.account.postbox.transaction { transaction -> DownloadItem? in
|
||||
if let message = transaction.getMessage(id) {
|
||||
itemSignals.append(context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: id))
|
||||
|> map { message -> DownloadItem? in
|
||||
if let message = message {
|
||||
return DownloadItem(resourceId: entry.resourceReference.resource.id, message: message, priority: entry.priority, isPaused: entry.isPaused)
|
||||
}
|
||||
return nil
|
||||
@ -1134,7 +1135,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}
|
||||
existingMessageIds.insert(item.message.id)
|
||||
|
||||
let message = EngineMessage(item.message)
|
||||
let message = item.message
|
||||
|
||||
if !queryTokens.isEmpty {
|
||||
if !messageMatchesTokens(message: message, tokens: queryTokens) {
|
||||
|
||||
@ -55,7 +55,7 @@ public final class HashtagSearchController: TelegramBaseController {
|
||||
}, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { [weak self] peer, message, _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.openMessageFromSearchDisposable.set((storedMessageFromSearchPeer(account: strongSelf.context.account, peer: peer._asPeer()) |> deliverOnMainQueue).start(next: { actualPeerId in
|
||||
strongSelf.openMessageFromSearchDisposable.set((strongSelf.context.engine.peers.ensurePeerIsLocallyAvailable(peer: peer) |> deliverOnMainQueue).start(next: { actualPeerId in
|
||||
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: actualPeerId), subject: message.id.peerId == actualPeerId ? .message(id: .id(message.id), highlight: true, timecode: nil) : nil, keepStack: .always))
|
||||
}
|
||||
|
||||
36
submodules/Postbox/Sources/MutableLocalNoticeEntryView.swift
Normal file
36
submodules/Postbox/Sources/MutableLocalNoticeEntryView.swift
Normal file
@ -0,0 +1,36 @@
|
||||
import Foundation
|
||||
|
||||
final class MutableLocalNoticeEntryView: MutablePostboxView {
|
||||
private let key: NoticeEntryKey
|
||||
fileprivate var value: CodableEntry?
|
||||
|
||||
init(postbox: PostboxImpl, key: NoticeEntryKey) {
|
||||
self.key = key
|
||||
self.value = postbox.noticeTable.get(key: key)
|
||||
}
|
||||
|
||||
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool {
|
||||
var updated = false
|
||||
if transaction.updatedNoticeEntryKeys.contains(self.key) {
|
||||
self.value = postbox.noticeTable.get(key: key)
|
||||
updated = true
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return LocalNoticeEntryView(self)
|
||||
}
|
||||
}
|
||||
|
||||
public final class LocalNoticeEntryView: PostboxView {
|
||||
public let value: CodableEntry?
|
||||
|
||||
init(_ view: MutableLocalNoticeEntryView) {
|
||||
self.value = view.value
|
||||
}
|
||||
}
|
||||
@ -33,6 +33,7 @@ public enum PostboxViewKey: Hashable {
|
||||
case topChatMessage(peerIds: [PeerId])
|
||||
case contacts(accountPeerId: PeerId?, includePresences: Bool)
|
||||
case deletedMessages(peerId: PeerId)
|
||||
case notice(key: NoticeEntryKey)
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
switch self {
|
||||
@ -109,6 +110,8 @@ public enum PostboxViewKey: Hashable {
|
||||
hasher.combine(16)
|
||||
case let .deletedMessages(peerId):
|
||||
hasher.combine(peerId)
|
||||
case let .notice(key):
|
||||
hasher.combine(key)
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,6 +309,12 @@ public enum PostboxViewKey: Hashable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .notice(key):
|
||||
if case .notice(key) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -376,5 +385,7 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost
|
||||
return MutableContactPeersView(postbox: postbox, accountPeerId: accountPeerId, includePresences: includePresences)
|
||||
case let .deletedMessages(peerId):
|
||||
return MutableDeletedMessagesView(peerId: peerId)
|
||||
case let .notice(key):
|
||||
return MutableLocalNoticeEntryView(postbox: postbox, key: key)
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,6 +174,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1460809483] = { return Api.Dialog.parse_dialog($0) }
|
||||
dict[1908216652] = { return Api.Dialog.parse_dialogFolder($0) }
|
||||
dict[1949890536] = { return Api.DialogFilter.parse_dialogFilter($0) }
|
||||
dict[909284270] = { return Api.DialogFilter.parse_dialogFilterDefault($0) }
|
||||
dict[2004110666] = { return Api.DialogFilterSuggested.parse_dialogFilterSuggested($0) }
|
||||
dict[-445792507] = { return Api.DialogPeer.parse_dialogPeer($0) }
|
||||
dict[1363483106] = { return Api.DialogPeer.parse_dialogPeerFolder($0) }
|
||||
@ -426,6 +427,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1262252875] = { return Api.MessageAction.parse_messageActionWebViewDataSent($0) }
|
||||
dict[1205698681] = { return Api.MessageAction.parse_messageActionWebViewDataSentMe($0) }
|
||||
dict[546203849] = { return Api.MessageEntity.parse_inputMessageEntityMentionName($0) }
|
||||
dict[1592721940] = { return Api.MessageEntity.parse_messageEntityAnimatedEmoji($0) }
|
||||
dict[1981704948] = { return Api.MessageEntity.parse_messageEntityBankCard($0) }
|
||||
dict[34469328] = { return Api.MessageEntity.parse_messageEntityBlockquote($0) }
|
||||
dict[-1117713463] = { return Api.MessageEntity.parse_messageEntityBold($0) }
|
||||
@ -985,7 +987,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[946083368] = { return Api.messages.StickerSetInstallResult.parse_stickerSetInstallResultSuccess($0) }
|
||||
dict[816245886] = { return Api.messages.Stickers.parse_stickers($0) }
|
||||
dict[-244016606] = { return Api.messages.Stickers.parse_stickersNotModified($0) }
|
||||
dict[-1442723025] = { return Api.messages.TranscribedAudio.parse_transcribedAudio($0) }
|
||||
dict[-1077051894] = { return Api.messages.TranscribedAudio.parse_transcribedAudio($0) }
|
||||
dict[1741309751] = { return Api.messages.TranslatedText.parse_translateNoResult($0) }
|
||||
dict[-1575684144] = { return Api.messages.TranslatedText.parse_translateResultText($0) }
|
||||
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
public extension Api {
|
||||
enum MessageEntity: TypeConstructorDescription {
|
||||
case inputMessageEntityMentionName(offset: Int32, length: Int32, userId: Api.InputUser)
|
||||
case messageEntityAnimatedEmoji(offset: Int32, length: Int32)
|
||||
case messageEntityBankCard(offset: Int32, length: Int32)
|
||||
case messageEntityBlockquote(offset: Int32, length: Int32)
|
||||
case messageEntityBold(offset: Int32, length: Int32)
|
||||
@ -31,6 +32,13 @@ public extension Api {
|
||||
serializeInt32(length, buffer: buffer, boxed: false)
|
||||
userId.serialize(buffer, true)
|
||||
break
|
||||
case .messageEntityAnimatedEmoji(let offset, let length):
|
||||
if boxed {
|
||||
buffer.appendInt32(1592721940)
|
||||
}
|
||||
serializeInt32(offset, buffer: buffer, boxed: false)
|
||||
serializeInt32(length, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .messageEntityBankCard(let offset, let length):
|
||||
if boxed {
|
||||
buffer.appendInt32(1981704948)
|
||||
@ -174,6 +182,8 @@ public extension Api {
|
||||
switch self {
|
||||
case .inputMessageEntityMentionName(let offset, let length, let userId):
|
||||
return ("inputMessageEntityMentionName", [("offset", String(describing: offset)), ("length", String(describing: length)), ("userId", String(describing: userId))])
|
||||
case .messageEntityAnimatedEmoji(let offset, let length):
|
||||
return ("messageEntityAnimatedEmoji", [("offset", String(describing: offset)), ("length", String(describing: length))])
|
||||
case .messageEntityBankCard(let offset, let length):
|
||||
return ("messageEntityBankCard", [("offset", String(describing: offset)), ("length", String(describing: length))])
|
||||
case .messageEntityBlockquote(let offset, let length):
|
||||
@ -234,6 +244,20 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_messageEntityAnimatedEmoji(_ reader: BufferReader) -> MessageEntity? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.MessageEntity.messageEntityAnimatedEmoji(offset: _1!, length: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_messageEntityBankCard(_ reader: BufferReader) -> MessageEntity? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
|
||||
@ -490,14 +490,15 @@ public extension Api.messages {
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum TranscribedAudio: TypeConstructorDescription {
|
||||
case transcribedAudio(text: String)
|
||||
case transcribedAudio(transcriptionId: Int64, text: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .transcribedAudio(let text):
|
||||
case .transcribedAudio(let transcriptionId, let text):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1442723025)
|
||||
buffer.appendInt32(-1077051894)
|
||||
}
|
||||
serializeInt64(transcriptionId, buffer: buffer, boxed: false)
|
||||
serializeString(text, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
@ -505,17 +506,20 @@ public extension Api.messages {
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .transcribedAudio(let text):
|
||||
return ("transcribedAudio", [("text", String(describing: text))])
|
||||
case .transcribedAudio(let transcriptionId, let text):
|
||||
return ("transcribedAudio", [("transcriptionId", String(describing: transcriptionId)), ("text", String(describing: text))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_transcribedAudio(_ reader: BufferReader) -> TranscribedAudio? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.messages.TranscribedAudio.transcribedAudio(text: _1!)
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.TranscribedAudio.transcribedAudio(transcriptionId: _1!, text: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
||||
@ -4963,6 +4963,24 @@ public extension Api.functions.messages {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func rateTranscribedAudio(peer: Api.InputPeer, msgId: Int32, transcriptionId: Int64, good: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(2132608815)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt64(transcriptionId, buffer: buffer, boxed: false)
|
||||
good.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "messages.rateTranscribedAudio", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("transcriptionId", String(describing: transcriptionId)), ("good", String(describing: good))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func readDiscussion(peer: Api.InputPeer, msgId: Int32, readMaxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
|
||||
@ -897,6 +897,7 @@ public extension Api {
|
||||
public extension Api {
|
||||
enum DialogFilter: TypeConstructorDescription {
|
||||
case dialogFilter(flags: Int32, id: Int32, title: String, emoticon: String?, pinnedPeers: [Api.InputPeer], includePeers: [Api.InputPeer], excludePeers: [Api.InputPeer])
|
||||
case dialogFilterDefault
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -923,6 +924,12 @@ public extension Api {
|
||||
for item in excludePeers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .dialogFilterDefault:
|
||||
if boxed {
|
||||
buffer.appendInt32(909284270)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -931,6 +938,8 @@ public extension Api {
|
||||
switch self {
|
||||
case .dialogFilter(let flags, let id, let title, let emoticon, let pinnedPeers, let includePeers, let excludePeers):
|
||||
return ("dialogFilter", [("flags", String(describing: flags)), ("id", String(describing: id)), ("title", String(describing: title)), ("emoticon", String(describing: emoticon)), ("pinnedPeers", String(describing: pinnedPeers)), ("includePeers", String(describing: includePeers)), ("excludePeers", String(describing: excludePeers))])
|
||||
case .dialogFilterDefault:
|
||||
return ("dialogFilterDefault", [])
|
||||
}
|
||||
}
|
||||
|
||||
@ -969,6 +978,9 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_dialogFilterDefault(_ reader: BufferReader) -> DialogFilter? {
|
||||
return Api.DialogFilter.dialogFilterDefault
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -374,6 +374,8 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes
|
||||
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BankCard))
|
||||
case let .messageEntitySpoiler(offset, length):
|
||||
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Spoiler))
|
||||
case let .messageEntityAnimatedEmoji(offset, length):
|
||||
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .AnimatedEmoji))
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
@ -48,6 +48,8 @@ func apiEntitiesFromMessageTextEntities(_ entities: [MessageTextEntity], associa
|
||||
apiEntities.append(.messageEntityBankCard(offset: offset, length: length))
|
||||
case .Spoiler:
|
||||
apiEntities.append(.messageEntitySpoiler(offset: offset, length: length))
|
||||
case .AnimatedEmoji:
|
||||
apiEntities.append(.messageEntityAnimatedEmoji(offset: offset, length: length))
|
||||
case .Custom:
|
||||
break
|
||||
}
|
||||
|
||||
@ -142,7 +142,7 @@ private func findMediaResource(media: Media, previousMedia: Media?, resource: Me
|
||||
return nil
|
||||
}
|
||||
|
||||
public func findMediaResourceById(message: Message, resourceId: MediaResourceId) -> TelegramMediaResource? {
|
||||
public func findMediaResourceById(message: EngineMessage, resourceId: MediaResourceId) -> TelegramMediaResource? {
|
||||
for media in message.media {
|
||||
if let result = findMediaResourceById(media: media, resourceId: resourceId) {
|
||||
return result
|
||||
|
||||
@ -709,6 +709,8 @@ private func decryptedEntities73(_ entities: [MessageTextEntity]?) -> [SecretApi
|
||||
break
|
||||
case .Spoiler:
|
||||
break
|
||||
case .AnimatedEmoji:
|
||||
break
|
||||
case .Custom:
|
||||
break
|
||||
}
|
||||
@ -760,6 +762,8 @@ private func decryptedEntities101(_ entities: [MessageTextEntity]?) -> [SecretAp
|
||||
break
|
||||
case .Spoiler:
|
||||
break
|
||||
case .AnimatedEmoji:
|
||||
break
|
||||
case .Custom:
|
||||
break
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ public enum MessageTextEntityType: Equatable {
|
||||
case Underline
|
||||
case BankCard
|
||||
case Spoiler
|
||||
case AnimatedEmoji
|
||||
case Custom(type: CustomEntityType)
|
||||
}
|
||||
|
||||
@ -71,6 +72,8 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable {
|
||||
self.type = .BankCard
|
||||
case 17:
|
||||
self.type = .Spoiler
|
||||
case 18:
|
||||
self.type = .AnimatedEmoji
|
||||
case Int32.max:
|
||||
self.type = .Custom(type: decoder.decodeInt32ForKey("type", orElse: 0))
|
||||
default:
|
||||
@ -126,6 +129,8 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable {
|
||||
self.type = .BankCard
|
||||
case 17:
|
||||
self.type = .Spoiler
|
||||
case 18:
|
||||
self.type = .AnimatedEmoji
|
||||
case Int32.max:
|
||||
let customType: Int32 = (try? container.decode(Int32.self, forKey: "type")) ?? 0
|
||||
self.type = .Custom(type: customType)
|
||||
@ -176,6 +181,8 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable {
|
||||
encoder.encodeInt32(16, forKey: "_rawValue")
|
||||
case .Spoiler:
|
||||
encoder.encodeInt32(17, forKey: "_rawValue")
|
||||
case .AnimatedEmoji:
|
||||
encoder.encodeInt32(18, forKey: "_rawValue")
|
||||
case let .Custom(type):
|
||||
encoder.encodeInt32(Int32.max, forKey: "_rawValue")
|
||||
encoder.encodeInt32(type, forKey: "type")
|
||||
@ -226,6 +233,8 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable {
|
||||
try container.encode(16 as Int32, forKey: "_rawValue")
|
||||
case .Spoiler:
|
||||
try container.encode(17 as Int32, forKey: "_rawValue")
|
||||
case .AnimatedEmoji:
|
||||
try container.encode(18 as Int32, forKey: "_rawValue")
|
||||
case let .Custom(type):
|
||||
try container.encode(Int32.max as Int32, forKey: "_rawValue")
|
||||
try container.encode(type as Int32, forKey: "type")
|
||||
|
||||
@ -166,5 +166,27 @@ public extension TelegramEngine.EngineData.Item {
|
||||
return EngineConfiguration.UserLimits(UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: self.isPremium))
|
||||
}
|
||||
}
|
||||
|
||||
public struct SuggestedLocalization: TelegramEngineDataItem, PostboxViewDataItem {
|
||||
public typealias Result = SuggestedLocalizationEntry?
|
||||
|
||||
public init() {
|
||||
}
|
||||
|
||||
var key: PostboxViewKey {
|
||||
return .preferences(keys: Set([PreferencesKeys.suggestedLocalization]))
|
||||
}
|
||||
|
||||
func extract(view: PostboxView) -> Result {
|
||||
guard let view = view as? PreferencesView else {
|
||||
preconditionFailure()
|
||||
}
|
||||
guard let suggestedLocalization = view.values[PreferencesKeys.suggestedLocalization]?.get(SuggestedLocalizationEntry.self) else {
|
||||
return nil
|
||||
}
|
||||
return suggestedLocalization
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,11 +54,15 @@ public extension EnginePeerReadCounters {
|
||||
|
||||
public extension TelegramEngine.EngineData.Item {
|
||||
enum Messages {
|
||||
public struct Message: TelegramEngineDataItem, PostboxViewDataItem {
|
||||
public struct Message: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
|
||||
public typealias Result = Optional<EngineMessage>
|
||||
|
||||
fileprivate var id: EngineMessage.Id
|
||||
|
||||
public var mapKey: EngineMessage.Id {
|
||||
return self.id
|
||||
}
|
||||
|
||||
public init(id: EngineMessage.Id) {
|
||||
self.id = id
|
||||
}
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
|
||||
public extension TelegramEngine.EngineData.Item {
|
||||
enum Notices {
|
||||
public struct Notice: TelegramEngineDataItem, PostboxViewDataItem {
|
||||
public typealias Result = CodableEntry?
|
||||
|
||||
private let entryKey: NoticeEntryKey
|
||||
|
||||
public init(key: NoticeEntryKey) {
|
||||
self.entryKey = key
|
||||
}
|
||||
|
||||
var key: PostboxViewKey {
|
||||
return .notice(key: self.entryKey)
|
||||
}
|
||||
|
||||
func extract(view: PostboxView) -> Result {
|
||||
guard let view = view as? LocalNoticeEntryView else {
|
||||
preconditionFailure()
|
||||
}
|
||||
return view.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -32,7 +32,7 @@ public extension TelegramEngine.EngineData.Item {
|
||||
}
|
||||
}
|
||||
|
||||
public struct RenderedPeer: TelegramEngineDataItem, PostboxViewDataItem {
|
||||
public struct RenderedPeer: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
|
||||
public typealias Result = Optional<EngineRenderedPeer>
|
||||
|
||||
fileprivate var id: EnginePeer.Id
|
||||
|
||||
@ -322,7 +322,7 @@ public extension TelegramEngine {
|
||||
return _internal_translate(network: self.account.network, text: text, fromLang: fromLang, toLang: toLang)
|
||||
}
|
||||
|
||||
public func transcribeAudio(messageId: MessageId) -> Signal<String?, NoError> {
|
||||
public func transcribeAudio(messageId: MessageId) -> Signal<EngineAudioTranscriptionResult?, NoError> {
|
||||
return _internal_transcribeAudio(postbox: self.account.postbox, network: self.account.network, messageId: messageId)
|
||||
}
|
||||
|
||||
@ -354,5 +354,13 @@ public extension TelegramEngine {
|
||||
public func attachMenuBots() -> Signal<[AttachMenuBot], NoError> {
|
||||
return _internal_attachMenuBots(postbox: self.account.postbox)
|
||||
}
|
||||
|
||||
public func ensureMessagesAreLocallyAvailable(messages: [EngineMessage]) {
|
||||
let _ = self.account.postbox.transaction({ transaction in
|
||||
for message in messages {
|
||||
_internal_storeMessageFromSearch(transaction: transaction, message: message._asMessage())
|
||||
}
|
||||
}).start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,11 +29,16 @@ func _internal_translate(network: Network, text: String, fromLang: String?, toLa
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_transcribeAudio(postbox: Postbox, network: Network, messageId: MessageId) -> Signal<String?, NoError> {
|
||||
public struct EngineAudioTranscriptionResult {
|
||||
public var id: Int64
|
||||
public var text: String
|
||||
}
|
||||
|
||||
func _internal_transcribeAudio(postbox: Postbox, network: Network, messageId: MessageId) -> Signal<EngineAudioTranscriptionResult?, NoError> {
|
||||
return postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<String?, NoError> in
|
||||
|> mapToSignal { inputPeer -> Signal<EngineAudioTranscriptionResult?, NoError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .single(nil)
|
||||
}
|
||||
@ -42,13 +47,13 @@ func _internal_transcribeAudio(postbox: Postbox, network: Network, messageId: Me
|
||||
|> `catch` { _ -> Signal<Api.messages.TranscribedAudio?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<String?, NoError> in
|
||||
|> mapToSignal { result -> Signal<EngineAudioTranscriptionResult?, NoError> in
|
||||
guard let result = result else {
|
||||
return .single(nil)
|
||||
}
|
||||
switch result {
|
||||
case let .transcribedAudio(string):
|
||||
return .single(string)
|
||||
case let .transcribedAudio(transcriptionId, text):
|
||||
return .single(EngineAudioTranscriptionResult(id: transcriptionId, text: text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,6 +336,8 @@ extension ChatListFilter {
|
||||
}
|
||||
)
|
||||
)
|
||||
case .dialogFilterDefault:
|
||||
preconditionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
@ -465,6 +467,8 @@ private func requestChatListFilters(accountPeerId: PeerId, postbox: Postbox, net
|
||||
}
|
||||
}
|
||||
}
|
||||
case .dialogFilterDefault:
|
||||
break
|
||||
}
|
||||
}
|
||||
return (filters, missingPeers, missingChats)
|
||||
|
||||
@ -718,6 +718,10 @@ public extension TelegramEngine {
|
||||
public func deleteNotificationSound(fileId: Int64) -> Signal<Never, DeleteNotificationSoundError> {
|
||||
return _internal_deleteNotificationSound(account: self.account, fileId: fileId)
|
||||
}
|
||||
|
||||
public func ensurePeerIsLocallyAvailable(peer: EnginePeer) -> Signal<EnginePeer.Id, NoError> {
|
||||
return _internal_storedMessageFromSearchPeer(account: self.account, peer: peer._asPeer())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
|
||||
public func storedMessageFromSearchPeer(account: Account, peer: Peer) -> Signal<PeerId, NoError> {
|
||||
func _internal_storedMessageFromSearchPeer(account: Account, peer: Peer) -> Signal<PeerId, NoError> {
|
||||
return account.postbox.transaction { transaction -> PeerId in
|
||||
if transaction.getPeer(peer.id) == nil {
|
||||
updatePeers(transaction: transaction, peers: [peer], update: { previousPeer, updatedPeer in
|
||||
@ -17,25 +16,7 @@ public func storedMessageFromSearchPeer(account: Account, peer: Peer) -> Signal<
|
||||
}
|
||||
}
|
||||
|
||||
public func storedMessageFromSearch(account: Account, message: Message) -> Signal<Void, NoError> {
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
if transaction.getMessage(message.id) == nil {
|
||||
for (_, peer) in message.peers {
|
||||
if transaction.getPeer(peer.id) == nil {
|
||||
updatePeers(transaction: transaction, peers: [peer], update: { previousPeer, updatedPeer in
|
||||
return updatedPeer
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let storeMessage = StoreMessage(id: .Id(message.id), globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, timestamp: message.timestamp, flags: StoreMessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: message.author?.id, text: message.text, attributes: message.attributes, media: message.media)
|
||||
|
||||
let _ = transaction.addMessages([storeMessage], location: .Random)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func storeMessageFromSearch(transaction: Transaction, message: Message) {
|
||||
func _internal_storeMessageFromSearch(transaction: Transaction, message: Message) {
|
||||
if transaction.getMessage(message.id) == nil {
|
||||
for (_, peer) in message.peers {
|
||||
if transaction.getPeer(peer.id) == nil {
|
||||
@ -13,6 +13,7 @@ public final class AudioWaveformComponent: Component {
|
||||
public let samples: Data
|
||||
public let peak: Int32
|
||||
public let status: Signal<MediaPlayerStatus, NoError>
|
||||
public let seek: (Double) -> Void
|
||||
|
||||
public init(
|
||||
backgroundColor: UIColor,
|
||||
@ -20,7 +21,8 @@ public final class AudioWaveformComponent: Component {
|
||||
shimmerColor: UIColor?,
|
||||
samples: Data,
|
||||
peak: Int32,
|
||||
status: Signal<MediaPlayerStatus, NoError>
|
||||
status: Signal<MediaPlayerStatus, NoError>,
|
||||
seek: @escaping (Double) -> Void
|
||||
) {
|
||||
self.backgroundColor = backgroundColor
|
||||
self.foregroundColor = foregroundColor
|
||||
@ -28,6 +30,7 @@ public final class AudioWaveformComponent: Component {
|
||||
self.samples = samples
|
||||
self.peak = peak
|
||||
self.status = status
|
||||
self.seek = seek
|
||||
}
|
||||
|
||||
public static func ==(lhs: AudioWaveformComponent, rhs: AudioWaveformComponent) -> Bool {
|
||||
@ -49,7 +52,7 @@ public final class AudioWaveformComponent: Component {
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
public final class View: UIView, UIGestureRecognizerDelegate {
|
||||
private struct ShimmerParams: Equatable {
|
||||
var backgroundColor: UIColor
|
||||
var foregroundColor: UIColor
|
||||
@ -147,17 +150,39 @@ public final class AudioWaveformComponent: Component {
|
||||
return LayerImpl.self
|
||||
}
|
||||
|
||||
private var panRecognizer: UIPanGestureRecognizer?
|
||||
|
||||
private var endScrubbing: ((Bool) -> Void)?
|
||||
private var updateScrubbing: ((CGFloat, Double) -> Void)?
|
||||
private var updateMultiplier: ((Double) -> Void)?
|
||||
|
||||
private var verticalPanEnabled = false
|
||||
|
||||
private var scrubbingMultiplier: Double = 1.0
|
||||
private var scrubbingStartLocation: CGPoint?
|
||||
|
||||
private var component: AudioWaveformComponent?
|
||||
private var validSize: CGSize?
|
||||
|
||||
private var playbackStatus: MediaPlayerStatus?
|
||||
private var scrubbingBeginTimestamp: Double?
|
||||
private var scrubbingTimestampValue: Double?
|
||||
private var isAwaitingScrubbingApplication: Bool = false
|
||||
private var statusDisposable: Disposable?
|
||||
private var playbackStatusAnimator: ConstantDisplayLinkAnimator?
|
||||
|
||||
private var revealProgress: CGFloat = 1.0
|
||||
private var animator: DisplayLinkAnimator?
|
||||
|
||||
public var enableScrubbing: Bool = false {
|
||||
didSet {
|
||||
if self.enableScrubbing != oldValue {
|
||||
self.disablesInteractiveTransitionGestureRecognizer = self.enableScrubbing
|
||||
self.panRecognizer?.isEnabled = self.enableScrubbing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
@ -170,6 +195,12 @@ public final class AudioWaveformComponent: Component {
|
||||
(self.layer as! LayerImpl).didExitHierarchy = { [weak self] in
|
||||
self?.updatePlaybackAnimation()
|
||||
}
|
||||
|
||||
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
||||
panRecognizer.delegate = self
|
||||
self.addGestureRecognizer(panRecognizer)
|
||||
panRecognizer.isEnabled = false
|
||||
self.panRecognizer = panRecognizer
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
@ -180,6 +211,86 @@ public final class AudioWaveformComponent: Component {
|
||||
self.statusDisposable?.dispose()
|
||||
}
|
||||
|
||||
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
var location = recognizer.location(in: self)
|
||||
location.x -= self.bounds.minX
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
self.scrubbingStartLocation = location
|
||||
self.beginScrubbing()
|
||||
case .changed:
|
||||
if let scrubbingStartLocation = self.scrubbingStartLocation {
|
||||
let delta = location.x - scrubbingStartLocation.x
|
||||
var multiplier: Double = 1.0
|
||||
var skipUpdate = false
|
||||
if self.verticalPanEnabled, location.y > scrubbingStartLocation.y {
|
||||
let verticalDelta = abs(location.y - scrubbingStartLocation.y)
|
||||
if verticalDelta > 150.0 {
|
||||
multiplier = 0.01
|
||||
} else if verticalDelta > 100.0 {
|
||||
multiplier = 0.25
|
||||
} else if verticalDelta > 50.0 {
|
||||
multiplier = 0.5
|
||||
}
|
||||
if multiplier != self.scrubbingMultiplier {
|
||||
skipUpdate = true
|
||||
self.scrubbingMultiplier = multiplier
|
||||
self.scrubbingStartLocation = CGPoint(x: location.x, y: scrubbingStartLocation.y)
|
||||
self.updateMultiplier?(multiplier)
|
||||
}
|
||||
}
|
||||
if !skipUpdate {
|
||||
self.updateScrubbing(addedFraction: delta / self.bounds.size.width, multiplier: multiplier)
|
||||
}
|
||||
}
|
||||
case .ended, .cancelled:
|
||||
if let scrubbingStartLocation = self.scrubbingStartLocation {
|
||||
self.scrubbingStartLocation = nil
|
||||
let delta = location.x - scrubbingStartLocation.x
|
||||
self.updateScrubbing?(delta / self.bounds.size.width, self.scrubbingMultiplier)
|
||||
self.endScrubbing(apply: recognizer.state == .ended)
|
||||
//self.highlighted?(false)
|
||||
self.scrubbingMultiplier = 1.0
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func beginScrubbing() {
|
||||
if let statusValue = self.playbackStatus, statusValue.duration > 0.0 {
|
||||
self.scrubbingBeginTimestamp = statusValue.timestamp
|
||||
self.scrubbingTimestampValue = statusValue.timestamp
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
private func endScrubbing(apply: Bool) {
|
||||
self.scrubbingBeginTimestamp = nil
|
||||
let scrubbingTimestampValue = self.scrubbingTimestampValue
|
||||
|
||||
self.isAwaitingScrubbingApplication = true
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2, execute: { [weak self] in
|
||||
guard let strongSelf = self, strongSelf.isAwaitingScrubbingApplication else {
|
||||
return
|
||||
}
|
||||
strongSelf.isAwaitingScrubbingApplication = false
|
||||
strongSelf.scrubbingTimestampValue = nil
|
||||
strongSelf.setNeedsDisplay()
|
||||
})
|
||||
|
||||
if let scrubbingTimestampValue = scrubbingTimestampValue, apply {
|
||||
self.component?.seek(scrubbingTimestampValue)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateScrubbing(addedFraction: CGFloat, multiplier: Double) {
|
||||
if let statusValue = self.playbackStatus, let scrubbingBeginTimestamp = self.scrubbingBeginTimestamp, Double(0.0).isLess(than: statusValue.duration) {
|
||||
self.scrubbingTimestampValue = scrubbingBeginTimestamp + (statusValue.duration * Double(addedFraction)) * multiplier
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
public func animateIn() {
|
||||
if self.animator == nil {
|
||||
self.revealProgress = 0.0
|
||||
@ -226,6 +337,12 @@ public final class AudioWaveformComponent: Component {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if strongSelf.isAwaitingScrubbingApplication, value.duration > 0.0, let scrubbingTimestampValue = strongSelf.scrubbingTimestampValue, abs(value.timestamp - scrubbingTimestampValue) <= value.duration * 0.01 {
|
||||
strongSelf.isAwaitingScrubbingApplication = false
|
||||
strongSelf.scrubbingTimestampValue = nil
|
||||
}
|
||||
|
||||
if strongSelf.playbackStatus != value {
|
||||
strongSelf.playbackStatus = value
|
||||
strongSelf.setNeedsDisplay()
|
||||
@ -439,6 +556,7 @@ public final class AudioWaveformComponent: Component {
|
||||
}*/
|
||||
|
||||
context.setFillColor(component.backgroundColor.mixedWith(component.foregroundColor, alpha: colorMixFraction).cgColor)
|
||||
context.setBlendMode(.copy)
|
||||
|
||||
let adjustedSampleHeight = sampleHeight - diff
|
||||
if adjustedSampleHeight.isLessThanOrEqualTo(sampleWidth) {
|
||||
@ -450,9 +568,9 @@ public final class AudioWaveformComponent: Component {
|
||||
width: sampleWidth,
|
||||
height: adjustedSampleHeight - halfSampleWidth
|
||||
)
|
||||
context.fill(adjustedRect)
|
||||
context.fillEllipse(in: CGRect(x: adjustedRect.minX, y: adjustedRect.minY - halfSampleWidth, width: sampleWidth, height: sampleWidth))
|
||||
context.fillEllipse(in: CGRect(x: adjustedRect.minX, y: adjustedRect.maxY - halfSampleWidth, width: sampleWidth, height: sampleWidth))
|
||||
context.fill(adjustedRect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,7 +368,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
}
|
||||
strongSelf.transcribeDisposable = nil
|
||||
strongSelf.audioTranscriptionState = .expanded
|
||||
strongSelf.transcribedText = result
|
||||
strongSelf.transcribedText = result?.text
|
||||
strongSelf.requestUpdateLayout(true)
|
||||
})
|
||||
}
|
||||
@ -962,7 +962,12 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
shimmerColor: isTranscriptionInProgress ? messageTheme.mediaActiveControlColor : nil,
|
||||
samples: audioWaveform?.samples ?? Data(),
|
||||
peak: audioWaveform?.peak ?? 0,
|
||||
status: strongSelf.playbackStatus.get()
|
||||
status: strongSelf.playbackStatus.get(),
|
||||
seek: { timestamp in
|
||||
if let strongSelf = self, let context = strongSelf.context, let message = strongSelf.message, let type = peerMessageMediaPlayerType(message) {
|
||||
context.sharedContext.mediaManager.playlistControl(.seek(timestamp), type: type)
|
||||
}
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: scrubbingFrame.size
|
||||
@ -1249,7 +1254,9 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
if self.message?.forwardInfo != nil {
|
||||
fetchStatus = resourceStatus.fetchStatus
|
||||
}
|
||||
(self.waveformView?.componentView as? AudioWaveformComponent.View)?.enableScrubbing = false
|
||||
//self.waveformScrubbingNode?.enableScrubbing = false
|
||||
|
||||
switch fetchStatus {
|
||||
case let .Fetching(_, progress):
|
||||
let adjustedProgress = max(progress, 0.027)
|
||||
@ -1283,7 +1290,9 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
case let .playbackStatus(playbackStatus):
|
||||
(self.waveformView?.componentView as? AudioWaveformComponent.View)?.enableScrubbing = true
|
||||
//self.waveformScrubbingNode?.enableScrubbing = true
|
||||
|
||||
switch playbackStatus {
|
||||
case .playing:
|
||||
state = .pause
|
||||
|
||||
@ -1846,7 +1846,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
guard let strongSelf = self, let node = node as? ContextExtractedContentContainingNode else {
|
||||
return
|
||||
}
|
||||
let _ = storedMessageFromSearch(account: strongSelf.context.account, message: message).start()
|
||||
|
||||
strongSelf.context.engine.messages.ensureMessagesAreLocallyAvailable(messages: [EngineMessage(message)])
|
||||
|
||||
var linkForCopying: String?
|
||||
var currentSupernode: ASDisplayNode? = node
|
||||
@ -2196,11 +2197,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
var foundGalleryMessage: Message?
|
||||
if let searchContentNode = strongSelf.searchDisplayController?.contentNode as? ChatHistorySearchContainerNode {
|
||||
if let galleryMessage = searchContentNode.messageForGallery(message.id) {
|
||||
let _ = (strongSelf.context.account.postbox.transaction { transaction -> Void in
|
||||
if transaction.getMessage(galleryMessage.id) == nil {
|
||||
storeMessageFromSearch(transaction: transaction, message: galleryMessage)
|
||||
}
|
||||
}).start()
|
||||
strongSelf.context.engine.messages.ensureMessagesAreLocallyAvailable(messages: [EngineMessage(galleryMessage)])
|
||||
foundGalleryMessage = galleryMessage
|
||||
}
|
||||
}
|
||||
@ -3200,11 +3197,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
var foundGalleryMessage: Message?
|
||||
if let searchContentNode = self.searchDisplayController?.contentNode as? ChatHistorySearchContainerNode {
|
||||
if let galleryMessage = searchContentNode.messageForGallery(id) {
|
||||
let _ = (self.context.account.postbox.transaction { transaction -> Void in
|
||||
if transaction.getMessage(galleryMessage.id) == nil {
|
||||
storeMessageFromSearch(transaction: transaction, message: galleryMessage)
|
||||
}
|
||||
}).start()
|
||||
self.context.engine.messages.ensureMessagesAreLocallyAvailable(messages: [EngineMessage(galleryMessage)])
|
||||
foundGalleryMessage = galleryMessage
|
||||
}
|
||||
}
|
||||
@ -5754,7 +5747,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
for childController in tabController.controllers {
|
||||
if let chatListController = childController as? ChatListController {
|
||||
chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), joined: false, deleteGloballyIfPossible: globally, completion: { [weak navigationController] deleted in
|
||||
chatListController.maybeAskForPeerChatRemoval(peer: EngineRenderedPeer(peer: EnginePeer(peer)), joined: false, deleteGloballyIfPossible: globally, completion: { [weak navigationController] deleted in
|
||||
if deleted {
|
||||
navigationController?.popToRoot(animated: true)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user