mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-24 07:05:35 +00:00
no message
This commit is contained in:
@@ -240,6 +240,7 @@
|
||||
D08D452F1D5E340300A7428A /* Display.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D08D452A1D5E340300A7428A /* Display.framework */; };
|
||||
D08D45301D5E340300A7428A /* Postbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D08D452B1D5E340300A7428A /* Postbox.framework */; };
|
||||
D08D45311D5E340300A7428A /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D08D452C1D5E340300A7428A /* SwiftSignalKit.framework */; };
|
||||
D099261F1E69791E00D95539 /* GroupsInCommonController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D099261E1E69791E00D95539 /* GroupsInCommonController.swift */; };
|
||||
D099EA1F1DE7450B001AF5A8 /* HorizontalListContextResultsChatInputContextPanelNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D099EA1E1DE7450B001AF5A8 /* HorizontalListContextResultsChatInputContextPanelNode.swift */; };
|
||||
D099EA211DE7451D001AF5A8 /* HorizontalListContextResultsChatInputPanelItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D099EA201DE7451D001AF5A8 /* HorizontalListContextResultsChatInputPanelItem.swift */; };
|
||||
D099EA271DE765DB001AF5A8 /* ManagedMediaId.swift in Sources */ = {isa = PBXBuildFile; fileRef = D099EA261DE765DB001AF5A8 /* ManagedMediaId.swift */; };
|
||||
@@ -717,6 +718,7 @@
|
||||
D08D452B1D5E340300A7428A /* Postbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Postbox.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphonesimulator/Postbox.framework"; sourceTree = "<group>"; };
|
||||
D08D452C1D5E340300A7428A /* SwiftSignalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftSignalKit.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphonesimulator/SwiftSignalKit.framework"; sourceTree = "<group>"; };
|
||||
D08D452D1D5E340300A7428A /* TelegramCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TelegramCore.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphonesimulator/TelegramCore.framework"; sourceTree = "<group>"; };
|
||||
D099261E1E69791E00D95539 /* GroupsInCommonController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupsInCommonController.swift; sourceTree = "<group>"; };
|
||||
D099EA1E1DE7450B001AF5A8 /* HorizontalListContextResultsChatInputContextPanelNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorizontalListContextResultsChatInputContextPanelNode.swift; sourceTree = "<group>"; };
|
||||
D099EA201DE7451D001AF5A8 /* HorizontalListContextResultsChatInputPanelItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorizontalListContextResultsChatInputPanelItem.swift; sourceTree = "<group>"; };
|
||||
D099EA261DE765DB001AF5A8 /* ManagedMediaId.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedMediaId.swift; sourceTree = "<group>"; };
|
||||
@@ -1781,6 +1783,7 @@
|
||||
D0613FD41E6064D200202CDB /* ConvertToSupergroupController.swift */,
|
||||
D033FEAA1E61BFC100644997 /* GroupAdminsController.swift */,
|
||||
D0528E621E65BECA00E2FEF5 /* UserInfoController.swift */,
|
||||
D099261E1E69791E00D95539 /* GroupsInCommonController.swift */,
|
||||
);
|
||||
name = Controller;
|
||||
sourceTree = "<group>";
|
||||
@@ -2778,6 +2781,7 @@
|
||||
D0EE971A1D88BCA0006C18E1 /* ChatInfo.swift in Sources */,
|
||||
D0F69DE31D6B8A420046BCD6 /* ListControllerItem.swift in Sources */,
|
||||
D0736F211DF41CFD00F2C02A /* ManagedAudioPlaylistPlayer.swift in Sources */,
|
||||
D099261F1E69791E00D95539 /* GroupsInCommonController.swift in Sources */,
|
||||
D0177B801DFAE18500A5083A /* MediaPlayerTimeTextNode.swift in Sources */,
|
||||
D07A7DA31D957671005BCD27 /* ListMessageSnippetItemNode.swift in Sources */,
|
||||
D0F69E6B1D6B8C160046BCD6 /* MapInputControllerNode.swift in Sources */,
|
||||
|
||||
@@ -38,6 +38,7 @@ public class ChatController: TelegramController {
|
||||
private var controllerInteraction: ChatControllerInteraction?
|
||||
private var interfaceInteraction: ChatPanelInterfaceInteraction?
|
||||
|
||||
private let messageContextDisposable = MetaDisposable()
|
||||
private let controllerNavigationDisposable = MetaDisposable()
|
||||
private let sentMessageEventsDisposable = MetaDisposable()
|
||||
private let messageActionCallbackDisposable = MetaDisposable()
|
||||
@@ -55,7 +56,7 @@ public class ChatController: TelegramController {
|
||||
private var resolveUrlDisposable: MetaDisposable?
|
||||
|
||||
private var contextQueryState: (ChatPresentationInputQuery?, Disposable)?
|
||||
private var urlPreviewQueryState: (URL?, Disposable)?
|
||||
private var urlPreviewQueryState: (String?, Disposable)?
|
||||
|
||||
private var audioRecorderValue: ManagedAudioRecorder?
|
||||
private var audioRecorderFeedback: HapticFeedback?
|
||||
@@ -336,7 +337,7 @@ public class ChatController: TelegramController {
|
||||
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageId(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: "")) }
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageId(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: "")).withUpdatedComposeDisableUrlPreview(nil) }
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -529,6 +530,7 @@ public class ChatController: TelegramController {
|
||||
self.navigationActionDisposable.dispose()
|
||||
self.galleryHiddenMesageAndMediaDisposable.dispose()
|
||||
self.peerDisposable.dispose()
|
||||
self.messageContextDisposable.dispose()
|
||||
self.controllerNavigationDisposable.dispose()
|
||||
self.sentMessageEventsDisposable.dispose()
|
||||
self.messageActionCallbackDisposable.dispose()
|
||||
@@ -872,6 +874,18 @@ public class ChatController: TelegramController {
|
||||
}
|
||||
}
|
||||
|
||||
self.chatDisplayNode.dismissUrlPreview = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if let (link, _) = strongSelf.presentationInterfaceState.urlPreview {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
$0.updatedInterfaceState {
|
||||
$0.withUpdatedComposeDisableUrlPreview(link)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.chatDisplayNode.navigateToLatestButton.tapped = { [weak self] in
|
||||
if let strongSelf = self, strongSelf.isNodeLoaded {
|
||||
strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory()
|
||||
@@ -901,9 +915,53 @@ public class ChatController: TelegramController {
|
||||
}, deleteSelectedMessages: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if let messageIds = strongSelf.presentationInterfaceState.interfaceState.selectionState?.selectedIds, !messageIds.isEmpty {
|
||||
let _ = deleteMessagesInteractively(postbox: strongSelf.account.postbox, messageIds: Array(messageIds), type: .forLocalPeer).start()
|
||||
strongSelf.messageContextDisposable.set((chatDeleteMessagesOptions(account: strongSelf.account, messageIds: messageIds) |> deliverOnMainQueue).start(next: { options in
|
||||
if let strongSelf = self, !options.isEmpty {
|
||||
let actionSheet = ActionSheetController()
|
||||
var items: [ActionSheetItem] = []
|
||||
var personalPeerName: String?
|
||||
var isChannel = false
|
||||
if let user = strongSelf.presentationInterfaceState.peer as? TelegramUser {
|
||||
personalPeerName = user.compactDisplayTitle
|
||||
} else if let channel = strongSelf.presentationInterfaceState.peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
isChannel = true
|
||||
}
|
||||
|
||||
if options.contains(.globally) {
|
||||
let globalTitle: String
|
||||
if isChannel {
|
||||
globalTitle = "Delete"
|
||||
} else if let personalPeerName = personalPeerName {
|
||||
globalTitle = "Delete for me and \(personalPeerName)"
|
||||
} else {
|
||||
globalTitle = "Delete for everyone"
|
||||
}
|
||||
items.append(ActionSheetButtonItem(title: globalTitle, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } })
|
||||
let _ = deleteMessagesInteractively(postbox: strongSelf.account.postbox, messageIds: Array(messageIds), type: .forEveryone).start()
|
||||
}
|
||||
}))
|
||||
}
|
||||
if options.contains(.locally) {
|
||||
items.append(ActionSheetButtonItem(title: "Delete for me", color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } })
|
||||
let _ = deleteMessagesInteractively(postbox: strongSelf.account.postbox, messageIds: Array(messageIds), type: .forLocalPeer).start()
|
||||
}
|
||||
}))
|
||||
}
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: "Cancel", color: .accent, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])])
|
||||
strongSelf.present(actionSheet, in: .window)
|
||||
}
|
||||
}))
|
||||
}
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } })
|
||||
}
|
||||
}, forwardSelectedMessages: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
@@ -997,7 +1055,7 @@ public class ChatController: TelegramController {
|
||||
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageId(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: "")) }
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageId(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: "")).withUpdatedComposeDisableUrlPreview(nil) }
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1039,7 +1097,7 @@ public class ChatController: TelegramController {
|
||||
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageId(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: "")) }
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageId(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: "")).withUpdatedComposeDisableUrlPreview(nil) }
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1316,14 +1374,22 @@ public class ChatController: TelegramController {
|
||||
inScopeResult = result
|
||||
} else {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedUrlPreview(result($0.urlPreview))
|
||||
if let updatedUrlPreviewUrl = updatedUrlPreviewUrl, let webpage = result($0.urlPreview?.1) {
|
||||
return $0.updatedUrlPreview((updatedUrlPreviewUrl, webpage))
|
||||
} else {
|
||||
return $0.updatedUrlPreview(nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}))
|
||||
inScope = false
|
||||
if let inScopeResult = inScopeResult {
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedUrlPreview(inScopeResult(updatedChatPresentationInterfaceState.urlPreview))
|
||||
if let updatedUrlPreviewUrl = updatedUrlPreviewUrl, let webpage = inScopeResult(updatedChatPresentationInterfaceState.urlPreview?.1) {
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedUrlPreview((updatedUrlPreviewUrl, webpage))
|
||||
} else {
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedUrlPreview(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1383,6 +1449,7 @@ public class ChatController: TelegramController {
|
||||
ActionSheetButtonItem(title: "Delete All Messages", color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } })
|
||||
let _ = clearHistoryInteractively(postbox: strongSelf.account.postbox, peerId: strongSelf.peerId).start()
|
||||
}
|
||||
})
|
||||
@@ -1468,7 +1535,7 @@ public class ChatController: TelegramController {
|
||||
self.chatDisplayNode.setupSendActionOnViewUpdate({ [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageId(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: "")) }
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageId(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: "")).withUpdatedComposeDisableUrlPreview(nil) }
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -44,6 +44,7 @@ class ChatControllerNode: ASDisplayNode {
|
||||
var requestUpdateChatInterfaceState: (Bool, (ChatInterfaceState) -> ChatInterfaceState) -> Void = { _ in }
|
||||
var displayAttachmentMenu: () -> Void = { }
|
||||
var updateTypingActivity: () -> Void = { }
|
||||
var dismissUrlPreview: () -> Void = { }
|
||||
var setupSendActionOnViewUpdate: (@escaping () -> Void) -> Void = { _ in }
|
||||
var requestLayout: (ContainedViewLayoutTransition) -> Void = { _ in }
|
||||
|
||||
@@ -131,7 +132,7 @@ class ChatControllerNode: ASDisplayNode {
|
||||
if let strongSelf = strongSelf, let textInputPanelNode = strongSelf.inputPanelNode as? ChatTextInputPanelNode {
|
||||
strongSelf.ignoreUpdateHeight = true
|
||||
textInputPanelNode.text = ""
|
||||
strongSelf.requestUpdateChatInterfaceState(false, { $0.withUpdatedReplyMessageId(nil).withUpdatedForwardMessageIds(nil) })
|
||||
strongSelf.requestUpdateChatInterfaceState(false, { $0.withUpdatedReplyMessageId(nil).withUpdatedForwardMessageIds(nil).withUpdatedComposeDisableUrlPreview(nil) })
|
||||
strongSelf.ignoreUpdateHeight = false
|
||||
}
|
||||
})
|
||||
@@ -143,7 +144,11 @@ class ChatControllerNode: ASDisplayNode {
|
||||
if !entities.isEmpty {
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
messages.append(.message(text: text, attributes: attributes, media: nil, replyToMessageId: strongSelf.chatPresentationInterfaceState.interfaceState.replyMessageId))
|
||||
if strongSelf.chatPresentationInterfaceState.interfaceState.composeDisableUrlPreview != nil {
|
||||
attributes.append(OutgoingContentInfoMessageAttribute(flags: [.disableLinkPreviews]))
|
||||
}
|
||||
let webpage = strongSelf.chatPresentationInterfaceState.urlPreview?.1
|
||||
messages.append(.message(text: text, attributes: attributes, media: webpage, replyToMessageId: strongSelf.chatPresentationInterfaceState.interfaceState.replyMessageId))
|
||||
}
|
||||
if let forwardMessageIds = strongSelf.chatPresentationInterfaceState.interfaceState.forwardMessageIds {
|
||||
for id in forwardMessageIds {
|
||||
@@ -316,7 +321,7 @@ class ChatControllerNode: ASDisplayNode {
|
||||
} else if let _ = accessoryPanelNode as? EditAccessoryPanelNode {
|
||||
strongSelf.requestUpdateChatInterfaceState(true, { $0.withUpdatedEditMessage(nil) })
|
||||
} else if let _ = accessoryPanelNode as? WebpagePreviewAccessoryPanelNode {
|
||||
|
||||
strongSelf.dismissUrlPreview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +218,7 @@ struct ChatInterfaceMessageActionsState: Coding, Equatable {
|
||||
final class ChatInterfaceState: PeerChatInterfaceState, Equatable {
|
||||
let timestamp: Int32
|
||||
let composeInputState: ChatTextInputState
|
||||
let composeDisableUrlPreview: String?
|
||||
let replyMessageId: MessageId?
|
||||
let forwardMessageIds: [MessageId]?
|
||||
let editMessage: ChatEditMessageState?
|
||||
@@ -243,6 +244,7 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable {
|
||||
init() {
|
||||
self.timestamp = 0
|
||||
self.composeInputState = ChatTextInputState()
|
||||
self.composeDisableUrlPreview = nil
|
||||
self.replyMessageId = nil
|
||||
self.forwardMessageIds = nil
|
||||
self.editMessage = nil
|
||||
@@ -250,9 +252,10 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable {
|
||||
self.messageActionsState = ChatInterfaceMessageActionsState()
|
||||
}
|
||||
|
||||
init(timestamp: Int32, composeInputState: ChatTextInputState, replyMessageId: MessageId?, forwardMessageIds: [MessageId]?, editMessage: ChatEditMessageState?, selectionState: ChatInterfaceSelectionState?, messageActionsState: ChatInterfaceMessageActionsState) {
|
||||
init(timestamp: Int32, composeInputState: ChatTextInputState, composeDisableUrlPreview: String?, replyMessageId: MessageId?, forwardMessageIds: [MessageId]?, editMessage: ChatEditMessageState?, selectionState: ChatInterfaceSelectionState?, messageActionsState: ChatInterfaceMessageActionsState) {
|
||||
self.timestamp = timestamp
|
||||
self.composeInputState = composeInputState
|
||||
self.composeDisableUrlPreview = composeDisableUrlPreview
|
||||
self.replyMessageId = replyMessageId
|
||||
self.forwardMessageIds = forwardMessageIds
|
||||
self.editMessage = editMessage
|
||||
@@ -267,6 +270,11 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable {
|
||||
} else {
|
||||
self.composeInputState = ChatTextInputState()
|
||||
}
|
||||
if let composeDisableUrlPreview = decoder.decodeStringForKey("dup") as String? {
|
||||
self.composeDisableUrlPreview = composeDisableUrlPreview
|
||||
} else {
|
||||
self.composeDisableUrlPreview = nil
|
||||
}
|
||||
let replyMessageIdPeerId: Int64? = decoder.decodeInt64ForKey("r.p")
|
||||
let replyMessageIdNamespace: Int32? = decoder.decodeInt32ForKey("r.n")
|
||||
let replyMessageIdId: Int32? = decoder.decodeInt32ForKey("r.i")
|
||||
@@ -301,6 +309,11 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable {
|
||||
func encode(_ encoder: Encoder) {
|
||||
encoder.encodeInt32(self.timestamp, forKey: "ts")
|
||||
encoder.encodeObject(self.composeInputState, forKey: "is")
|
||||
if let composeDisableUrlPreview = self.composeDisableUrlPreview {
|
||||
encoder.encodeString(composeDisableUrlPreview, forKey: "dup")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "dup")
|
||||
}
|
||||
if let replyMessageId = self.replyMessageId {
|
||||
encoder.encodeInt64(replyMessageId.peerId.toInt64(), forKey: "r.p")
|
||||
encoder.encodeInt32(replyMessageId.namespace, forKey: "r.n")
|
||||
@@ -343,6 +356,9 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable {
|
||||
}
|
||||
|
||||
static func ==(lhs: ChatInterfaceState, rhs: ChatInterfaceState) -> Bool {
|
||||
if lhs.composeDisableUrlPreview != rhs.composeDisableUrlPreview {
|
||||
return false
|
||||
}
|
||||
if let lhsForwardMessageIds = lhs.forwardMessageIds, let rhsForwardMessageIds = rhs.forwardMessageIds {
|
||||
if lhsForwardMessageIds != rhsForwardMessageIds {
|
||||
return false
|
||||
@@ -359,7 +375,11 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable {
|
||||
func withUpdatedComposeInputState(_ inputState: ChatTextInputState) -> ChatInterfaceState {
|
||||
let updatedComposeInputState = inputState
|
||||
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState)
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState)
|
||||
}
|
||||
|
||||
func withUpdatedComposeDisableUrlPreview(_ disableUrlPreview: String?) -> ChatInterfaceState {
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: disableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState)
|
||||
}
|
||||
|
||||
func withUpdatedEffectiveInputState(_ inputState: ChatTextInputState) -> ChatInterfaceState {
|
||||
@@ -371,15 +391,15 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable {
|
||||
updatedComposeInputState = inputState
|
||||
}
|
||||
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: updatedEditMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState)
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: updatedEditMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState)
|
||||
}
|
||||
|
||||
func withUpdatedReplyMessageId(_ replyMessageId: MessageId?) -> ChatInterfaceState {
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, replyMessageId: replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState)
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState)
|
||||
}
|
||||
|
||||
func withUpdatedForwardMessageIds(_ forwardMessageIds: [MessageId]?) -> ChatInterfaceState {
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, replyMessageId: self.replyMessageId, forwardMessageIds: forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState)
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState)
|
||||
}
|
||||
|
||||
func withUpdatedSelectedMessage(_ messageId: MessageId) -> ChatInterfaceState {
|
||||
@@ -388,7 +408,7 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable {
|
||||
selectedIds.formUnion(selectionState.selectedIds)
|
||||
}
|
||||
selectedIds.insert(messageId)
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState)
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState)
|
||||
}
|
||||
|
||||
func withToggledSelectedMessage(_ messageId: MessageId) -> ChatInterfaceState {
|
||||
@@ -401,22 +421,22 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable {
|
||||
} else {
|
||||
selectedIds.insert(messageId)
|
||||
}
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState)
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState)
|
||||
}
|
||||
|
||||
func withoutSelectionState() -> ChatInterfaceState {
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: nil, messageActionsState: self.messageActionsState)
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: nil, messageActionsState: self.messageActionsState)
|
||||
}
|
||||
|
||||
func withUpdatedTimestamp(_ timestamp: Int32) -> ChatInterfaceState {
|
||||
return ChatInterfaceState(timestamp: timestamp, composeInputState: self.composeInputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState)
|
||||
return ChatInterfaceState(timestamp: timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState)
|
||||
}
|
||||
|
||||
func withUpdatedEditMessage(_ editMessage: ChatEditMessageState?) -> ChatInterfaceState {
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState)
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState)
|
||||
}
|
||||
|
||||
func withUpdatedMessageActionsState(_ f: (ChatInterfaceMessageActionsState) -> ChatInterfaceMessageActionsState) -> ChatInterfaceState {
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: f(self.messageActionsState))
|
||||
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: f(self.messageActionsState))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceS
|
||||
panelNode.interfaceInteraction = interfaceInteraction
|
||||
return panelNode
|
||||
}
|
||||
} else if let urlPreview = chatPresentationInterfaceState.urlPreview {
|
||||
if let previewPanelNode = currentPanel as? WebpagePreviewAccessoryPanelNode, previewPanelNode.webpage.id == urlPreview.id {
|
||||
} else if let urlPreview = chatPresentationInterfaceState.urlPreview, chatPresentationInterfaceState.interfaceState.composeDisableUrlPreview != urlPreview.0 {
|
||||
if let previewPanelNode = currentPanel as? WebpagePreviewAccessoryPanelNode, previewPanelNode.webpage.id == urlPreview.1.id {
|
||||
previewPanelNode.interfaceInteraction = interfaceInteraction
|
||||
return previewPanelNode
|
||||
} else {
|
||||
let panelNode = WebpagePreviewAccessoryPanelNode(account: account, webpage: urlPreview)
|
||||
let panelNode = WebpagePreviewAccessoryPanelNode(account: account, webpage: urlPreview.1)
|
||||
panelNode.interfaceInteraction = interfaceInteraction
|
||||
return panelNode
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import Postbox
|
||||
import TelegramCore
|
||||
import Display
|
||||
import UIKit
|
||||
import SwiftSignalKit
|
||||
|
||||
func contextMenuForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, account: Account, message: Message, interfaceInteraction: ChatPanelInterfaceInteraction?) -> ContextMenuController? {
|
||||
guard let peer = chatPresentationInterfaceState.peer, let interfaceInteraction = interfaceInteraction else {
|
||||
@@ -23,6 +24,7 @@ func contextMenuForChatPresentationIntefaceState(_ chatPresentationInterfaceStat
|
||||
canReply = false
|
||||
}
|
||||
case .group:
|
||||
canReply = true
|
||||
switch channel.role {
|
||||
case .creator, .editor, .moderator:
|
||||
canPin = true
|
||||
@@ -94,3 +96,83 @@ func contextMenuForChatPresentationIntefaceState(_ chatPresentationInterfaceStat
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
struct ChatDeleteMessagesOptions: OptionSet {
|
||||
var rawValue: Int32
|
||||
|
||||
init(rawValue: Int32) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
init() {
|
||||
self.rawValue = 0
|
||||
}
|
||||
|
||||
static let locally = ChatDeleteMessagesOptions(rawValue: 1 << 0)
|
||||
static let globally = ChatDeleteMessagesOptions(rawValue: 1 << 1)
|
||||
}
|
||||
|
||||
func chatDeleteMessagesOptions(account: Account, messageIds: Set<MessageId>) -> Signal<ChatDeleteMessagesOptions, NoError> {
|
||||
return account.postbox.modify { modifier -> ChatDeleteMessagesOptions in
|
||||
var optionsMap: [MessageId: ChatDeleteMessagesOptions] = [:]
|
||||
for id in messageIds {
|
||||
if let peer = modifier.getPeer(id.peerId), let message = modifier.getMessage(id) {
|
||||
if let channel = peer as? TelegramChannel {
|
||||
var options: ChatDeleteMessagesOptions = []
|
||||
if !message.flags.contains(.Incoming) {
|
||||
options.insert(.globally)
|
||||
} else {
|
||||
switch channel.role {
|
||||
case .creator:
|
||||
options.insert(.globally)
|
||||
case .moderator, .editor:
|
||||
options.insert(.globally)
|
||||
case .member:
|
||||
break
|
||||
}
|
||||
}
|
||||
optionsMap[message.id] = options
|
||||
} else if let group = peer as? TelegramGroup {
|
||||
var options: ChatDeleteMessagesOptions = []
|
||||
options.insert(.locally)
|
||||
if !message.flags.contains(.Incoming) {
|
||||
options.insert(.globally)
|
||||
} else {
|
||||
switch group.role {
|
||||
case .creator, .admin:
|
||||
options.insert(.globally)
|
||||
case .member:
|
||||
break
|
||||
}
|
||||
}
|
||||
optionsMap[message.id] = options
|
||||
} else if let _ = peer as? TelegramUser {
|
||||
var options: ChatDeleteMessagesOptions = []
|
||||
options.insert(.locally)
|
||||
if !message.flags.contains(.Incoming) {
|
||||
options.insert(.globally)
|
||||
}
|
||||
optionsMap[message.id] = options
|
||||
} else if let _ = peer as? TelegramSecretChat {
|
||||
var options: ChatDeleteMessagesOptions = []
|
||||
options.insert(.globally)
|
||||
optionsMap[message.id] = options
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
} else {
|
||||
optionsMap[id] = [.locally]
|
||||
}
|
||||
}
|
||||
|
||||
if !optionsMap.isEmpty {
|
||||
var reducedOptions = optionsMap.values.first!
|
||||
for value in optionsMap.values {
|
||||
reducedOptions.formIntersection(value)
|
||||
}
|
||||
return reducedOptions
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,22 +182,22 @@ func contextQueryResultStateForChatInterfacePresentationState(_ chatPresentation
|
||||
|
||||
private let dataDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType([.link]).rawValue)
|
||||
|
||||
func urlPreviewStateForChatInterfacePresentationState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, account: Account, currentQuery: URL?) -> (URL?, Signal<(TelegramMediaWebpage?) -> TelegramMediaWebpage?, NoError>)? {
|
||||
func urlPreviewStateForChatInterfacePresentationState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, account: Account, currentQuery: String?) -> (String?, Signal<(TelegramMediaWebpage?) -> TelegramMediaWebpage?, NoError>)? {
|
||||
if let dataDetector = dataDetector {
|
||||
let text = chatPresentationInterfaceState.interfaceState.composeInputState.inputText
|
||||
let utf16 = text.utf16
|
||||
|
||||
var detectedUrl: URL?
|
||||
var detectedUrl: String?
|
||||
|
||||
let matches = dataDetector.matches(in: text, options: [], range: NSRange(location: 0, length: utf16.count))
|
||||
if let match = matches.first {
|
||||
let urlText = (text as NSString).substring(with: match.range)
|
||||
detectedUrl = URL(string: urlText)
|
||||
detectedUrl = urlText
|
||||
}
|
||||
|
||||
if detectedUrl != currentQuery {
|
||||
if let detectedUrl = detectedUrl {
|
||||
return (detectedUrl, webpagePreview(account: account, url: detectedUrl.absoluteString) |> map { value in
|
||||
return (detectedUrl, webpagePreview(account: account, url: detectedUrl) |> map { value in
|
||||
return { _ in return value }
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -596,13 +596,13 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
let contentRect = rawContentRect.offsetBy(dx: editingOffset + 78.0 + revealOffset, dy: 0.0)
|
||||
|
||||
transition.updateFrame(node: strongSelf.dateNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x + contentRect.size.width - dateLayout.size.width, y: contentRect.origin.y + 2.0), size: dateLayout.size))
|
||||
strongSelf.dateNode.frame = CGRect(origin: CGPoint(x: contentRect.origin.x + contentRect.size.width - dateLayout.size.width, y: contentRect.origin.y + 2.0), size: dateLayout.size)
|
||||
|
||||
if let statusImage = statusImage {
|
||||
strongSelf.statusNode.image = statusImage
|
||||
strongSelf.statusNode.isHidden = false
|
||||
let statusSize = statusImage.size
|
||||
transition.updateFrame(node: strongSelf.statusNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x + contentRect.size.width - dateLayout.size.width - 2.0 - statusSize.width, y: contentRect.origin.y + 5.0), size: statusSize))
|
||||
strongSelf.statusNode.frame = CGRect(origin: CGPoint(x: contentRect.origin.x + contentRect.size.width - dateLayout.size.width - 2.0 - statusSize.width, y: contentRect.origin.y + 5.0), size: statusSize)
|
||||
} else {
|
||||
strongSelf.statusNode.image = nil
|
||||
strongSelf.statusNode.isHidden = true
|
||||
@@ -616,8 +616,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let badgeBackgroundFrame = CGRect(x: contentRect.maxX - badgeBackgroundWidth, y: contentRect.maxY - currentBadgeBackgroundImage.size.height - 2.0, width: badgeBackgroundWidth, height: currentBadgeBackgroundImage.size.height)
|
||||
let badgeTextFrame = CGRect(origin: CGPoint(x: badgeBackgroundFrame.midX - badgeLayout.size.width / 2.0, y: badgeBackgroundFrame.minY + 1.0), size: badgeLayout.size)
|
||||
|
||||
transition.updateFrame(node: strongSelf.badgeTextNode, frame: badgeTextFrame)
|
||||
transition.updateFrame(node: strongSelf.badgeBackgroundNode, frame: badgeBackgroundFrame)
|
||||
strongSelf.badgeTextNode.frame = badgeTextFrame
|
||||
strongSelf.badgeBackgroundNode.frame = badgeBackgroundFrame
|
||||
} else {
|
||||
strongSelf.badgeBackgroundNode.image = nil
|
||||
strongSelf.badgeBackgroundNode.isHidden = true
|
||||
@@ -632,9 +632,17 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.mutedIconNode.isHidden = true
|
||||
}
|
||||
|
||||
transition.updateFrame(node: strongSelf.titleNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x, y: contentRect.origin.y), size: titleLayout.size))
|
||||
let contentDeltaX = contentRect.origin.x - strongSelf.titleNode.frame.minX
|
||||
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: contentRect.origin.x, y: contentRect.origin.y), size: titleLayout.size)
|
||||
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: contentRect.origin.x, y: contentRect.maxY - textLayout.size.height - 1.0), size: textLayout.size)
|
||||
|
||||
transition.updateFrame(node: strongSelf.textNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x, y: contentRect.maxY - textLayout.size.height - 1.0), size: textLayout.size))
|
||||
if !contentDeltaX.isZero {
|
||||
let titlePosition = strongSelf.titleNode.position
|
||||
transition.animatePosition(node: strongSelf.titleNode, from: CGPoint(x: titlePosition.x - contentDeltaX, y: titlePosition.y))
|
||||
|
||||
let textPosition = strongSelf.textNode.position
|
||||
transition.animatePosition(node: strongSelf.textNode, from: CGPoint(x: textPosition.x - contentDeltaX, y: textPosition.y))
|
||||
}
|
||||
|
||||
let separatorInset: CGFloat
|
||||
if !nextIsPinned && item.index.pinningIndex != nil {
|
||||
@@ -663,7 +671,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -separatorHeight - topNegativeInset), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height + separatorHeight + topNegativeInset))
|
||||
|
||||
/*if crossfadeContent && animated {
|
||||
if let contents = strongSelf.contentNode.contents {
|
||||
if let contents = strongSelf.titleNode.contents {
|
||||
let tempNode = ASDisplayNode()
|
||||
tempNode.isLayerBacked = true
|
||||
tempNode.contents = contents
|
||||
@@ -674,9 +682,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
tempNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
if updateContentNode {
|
||||
strongSelf.contentNode.setNeedsDisplay()
|
||||
}*/
|
||||
|
||||
strongSelf.setRevealOptions(peerRevealOptions)
|
||||
|
||||
@@ -107,7 +107,7 @@ private func chatMediaInputGridEntries(view: ItemCollectionsView, recentStickers
|
||||
}
|
||||
|
||||
if let recentStickers = recentStickers, !recentStickers.items.isEmpty {
|
||||
let packInfo = StickerPackCollectionInfo(id: ItemCollectionId(namespace: Namespaces.ItemCollection.CloudRecentStickers, id: 0), accessHash: 0, title: "FREQUENTLY USED", shortName: "", hash: 0)
|
||||
let packInfo = StickerPackCollectionInfo(id: ItemCollectionId(namespace: Namespaces.ItemCollection.CloudRecentStickers, id: 0), flags: [], accessHash: 0, title: "FREQUENTLY USED", shortName: "", hash: 0)
|
||||
for i in 0 ..< min(20, recentStickers.items.count) {
|
||||
if let item = recentStickers.items[i].contents as? RecentMediaItem, let file = item.media as? TelegramMediaFile, let mediaId = item.media.id {
|
||||
let index = ItemCollectionItemIndex(index: Int32(i), id: mediaId.id)
|
||||
|
||||
@@ -179,7 +179,7 @@ struct ChatPresentationInterfaceState: Equatable {
|
||||
let canReportPeer: Bool
|
||||
let chatHistoryState: ChatHistoryNodeHistoryState?
|
||||
let botStartPayload: String?
|
||||
let urlPreview: TelegramMediaWebpage?
|
||||
let urlPreview: (String, TelegramMediaWebpage)?
|
||||
|
||||
init() {
|
||||
self.interfaceState = ChatInterfaceState()
|
||||
@@ -197,7 +197,7 @@ struct ChatPresentationInterfaceState: Equatable {
|
||||
self.urlPreview = nil
|
||||
}
|
||||
|
||||
init(interfaceState: ChatInterfaceState, peer: Peer?, inputTextPanelState: ChatTextInputPanelState, inputQueryResult: ChatPresentationInputQueryResult?, inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, peerIsBlocked: Bool, canReportPeer: Bool, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: TelegramMediaWebpage?) {
|
||||
init(interfaceState: ChatInterfaceState, peer: Peer?, inputTextPanelState: ChatTextInputPanelState, inputQueryResult: ChatPresentationInputQueryResult?, inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, peerIsBlocked: Bool, canReportPeer: Bool, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?) {
|
||||
self.interfaceState = interfaceState
|
||||
self.peer = peer
|
||||
self.inputTextPanelState = inputTextPanelState
|
||||
@@ -273,7 +273,10 @@ struct ChatPresentationInterfaceState: Equatable {
|
||||
}
|
||||
|
||||
if let lhsUrlPreview = lhs.urlPreview, let rhsUrlPreview = rhs.urlPreview {
|
||||
if !lhsUrlPreview.isEqual(rhsUrlPreview) {
|
||||
if lhsUrlPreview.0 != rhsUrlPreview.0 {
|
||||
return false
|
||||
}
|
||||
if !lhsUrlPreview.1.isEqual(rhsUrlPreview.1) {
|
||||
return false
|
||||
}
|
||||
} else if (lhs.urlPreview != nil) != (rhs.urlPreview != nil) {
|
||||
@@ -331,7 +334,7 @@ struct ChatPresentationInterfaceState: Equatable {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, peer: self.peer, inputTextPanelState: self.inputTextPanelState, inputQueryResult: self.inputQueryResult, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, peerIsBlocked: self.peerIsBlocked, canReportPeer: self.canReportPeer, chatHistoryState: chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview)
|
||||
}
|
||||
|
||||
func updatedUrlPreview(_ urlPreview: TelegramMediaWebpage?) -> ChatPresentationInterfaceState {
|
||||
func updatedUrlPreview(_ urlPreview: (String, TelegramMediaWebpage)?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, peer: self.peer, inputTextPanelState: self.inputTextPanelState, inputQueryResult: self.inputQueryResult, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, peerIsBlocked: self.peerIsBlocked, canReportPeer: self.canReportPeer, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: urlPreview)
|
||||
}
|
||||
}
|
||||
|
||||
181
TelegramUI/GroupsInCommonController.swift
Normal file
181
TelegramUI/GroupsInCommonController.swift
Normal file
@@ -0,0 +1,181 @@
|
||||
import Foundation
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
|
||||
private final class GroupsInCommonControllerArguments {
|
||||
let account: Account
|
||||
|
||||
let openPeer: (PeerId) -> Void
|
||||
|
||||
init(account: Account, openPeer: @escaping (PeerId) -> Void) {
|
||||
self.account = account
|
||||
self.openPeer = openPeer
|
||||
}
|
||||
}
|
||||
|
||||
private enum GroupsInCommonSection: Int32 {
|
||||
case peers
|
||||
}
|
||||
|
||||
private enum GroupsInCommonEntryStableId: Hashable {
|
||||
case peer(PeerId)
|
||||
|
||||
var hashValue: Int {
|
||||
switch self {
|
||||
case let .peer(peerId):
|
||||
return peerId.hashValue
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: GroupsInCommonEntryStableId, rhs: GroupsInCommonEntryStableId) -> Bool {
|
||||
switch lhs {
|
||||
case let .peer(peerId):
|
||||
if case .peer(peerId) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum GroupsInCommonEntry: ItemListNodeEntry {
|
||||
case peerItem(Int32, Peer)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .peerItem:
|
||||
return GroupsInCommonSection.peers.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: GroupsInCommonEntryStableId {
|
||||
switch self {
|
||||
case let .peerItem(_, peer):
|
||||
return .peer(peer.id)
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: GroupsInCommonEntry, rhs: GroupsInCommonEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .peerItem(lhsIndex, lhsPeer):
|
||||
if case let .peerItem(rhsIndex, rhsPeer) = rhs {
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
if !lhsPeer.isEqual(rhsPeer) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func <(lhs: GroupsInCommonEntry, rhs: GroupsInCommonEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .peerItem(index, _):
|
||||
switch rhs {
|
||||
case let .peerItem(rhsIndex, _):
|
||||
return index < rhsIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func item(_ arguments: GroupsInCommonControllerArguments) -> ListViewItem {
|
||||
switch self {
|
||||
case let .peerItem(_, peer):
|
||||
return ItemListPeerItem(account: arguments.account, peer: peer, presence: nil, text: .none, label: nil, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, sectionId: self.section, action: {
|
||||
arguments.openPeer(peer.id)
|
||||
}, setPeerIdWithRevealedOptions: { _ in
|
||||
}, removePeer: { _ in
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct GroupsInCommonControllerState: Equatable {
|
||||
static func ==(lhs: GroupsInCommonControllerState, rhs: GroupsInCommonControllerState) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private func groupsInCommonControllerEntries(state: GroupsInCommonControllerState, peers: [Peer]?) -> [GroupsInCommonEntry] {
|
||||
var entries: [GroupsInCommonEntry] = []
|
||||
|
||||
if let peers = peers {
|
||||
var index: Int32 = 0
|
||||
for peer in peers {
|
||||
entries.append(.peerItem(index, peer))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
public func groupsInCommonController(account: Account, peerId: PeerId) -> ViewController {
|
||||
let statePromise = ValuePromise(GroupsInCommonControllerState(), ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: GroupsInCommonControllerState())
|
||||
let updateState: ((GroupsInCommonControllerState) -> GroupsInCommonControllerState) -> Void = { f in
|
||||
statePromise.set(stateValue.modify { f($0) })
|
||||
}
|
||||
|
||||
let actionsDisposable = DisposableSet()
|
||||
|
||||
let peersPromise = Promise<[Peer]?>(nil)
|
||||
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
|
||||
let arguments = GroupsInCommonControllerArguments(account: account, openPeer: { memberId in
|
||||
pushControllerImpl?(ChatController(account: account, peerId: memberId))
|
||||
})
|
||||
|
||||
let peersSignal: Signal<[Peer]?, NoError> = .single(nil) |> then(groupsInCommon(account: account, peerId: peerId) |> mapToSignal { peerIds -> Signal<[Peer], NoError> in
|
||||
return account.postbox.modify { modifier -> [Peer] in
|
||||
var result: [Peer] = []
|
||||
for id in peerIds {
|
||||
if let peer = modifier.getPeer(id) {
|
||||
result.append(peer)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|> map { Optional($0) })
|
||||
|
||||
peersPromise.set(peersSignal)
|
||||
|
||||
var previousPeers: [Peer]?
|
||||
|
||||
let signal = combineLatest(statePromise.get(), peersPromise.get())
|
||||
|> deliverOnMainQueue
|
||||
|> map { state, peers -> (ItemListControllerState, (ItemListNodeState<GroupsInCommonEntry>, GroupsInCommonEntry.ItemGenerationArguments)) in
|
||||
var emptyStateItem: ItemListControllerEmptyStateItem?
|
||||
if peers == nil {
|
||||
emptyStateItem = ItemListLoadingIndicatorEmptyStateItem()
|
||||
}
|
||||
|
||||
let previous = previousPeers
|
||||
previousPeers = peers
|
||||
|
||||
let controllerState = ItemListControllerState(title: "Groups in Common", leftNavigationButton: nil, rightNavigationButton: nil, animateChanges: false)
|
||||
let listState = ItemListNodeState(entries: groupsInCommonControllerEntries(state: state, peers: peers), style: .blocks, emptyStateItem: emptyStateItem, animateChanges: previous != nil && peers != nil && previous!.count >= peers!.count)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
} |> afterDisposed {
|
||||
actionsDisposable.dispose()
|
||||
}
|
||||
|
||||
let controller = ItemListController(signal)
|
||||
pushControllerImpl = { [weak controller] c in
|
||||
if let controller = controller {
|
||||
(controller.navigationController as? NavigationController)?.pushViewController(c)
|
||||
}
|
||||
}
|
||||
controller.navigationItem.backBarButtonItem = UIBarButtonItem(title: "Back", style: .plain, target: nil, action: nil)
|
||||
return controller
|
||||
}
|
||||
@@ -34,7 +34,7 @@ public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, me
|
||||
if data.complete {
|
||||
if file.mimeType.hasPrefix("image/") {
|
||||
return Signal { subscriber in
|
||||
if let image = UIImage(contentsOfFile: data.path), let scaledImage = generateImage(image.size.fitted(CGSize(width: 90.0, height: 90.0)), context: { size, context in
|
||||
if let image = UIImage(contentsOfFile: data.path), let scaledImage = generateImage(image.size.fitted(CGSize(width: 90.0, height: 90.0)), contextGenerator: { size, context in
|
||||
context.setBlendMode(.copy)
|
||||
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size))
|
||||
}), let thumbnailData = UIImageJPEGRepresentation(scaledImage, 0.6) {
|
||||
|
||||
@@ -9,14 +9,16 @@ private final class UserInfoControllerArguments {
|
||||
let updateEditingName: (ItemListAvatarAndNameInfoItemName) -> Void
|
||||
let changeNotificationMuteSettings: () -> Void
|
||||
let openSharedMedia: () -> Void
|
||||
let openGroupsInCommon: () -> Void
|
||||
let updatePeerBlocked: (Bool) -> Void
|
||||
let deleteContact: () -> Void
|
||||
|
||||
init(account: Account, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, changeNotificationMuteSettings: @escaping () -> Void, openSharedMedia: @escaping () -> Void, updatePeerBlocked: @escaping (Bool) -> Void, deleteContact: @escaping () -> Void) {
|
||||
init(account: Account, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, changeNotificationMuteSettings: @escaping () -> Void, openSharedMedia: @escaping () -> Void, openGroupsInCommon: @escaping () -> Void, updatePeerBlocked: @escaping (Bool) -> Void, deleteContact: @escaping () -> Void) {
|
||||
self.account = account
|
||||
self.updateEditingName = updateEditingName
|
||||
self.changeNotificationMuteSettings = changeNotificationMuteSettings
|
||||
self.openSharedMedia = openSharedMedia
|
||||
self.openGroupsInCommon = openGroupsInCommon
|
||||
self.updatePeerBlocked = updatePeerBlocked
|
||||
self.deleteContact = deleteContact
|
||||
}
|
||||
@@ -40,6 +42,7 @@ private enum UserInfoEntry: ItemListNodeEntry {
|
||||
case sharedMedia
|
||||
case notifications(settings: PeerNotificationSettings?)
|
||||
case notificationSound(settings: PeerNotificationSettings?)
|
||||
case groupsInCommon(Int32)
|
||||
case secretEncryptionKey(SecretChatKeyFingerprint)
|
||||
case block(action: DestructiveUserInfoAction)
|
||||
|
||||
@@ -49,7 +52,7 @@ private enum UserInfoEntry: ItemListNodeEntry {
|
||||
return UserInfoSection.info.rawValue
|
||||
case .sendMessage, .shareContact, .startSecretChat:
|
||||
return UserInfoSection.actions.rawValue
|
||||
case .sharedMedia, .notifications, .notificationSound, .secretEncryptionKey:
|
||||
case .sharedMedia, .notifications, .notificationSound, .secretEncryptionKey, .groupsInCommon:
|
||||
return UserInfoSection.sharedMediaAndNotifications.rawValue
|
||||
case .block:
|
||||
return UserInfoSection.block.rawValue
|
||||
@@ -166,6 +169,12 @@ private enum UserInfoEntry: ItemListNodeEntry {
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case let .groupsInCommon(count):
|
||||
if case .groupsInCommon(count) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .secretEncryptionKey(fingerprint):
|
||||
if case .secretEncryptionKey(fingerprint) = rhs {
|
||||
return true
|
||||
@@ -204,10 +213,12 @@ private enum UserInfoEntry: ItemListNodeEntry {
|
||||
return 1005
|
||||
case .notificationSound:
|
||||
return 1006
|
||||
case .secretEncryptionKey:
|
||||
case .groupsInCommon:
|
||||
return 1007
|
||||
case .block:
|
||||
case .secretEncryptionKey:
|
||||
return 1008
|
||||
case .block:
|
||||
return 1009
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,6 +269,10 @@ private enum UserInfoEntry: ItemListNodeEntry {
|
||||
label = "Default"
|
||||
return ItemListDisclosureItem(title: "Sound", label: label, sectionId: self.section, style: .plain, action: {
|
||||
})
|
||||
case let .groupsInCommon(count):
|
||||
return ItemListDisclosureItem(title: "Groups in Common", label: "\(count)", sectionId: self.section, style: .plain, action: {
|
||||
arguments.openGroupsInCommon()
|
||||
})
|
||||
case let .secretEncryptionKey(fingerprint):
|
||||
return ItemListDisclosureItem(title: "Encryption Key", label: "", sectionId: self.section, style: .plain, action: {
|
||||
})
|
||||
@@ -360,11 +375,6 @@ private func userInfoEntries(account: Account, view: PeerView, state: UserInfoSt
|
||||
}
|
||||
}
|
||||
|
||||
var editable = true
|
||||
if peer is TelegramSecretChat {
|
||||
editable = false
|
||||
}
|
||||
|
||||
if let phoneNumber = user.phone, !phoneNumber.isEmpty {
|
||||
entries.append(UserInfoEntry.phoneNumber(index: 0, value: PhoneNumberWithLabel(label: "home", number: phoneNumber)))
|
||||
}
|
||||
@@ -384,6 +394,9 @@ private func userInfoEntries(account: Account, view: PeerView, state: UserInfoSt
|
||||
entries.append(UserInfoEntry.sharedMedia)
|
||||
}
|
||||
entries.append(UserInfoEntry.notifications(settings: view.notificationSettings))
|
||||
if let groupsInCommon = (view.cachedData as? CachedUserData)?.commonGroupCount, !isEditing {
|
||||
entries.append(UserInfoEntry.groupsInCommon(groupsInCommon))
|
||||
}
|
||||
|
||||
if let _ = peer as? TelegramSecretChat {
|
||||
entries.append(UserInfoEntry.secretEncryptionKey(SecretChatKeyFingerprint(k0: 0, k1: 0, k2: 0, k3: 0)))
|
||||
@@ -486,6 +499,8 @@ public func userInfoController(account: Account, peerId: PeerId) -> ViewControll
|
||||
if let controller = peerSharedMediaController(account: account, peerId: peerId) {
|
||||
pushControllerImpl?(controller)
|
||||
}
|
||||
}, openGroupsInCommon: {
|
||||
pushControllerImpl?(groupsInCommonController(account: account, peerId: peerId))
|
||||
}, updatePeerBlocked: { value in
|
||||
updatePeerBlockedDisposable.set(requestUpdatePeerIsBlocked(account: account, peerId: peerId, isBlocked: value).start())
|
||||
}, deleteContact: {
|
||||
|
||||
Reference in New Issue
Block a user