Various improvements

This commit is contained in:
Isaac 2024-07-12 13:37:14 +04:00
parent 648c9807bf
commit 649b7e4ee6
14 changed files with 396 additions and 47 deletions

View File

@ -236,6 +236,59 @@ private func mergedResult(_ state: SearchMessagesState) -> SearchMessagesResult
return SearchMessagesResult(messages: messages, readStates: readStates, threadInfo: threadInfo, totalCount: state.main.totalCount + (state.additional?.totalCount ?? 0), completed: state.main.completed && (state.additional?.completed ?? true))
}
func _internal_getSearchMessageCount(account: Account, location: SearchMessagesLocation, query: String) -> Signal<Int?, NoError> {
guard case let .peer(peerId, fromId, _, _, threadId, _, _) = location else {
return .single(nil)
}
return account.postbox.transaction { transaction -> (Api.InputPeer?, Api.InputPeer?) in
var chatPeer = transaction.getPeer(peerId).flatMap(apiInputPeer)
var fromPeer: Api.InputPeer?
if let fromId {
if let value = transaction.getPeer(fromId).flatMap(apiInputPeer) {
fromPeer = value
} else {
chatPeer = nil
}
}
return (chatPeer, fromPeer)
}
|> mapToSignal { inputPeer, fromPeer -> Signal<Int?, NoError> in
guard let inputPeer else {
return .single(nil)
}
var flags: Int32 = 0
if let _ = fromPeer {
flags |= (1 << 0)
}
var topMsgId: Int32?
if let threadId = threadId {
flags |= (1 << 1)
topMsgId = Int32(clamping: threadId)
}
return account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromPeer, savedPeerId: nil, savedReaction: nil, topMsgId: topMsgId, filter: .inputMessagesFilterEmpty, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1, maxId: 0, minId: 0, hash: 0))
|> map { result -> Int? in
switch result {
case let .channelMessages(_, _, count, _, _, _, _, _):
return Int(count)
case let .messages(messages, _, _):
return messages.count
case let .messagesNotModified(count):
return Int(count)
case let .messagesSlice(_, count, _, _, _, _, _):
return Int(count)
}
}
|> `catch` { _ -> Signal<Int?, NoError> in
return .single(nil)
}
}
}
func _internal_searchMessages(account: Account, location: SearchMessagesLocation, query: String, state: SearchMessagesState?, centerId: MessageId?, limit: Int32 = 100) -> Signal<(SearchMessagesResult, SearchMessagesState), NoError> {
if case let .peer(peerId, fromId, tags, reactions, threadId, minDate, maxDate) = location, fromId == nil, tags == nil, peerId == account.peerId, let reactions, let reaction = reactions.first, (minDate == nil || minDate == 0), (maxDate == nil || maxDate == 0) {
return account.postbox.transaction { transaction -> (SearchMessagesResult, SearchMessagesState) in

View File

@ -76,6 +76,10 @@ public extension TelegramEngine {
return _internal_searchMessages(account: self.account, location: location, query: query, state: state, centerId: centerId, limit: limit)
}
public func getSearchMessageCount(location: SearchMessagesLocation, query: String) -> Signal<Int?, NoError> {
return _internal_getSearchMessageCount(account: self.account, location: location, query: query)
}
public func searchHashtagPosts(hashtag: String, state: SearchMessagesState?, limit: Int32 = 100) -> Signal<(SearchMessagesResult, SearchMessagesState), NoError> {
return _internal_searchHashtagPosts(account: self.account, hashtag: hashtag, state: state, limit: limit)
}

View File

@ -194,6 +194,7 @@ private final class AdminUserActionsSheetComponent: Component {
let chatPeer: EnginePeer
let peers: [RenderedChannelParticipant]
let messageCount: Int
let deleteAllMessageCount: Int?
let completion: (AdminUserActionsSheet.Result) -> Void
init(
@ -201,12 +202,14 @@ private final class AdminUserActionsSheetComponent: Component {
chatPeer: EnginePeer,
peers: [RenderedChannelParticipant],
messageCount: Int,
deleteAllMessageCount: Int?,
completion: @escaping (AdminUserActionsSheet.Result) -> Void
) {
self.context = context
self.chatPeer = chatPeer
self.peers = peers
self.messageCount = messageCount
self.deleteAllMessageCount = deleteAllMessageCount
self.completion = completion
}
@ -223,6 +226,9 @@ private final class AdminUserActionsSheetComponent: Component {
if lhs.messageCount != rhs.messageCount {
return false
}
if lhs.deleteAllMessageCount != rhs.deleteAllMessageCount {
return false
}
return true
}
@ -642,7 +648,7 @@ private final class AdminUserActionsSheetComponent: Component {
let sectionId: AnyHashable
let selectedPeers: Set<EnginePeer.Id>
let isExpanded: Bool
let title: String
var title: String
switch section {
case .report:
@ -870,7 +876,14 @@ private final class AdminUserActionsSheetComponent: Component {
)))
}
let titleString: String = environment.strings.Chat_AdminActionSheet_DeleteTitle(Int32(component.messageCount))
var titleString: String = environment.strings.Chat_AdminActionSheet_DeleteTitle(Int32(component.messageCount))
if let deleteAllMessageCount = component.deleteAllMessageCount {
if self.optionDeleteAllSelectedPeers == Set(component.peers.map(\.peer.id)) {
titleString = environment.strings.Chat_AdminActionSheet_DeleteTitle(Int32(deleteAllMessageCount))
}
}
let titleSize = self.title.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
@ -884,7 +897,9 @@ private final class AdminUserActionsSheetComponent: Component {
if titleView.superview == nil {
self.navigationBarContainer.addSubview(titleView)
}
transition.setFrame(view: titleView, frame: titleFrame)
//transition.setPosition(view: titleView, position: titleFrame.center)
titleView.center = titleFrame.center
titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
}
let navigationBackgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: 54.0))
@ -1424,10 +1439,10 @@ public class AdminUserActionsSheet: ViewControllerComponentContainer {
private var isDismissed: Bool = false
public init(context: AccountContext, chatPeer: EnginePeer, peers: [RenderedChannelParticipant], messageCount: Int, completion: @escaping (Result) -> Void) {
public init(context: AccountContext, chatPeer: EnginePeer, peers: [RenderedChannelParticipant], messageCount: Int, deleteAllMessageCount: Int?, completion: @escaping (Result) -> Void) {
self.context = context
super.init(context: context, component: AdminUserActionsSheetComponent(context: context, chatPeer: chatPeer, peers: peers, messageCount: messageCount, completion: completion), navigationBarAppearance: .none)
super.init(context: context, component: AdminUserActionsSheetComponent(context: context, chatPeer: chatPeer, peers: peers, messageCount: messageCount, deleteAllMessageCount: deleteAllMessageCount, completion: completion), navigationBarAppearance: .none)
self.statusBar.statusBarStyle = .Ignore
self.navigationPresentation = .flatModal

View File

@ -4747,25 +4747,31 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
}
if let forwardInfoNode = self.forwardInfoNode, forwardInfoNode.frame.contains(location) {
if let item = self.item, let forwardInfo = item.message.forwardInfo {
let performAction: () -> Void = {
let performAction: () -> Void = { [weak forwardInfoNode] in
if let sourceMessageId = forwardInfo.sourceMessageId {
if let channel = forwardInfo.author as? TelegramChannel, channel.addressName == nil {
if case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
} else if case .member = channel.participationStatus {
} else if !item.message.id.peerId.isReplies {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, false, forwardInfoNode, nil)
if let forwardInfoNode {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, false, forwardInfoNode, nil)
}
return
}
}
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId, NavigateToMessageParams(timestamp: nil, quote: nil))
if let forwardInfoNode {
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId, NavigateToMessageParams(timestamp: nil, quote: nil, progress: forwardInfoNode.makeActivate()?()))
}
} else if let peer = forwardInfo.source ?? forwardInfo.author {
item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info(nil) : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default)
} else if let _ = forwardInfo.authorSignature {
var subRect: CGRect?
if let textNode = forwardInfoNode.nameNode {
subRect = textNode.frame
if let forwardInfoNode {
var subRect: CGRect?
if let textNode = forwardInfoNode.nameNode {
subRect = textNode.frame
}
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, false, forwardInfoNode, subRect)
}
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, false, forwardInfoNode, subRect)
}
}

View File

@ -25,6 +25,7 @@ swift_library(
"//submodules/TelegramUI/Components/TextNodeWithEntities",
"//submodules/TelegramUI/Components/AnimationCache",
"//submodules/TelegramUI/Components/MultiAnimationRenderer",
"//submodules/TelegramUI/Components/TextLoadingEffect",
"//submodules/AvatarNode",
],
visibility = [

View File

@ -8,6 +8,8 @@ import TelegramPresentationData
import LocalizedPeerData
import AccountContext
import AvatarNode
import TextLoadingEffect
import SwiftSignalKit
public enum ChatMessageForwardInfoType: Equatable {
case bubble(incoming: Bool)
@ -85,6 +87,10 @@ public class ChatMessageForwardInfoNode: ASDisplayNode {
private var highlightColor: UIColor?
private var linkHighlightingNode: LinkHighlightingNode?
private var hasLinkProgress: Bool = false
private var linkProgressView: TextLoadingEffectView?
private var linkProgressDisposable: Disposable?
private var previousPeer: Peer?
public var openPsa: ((String, ASDisplayNode) -> Void)?
@ -93,6 +99,10 @@ public class ChatMessageForwardInfoNode: ASDisplayNode {
super.init()
}
deinit {
self.linkProgressDisposable?.dispose()
}
public func hasAction(at point: CGPoint) -> Bool {
if let infoNode = self.infoNode, infoNode.frame.contains(point) {
return true
@ -172,7 +182,6 @@ public class ChatMessageForwardInfoNode: ASDisplayNode {
if isHighlighted, !initialRects.isEmpty, let highlightColor = self.highlightColor {
let rects = initialRects
let linkHighlightingNode: LinkHighlightingNode
if let current = self.linkHighlightingNode {
linkHighlightingNode = current
@ -191,6 +200,85 @@ public class ChatMessageForwardInfoNode: ASDisplayNode {
}
}
public func makeActivate() -> (() -> Promise<Bool>?)? {
return { [weak self] in
guard let self else {
return nil
}
let promise = Promise<Bool>()
self.linkProgressDisposable?.dispose()
if self.hasLinkProgress {
self.hasLinkProgress = false
self.updateLinkProgressState()
}
self.linkProgressDisposable = (promise.get() |> deliverOnMainQueue).startStrict(next: { [weak self] value in
guard let self else {
return
}
if self.hasLinkProgress != value {
self.hasLinkProgress = value
self.updateLinkProgressState()
}
})
return promise
}
}
private func updateLinkProgressState() {
guard let highlightColor = self.highlightColor else {
return
}
if self.hasLinkProgress, let titleNode = self.titleNode, let nameNode = self.nameNode {
var initialRects: [CGRect] = []
let addRects: (TextNode, CGPoint, CGFloat) -> Void = { textNode, offset, additionalWidth in
guard let cachedLayout = textNode.cachedLayout else {
return
}
for rect in cachedLayout.linesRects() {
var rect = rect
rect.size.width += rect.origin.x + additionalWidth
rect.origin.x = 0.0
initialRects.append(rect.offsetBy(dx: offset.x, dy: offset.y))
}
}
let offsetY: CGFloat = -12.0
if let titleNode = self.titleNode {
addRects(titleNode, CGPoint(x: titleNode.frame.minX, y: offsetY + titleNode.frame.minY), 0.0)
if let nameNode = self.nameNode {
addRects(nameNode, CGPoint(x: titleNode.frame.minX, y: offsetY + nameNode.frame.minY), nameNode.frame.minX - titleNode.frame.minX)
}
}
let linkProgressView: TextLoadingEffectView
if let current = self.linkProgressView {
linkProgressView = current
} else {
linkProgressView = TextLoadingEffectView(frame: CGRect())
self.linkProgressView = linkProgressView
self.view.addSubview(linkProgressView)
}
linkProgressView.frame = titleNode.frame
let progressColor: UIColor = highlightColor
linkProgressView.update(color: progressColor, size: CGRectUnion(titleNode.frame, nameNode.frame).size, rects: initialRects)
} else {
if let linkProgressView = self.linkProgressView {
self.linkProgressView = nil
linkProgressView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak linkProgressView] _ in
linkProgressView?.removeFromSuperview()
})
}
}
}
public static func asyncLayout(_ maybeNode: ChatMessageForwardInfoNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ strings: PresentationStrings, _ type: ChatMessageForwardInfoType, _ peer: Peer?, _ authorName: String?, _ psaType: String?, _ storyData: StoryData?, _ constrainedSize: CGSize) -> (CGSize, (CGFloat) -> ChatMessageForwardInfoNode) {
let titleNodeLayout = TextNode.asyncLayout(maybeNode?.titleNode)
let nameNodeLayout = TextNode.asyncLayout(maybeNode?.nameNode)

View File

@ -370,6 +370,7 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode {
super.didLoad()
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
self.view.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(self.longPressGesture(_:))))
}
override public func animateIn() {
@ -491,9 +492,9 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode {
}
}
/*@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.interfaceInteraction?.navigateToMessage(self.messageId, false, true, .generic)
@objc func longPressGesture(_ recognizer: UILongPressGestureRecognizer) {
if case .began = recognizer.state {
self.interfaceInteraction?.navigateToMessage(self.messageId, false, true, ChatLoadingMessageSubject.generic)
}
}*/
}
}

View File

@ -868,7 +868,7 @@ final class PeerAllowedReactionsScreenComponent: Component {
}
contentHeight += reactionCountSectionSize.height
if "".isEmpty {
if !"".isEmpty {
contentHeight += 32.0
let paidReactionsSection: ComponentView<Empty>

View File

@ -171,4 +171,34 @@ public final class TextLoadingEffectView: UIView {
self.updateAnimations(size: maskFrame.size)
}
}
public func update(color: UIColor, size: CGSize, rects: [CGRect]) {
let rectsSet: [CGRect] = rects
let maskFrame = CGRect(origin: CGPoint(), size: size).insetBy(dx: -4.0, dy: -4.0)
self.maskContentsView.backgroundColor = color.withAlphaComponent(0.1)
self.maskBorderContentsView.backgroundColor = color.withAlphaComponent(0.12)
self.backgroundView.tintColor = color
self.borderBackgroundView.tintColor = color
self.maskContentsView.frame = maskFrame
self.maskBorderContentsView.frame = maskFrame
self.maskHighlightNode.updateRects(rectsSet)
self.maskHighlightNode.frame = CGRect(origin: CGPoint(x: -maskFrame.minX, y: -maskFrame.minY), size: CGSize())
self.maskBorderHighlightNode.updateRects(rectsSet)
self.maskBorderHighlightNode.frame = CGRect(origin: CGPoint(x: -maskFrame.minX, y: -maskFrame.minY), size: CGSize())
if self.size != maskFrame.size {
self.size = maskFrame.size
self.backgroundView.frame = CGRect(origin: CGPoint(x: -self.gradientWidth, y: 0.0), size: CGSize(width: self.gradientWidth, height: maskFrame.height))
self.borderBackgroundView.frame = CGRect(origin: CGPoint(x: -self.gradientWidth, y: 0.0), size: CGSize(width: self.gradientWidth, height: maskFrame.height))
self.updateAnimations(size: maskFrame.size)
}
}
}

View File

@ -29,7 +29,7 @@ extension ChatControllerImpl {
guard let self else {
return
}
self.navigateToMessage(from: fromId, to: .id(id, params), forceInCurrentChat: fromId.peerId == id.peerId && !params.forceNew, forceNew: params.forceNew)
self.navigateToMessage(from: fromId, to: .id(id, params), forceInCurrentChat: fromId.peerId == id.peerId && !params.forceNew, forceNew: params.forceNew, progress: params.progress)
}
let _ = (self.context.engine.data.get(
@ -77,6 +77,7 @@ extension ChatControllerImpl {
animated: Bool = true,
completion: (() -> Void)? = nil,
customPresentProgress: ((ViewController, Any?) -> Void)? = nil,
progress: Promise<Bool>? = nil,
statusSubject: ChatLoadingMessageSubject = .generic
) {
if !self.isNodeLoaded {
@ -160,31 +161,156 @@ extension ChatControllerImpl {
guard let self, let peer = peer else {
return
}
if let navigationController = self.effectiveNavigationController {
var chatLocation: NavigateToChatControllerParams.Location = .peer(peer)
var displayMessageNotFoundToast = false
if case let .channel(channel) = peer, channel.flags.contains(.isForum) {
if let message = message, let threadId = message.threadId {
chatLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false))
var quote: ChatControllerSubject.MessageHighlight.Quote?
if case let .id(_, params) = messageLocation {
quote = params.quote.flatMap { quote in ChatControllerSubject.MessageHighlight.Quote(string: quote.string, offset: quote.offset) }
}
var progressValue: Promise<Bool>?
if let value = progress {
progressValue = value
} else if case let .id(_, params) = messageLocation {
progressValue = params.progress
}
self.loadingMessage.set(.single(statusSubject) |> delay(0.1, queue: .mainQueue()))
var chatLocation: NavigateToChatControllerParams.Location = .peer(peer)
var preloadChatLocation: ChatLocation = .peer(id: peer.id)
var displayMessageNotFoundToast = false
if case let .channel(channel) = peer, channel.flags.contains(.isForum) {
if let message = message, let threadId = message.threadId {
let replyThreadMessage = ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)
chatLocation = .replyThread(replyThreadMessage)
preloadChatLocation = .replyThread(message: replyThreadMessage)
} else {
displayMessageNotFoundToast = true
}
}
let searchLocation: ChatHistoryInitialSearchLocation
switch messageLocation {
case let .id(id, _):
if case let .replyThread(message) = chatLocation, id == message.effectiveMessageId {
searchLocation = .index(.absoluteLowerBound())
} else {
searchLocation = .id(id)
}
case let .index(index):
searchLocation = .index(index)
case .upperBound:
searchLocation = .index(MessageIndex.upperBound(peerId: chatLocation.peerId))
}
var historyView: Signal<ChatHistoryViewUpdate, NoError>
let subject: ChatControllerSubject = .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: quote), timecode: nil)
historyView = preloadedChatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: searchLocation, quote: nil), count: 50, highlight: true), id: 0), context: self.context, chatLocation: preloadChatLocation, subject: subject, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>(value: nil), fixedCombinedReadStates: nil, tag: nil, additionalData: [])
var signal: Signal<(MessageIndex?, Bool), NoError>
signal = historyView
|> mapToSignal { historyView -> Signal<(MessageIndex?, Bool), NoError> in
switch historyView {
case .Loading:
return .single((nil, true))
case let .HistoryView(view, _, _, _, _, _, _):
for entry in view.entries {
if entry.message.id == messageLocation.messageId {
return .single((entry.message.index, false))
}
}
if case let .index(index) = searchLocation {
return .single((index, false))
}
return .single((nil, false))
}
}
|> take(until: { index in
return SignalTakeAction(passthrough: true, complete: !index.1)
})
/*#if DEBUG
signal = .single((nil, true)) |> then(signal |> delay(2.0, queue: .mainQueue()))
#endif*/
var cancelImpl: (() -> Void)?
let presentationData = self.presentationData
let displayTime = CACurrentMediaTime()
let progressSignal = Signal<Never, NoError> { [weak self] subscriber in
if let progressValue {
progressValue.set(.single(true))
return ActionDisposable {
Queue.mainQueue().async() {
progressValue.set(.single(false))
}
}
} else if case .generic = statusSubject {
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
if CACurrentMediaTime() - displayTime > 1.5 {
cancelImpl?()
}
}))
if let customPresentProgress = customPresentProgress {
customPresentProgress(controller, nil)
} else {
displayMessageNotFoundToast = true
self?.present(controller, in: .window(.root))
}
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
}
}
} else {
return EmptyDisposable
}
}
|> runOn(Queue.mainQueue())
|> delay(progressValue == nil ? 0.05 : 0.0, queue: Queue.mainQueue())
let progressDisposable = MetaDisposable()
var progressStarted = false
self.messageIndexDisposable.set((signal
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
}
}
|> deliverOnMainQueue).startStrict(next: { [weak self] index in
guard let self else {
return
}
var quote: ChatControllerSubject.MessageHighlight.Quote?
if case let .id(_, params) = messageLocation {
quote = params.quote.flatMap { quote in ChatControllerSubject.MessageHighlight.Quote(string: quote.string, offset: quote.offset) }
if let index = index.0 {
let _ = index
//strongSelf.chatDisplayNode.historyNode.scrollToMessage(from: scrollFromIndex, to: index, animated: animated, quote: quote, scrollPosition: scrollPosition)
} else if index.1 {
if !progressStarted {
progressStarted = true
progressDisposable.set(progressSignal.start())
}
return
}
let context = self.context
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: chatLocation, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: quote), timecode: nil, setupReply: false), keepStack: .always, chatListCompletion: { chatListController in
if displayMessageNotFoundToast {
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
chatListController.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.Conversation_MessageDoesntExist, timeout: nil, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in
return true
}), in: .current)
}
}))
if let navigationController = self.effectiveNavigationController {
let context = self.context
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: chatLocation, subject: subject, keepStack: .always, chatListCompletion: { chatListController in
if displayMessageNotFoundToast {
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
chatListController.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.Conversation_MessageDoesntExist, timeout: nil, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in
return true
}), in: .current)
}
}))
}
}, completed: { [weak self] in
if let strongSelf = self {
strongSelf.loadingMessage.set(.single(nil))
}
completion?()
}))
cancelImpl = { [weak self] in
if let strongSelf = self {
strongSelf.loadingMessage.set(.single(nil))
strongSelf.messageIndexDisposable.set(nil)
}
}
completion?()

View File

@ -125,6 +125,14 @@ extension ChatControllerImpl {
return
}
var deleteAllMessageCount: Signal<Int?, NoError> = .single(nil)
if authors.count == 1 {
deleteAllMessageCount = self.context.engine.messages.searchMessages(location: .peer(peerId: peerId, fromId: authors[0].id, tags: nil, reactions: nil, threadId: self.chatLocation.threadId, minDate: nil, maxDate: nil), query: "", state: nil)
|> map { result, _ -> Int? in
return Int(result.totalCount)
}
}
var signal = combineLatest(authors.map { author in
self.context.engine.peers.fetchChannelParticipant(peerId: peerId, participantId: author.id)
|> map { result -> (Peer, ChannelParticipant?) in
@ -161,8 +169,8 @@ extension ChatControllerImpl {
disposables.set(nil)
}
disposables.set((signal
|> deliverOnMainQueue).startStrict(next: { [weak self] authorsAndParticipants in
disposables.set((combineLatest(signal, deleteAllMessageCount)
|> deliverOnMainQueue).startStrict(next: { [weak self] authorsAndParticipants, deleteAllMessageCount in
guard let self else {
return
}
@ -212,6 +220,7 @@ extension ChatControllerImpl {
chatPeer: chatPeer,
peers: renderedParticipants,
messageCount: messageIds.count,
deleteAllMessageCount: deleteAllMessageCount,
completion: { [weak self] result in
guard let self else {
return
@ -259,8 +268,16 @@ extension ChatControllerImpl {
disposables.set(nil)
}
disposables.set((signal
|> deliverOnMainQueue).startStrict(next: { [weak self] maybeParticipant in
var deleteAllMessageCount: Signal<Int?, NoError> = .single(nil)
do {
deleteAllMessageCount = self.context.engine.messages.getSearchMessageCount(location: .peer(peerId: peerId, fromId: author.id, tags: nil, reactions: nil, threadId: self.chatLocation.threadId, minDate: nil, maxDate: nil), query: "")
|> map { result -> Int? in
return result
}
}
disposables.set((combineLatest(signal, deleteAllMessageCount)
|> deliverOnMainQueue).startStrict(next: { [weak self] maybeParticipant, deleteAllMessageCount in
guard let self else {
return
}
@ -310,6 +327,7 @@ extension ChatControllerImpl {
peer: authorPeer._asPeer()
)],
messageCount: messageIds.count,
deleteAllMessageCount: deleteAllMessageCount,
completion: { [weak self] result in
guard let self else {
return

View File

@ -257,10 +257,10 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
if let value = URL(string: "ipfs:/" + parsedUrl.path) {
parsedUrl = value
}
}
} else if let scheme = parsedUrl.scheme, scheme == "https", parsedUrl.host == "t.me", parsedUrl.path.hasPrefix("/ipfs/") {
if let value = URL(string: "ipfs://" + String(parsedUrl.path[parsedUrl.path.index(parsedUrl.path.startIndex, offsetBy: "/ipfs/".count)...])) {
parsedUrl = value
} else if parsedUrl.host == "ton" {
if let value = URL(string: "ton:/" + parsedUrl.path) {
parsedUrl = value
}
}
}
}
@ -1009,7 +1009,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
isInternetUrl = true
}
if context.sharedContext.immediateExperimentalUISettings.browserExperiment {
if parsedUrl.scheme == "ipfs" || parsedUrl.scheme == "ipns" {
if parsedUrl.scheme == "ipfs" || parsedUrl.scheme == "ipns" || parsedUrl.scheme == "ton" {
isInternetUrl = true
}
}

View File

@ -251,6 +251,10 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
}
if displayImage {
if captureProtected {
setLayerDisableScreenshots(self.imageNode.layer, captureProtected)
}
self.imageNode.setSignal(internalMediaGridMessageVideo(postbox: postbox, userLocation: userLocation, videoReference: fileReference, imageReference: imageReference, onlyFullSize: onlyFullSizeThumbnail, useLargeThumbnail: useLargeThumbnail, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail || fileReference.media.isInstantVideo) |> map { [weak self] getSize, getData in
Queue.mainQueue().async {
if let strongSelf = self, strongSelf.dimensions == nil {

View File

@ -134,6 +134,9 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, query: String)
if query.hasPrefix("ipfs/") {
return .externalUrl(url: "ipfs://" + String(query[query.index(query.startIndex, offsetBy: "ipfs/".count)...]))
}
if query.hasPrefix("ton/") {
return .externalUrl(url: "ton://" + String(query[query.index(query.startIndex, offsetBy: "ton/".count)...]))
}
}
if pathComponents[0].hasPrefix("+") || pathComponents[0].hasPrefix("%20") {