Cherry-pick various fixes

This commit is contained in:
Ilya Laktyushin 2024-04-15 16:24:29 +04:00
parent 5dc97b4e3d
commit 669a8719bf
31 changed files with 364 additions and 129 deletions

View File

@ -12050,3 +12050,5 @@ Sorry for the inconvenience.";
"MediaEditor.NewStickerPack.Title" = "New Sticker Set";
"MediaEditor.NewStickerPack.Text" = "Choose a name for your sticker set.";
"Premium.Gift.ContactSelection.SendMessage" = "Send Message";
"Premium.Gift.ContactSelection.OpenProfile" = "Open Profile";

View File

@ -637,6 +637,7 @@ public enum ContactListAction: Equatable {
case generic
case voiceCall
case videoCall
case more
}
public enum ContactListPeer: Equatable {

View File

@ -102,8 +102,10 @@ public final class ContactMultiselectionControllerParams {
public let alwaysEnabled: Bool
public let limit: Int32?
public let reachedLimit: ((Int32) -> Void)?
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: ContactMultiselectionControllerMode, options: [ContactListAdditionalOption], filters: [ContactListFilter] = [.excludeSelf], onlyWriteable: Bool = false, isGroupInvitation: Bool = false, isPeerEnabled: ((EnginePeer) -> Bool)? = nil, attemptDisabledItemSelection: ((EnginePeer, ChatListDisabledPeerReason) -> Void)? = nil, alwaysEnabled: Bool = false, limit: Int32? = nil, reachedLimit: ((Int32) -> Void)? = nil) {
public let openProfile: ((EnginePeer) -> Void)?
public let sendMessage: ((EnginePeer) -> Void)?
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: ContactMultiselectionControllerMode, options: [ContactListAdditionalOption], filters: [ContactListFilter] = [.excludeSelf], onlyWriteable: Bool = false, isGroupInvitation: Bool = false, isPeerEnabled: ((EnginePeer) -> Bool)? = nil, attemptDisabledItemSelection: ((EnginePeer, ChatListDisabledPeerReason) -> Void)? = nil, alwaysEnabled: Bool = false, limit: Int32? = nil, reachedLimit: ((Int32) -> Void)? = nil, openProfile: ((EnginePeer) -> Void)? = nil, sendMessage: ((EnginePeer) -> Void)? = nil) {
self.context = context
self.updatedPresentationData = updatedPresentationData
self.mode = mode
@ -116,6 +118,8 @@ public final class ContactMultiselectionControllerParams {
self.alwaysEnabled = alwaysEnabled
self.limit = limit
self.reachedLimit = reachedLimit
self.openProfile = openProfile
self.sendMessage = sendMessage
}
}

View File

@ -45,6 +45,7 @@ swift_library(
"//submodules/TooltipUI",
"//submodules/UndoUI",
"//submodules/TelegramIntents",
"//submodules/ContextUI",
],
visibility = [
"//visibility:public",

View File

@ -23,6 +23,7 @@ import AppBundle
import ContextUI
import PhoneNumberFormat
import LocalizedPeerData
import ContextUI
private let dropDownIcon = { () -> UIImage in
UIGraphicsBeginImageContextWithOptions(CGSize(width: 12.0, height: 12.0), false, 0.0)
@ -56,7 +57,7 @@ private final class ContactListNodeInteraction {
fileprivate let activateSearch: () -> Void
fileprivate let authorize: () -> Void
fileprivate let suppressWarning: () -> Void
fileprivate let openPeer: (ContactListPeer, ContactListAction) -> Void
fileprivate let openPeer: (ContactListPeer, ContactListAction, ASDisplayNode?, ContextGesture?) -> Void
fileprivate let openDisabledPeer: (EnginePeer, ChatListDisabledPeerReason) -> Void
fileprivate let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?, Bool) -> Void)?
fileprivate let openStories: (EnginePeer, ASDisplayNode) -> Void
@ -65,7 +66,7 @@ private final class ContactListNodeInteraction {
let itemHighlighting = ContactItemHighlighting()
init(activateSearch: @escaping () -> Void, authorize: @escaping () -> Void, suppressWarning: @escaping () -> Void, openPeer: @escaping (ContactListPeer, ContactListAction) -> Void, openDisabledPeer: @escaping (EnginePeer, ChatListDisabledPeerReason) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?, Bool) -> Void)?, openStories: @escaping (EnginePeer, ASDisplayNode) -> Void, deselectAll: @escaping () -> Void, toggleSelection: @escaping ([EnginePeer], Bool) -> Void) {
init(activateSearch: @escaping () -> Void, authorize: @escaping () -> Void, suppressWarning: @escaping () -> Void, openPeer: @escaping (ContactListPeer, ContactListAction, ASDisplayNode?, ContextGesture?) -> Void, openDisabledPeer: @escaping (EnginePeer, ChatListDisabledPeerReason) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?, Bool) -> Void)?, openStories: @escaping (EnginePeer, ASDisplayNode) -> Void, deselectAll: @escaping () -> Void, toggleSelection: @escaping ([EnginePeer], Bool) -> Void) {
self.activateSearch = activateSearch
self.authorize = authorize
self.suppressWarning = suppressWarning
@ -96,7 +97,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
case permissionInfo(PresentationTheme, String, String, Bool)
case permissionEnable(PresentationTheme, String)
case option(Int, ContactListAdditionalOption, ListViewItemHeader?, PresentationTheme, PresentationStrings)
case peer(Int, ContactListPeer, EnginePeer.Presence?, ListViewItemHeader?, ContactsPeerItemSelection, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PresentationPersonNameOrder, Bool, Bool, StoryData?, Bool)
case peer(Int, ContactListPeer, EnginePeer.Presence?, ListViewItemHeader?, ContactsPeerItemSelection, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PresentationPersonNameOrder, Bool, Bool, Bool, StoryData?, Bool)
var stableId: ContactListNodeEntryId {
switch self {
@ -110,7 +111,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
return .permission(action: true)
case let .option(index, _, _, _, _):
return .option(index: index)
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, storyData, _):
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, storyData, _):
switch peer {
case let .peer(peer, _, _):
return .peerId(peerId: peer.id.toInt64(), section: storyData != nil ? .stories : .contacts)
@ -143,7 +144,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
})
case let .option(_, option, header, _, _):
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, icon: option.icon, clearHighlightAutomatically: option.clearHighlightAutomatically, header: header, action: option.action)
case let .peer(_, peer, presence, header, selection, _, strings, dateTimeFormat, nameSortOrder, nameDisplayOrder, displayCallIcons, enabled, storyData, requiresPremiumForMessaging):
case let .peer(_, peer, presence, header, selection, _, strings, dateTimeFormat, nameSortOrder, nameDisplayOrder, displayCallIcons, hasMoreButton, enabled, storyData, requiresPremiumForMessaging):
var status: ContactsPeerItemStatus
let itemPeer: ContactsPeerItemPeer
var isContextActionEnabled = false
@ -200,11 +201,15 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
}
var additionalActions: [ContactsPeerItemAction] = []
if displayCallIcons {
additionalActions = [ContactsPeerItemAction(icon: .voiceCall, action: { _ in
interaction.openPeer(peer, .voiceCall)
}), ContactsPeerItemAction(icon: .videoCall, action: { _ in
interaction.openPeer(peer, .videoCall)
if hasMoreButton {
additionalActions = [ContactsPeerItemAction(icon: .more, action: { _, sourceNode, gesture in
interaction.openPeer(peer, .more, sourceNode, gesture)
})]
} else if displayCallIcons {
additionalActions = [ContactsPeerItemAction(icon: .voiceCall, action: { _, sourceNode, gesture in
interaction.openPeer(peer, .voiceCall, sourceNode, gesture)
}), ContactsPeerItemAction(icon: .videoCall, action: { _, sourceNode, gesture in
interaction.openPeer(peer, .videoCall, sourceNode, gesture)
})]
}
@ -218,7 +223,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
}
return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: isSearch ? .generalSearch(isSavedMessages: false) : .peer, peer: itemPeer, status: status, requiresPremiumForMessaging: requiresPremiumForMessaging, enabled: enabled, selection: selection, selectionPosition: .left, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), additionalActions: additionalActions, index: nil, header: header, action: { _ in
interaction.openPeer(peer, .generic)
interaction.openPeer(peer, .generic, nil, nil)
}, disabledAction: { _ in
if case let .peer(peer, _, _) = peer {
interaction.openDisabledPeer(EnginePeer(peer), requiresPremiumForMessaging ? .premiumRequired : .generic)
@ -263,9 +268,9 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
} else {
return false
}
case let .peer(lhsIndex, lhsPeer, lhsPresence, lhsHeader, lhsSelection, lhsTheme, lhsStrings, lhsTimeFormat, lhsSortOrder, lhsDisplayOrder, lhsDisplayCallIcons, lhsEnabled, lhsStoryData, lhsRequiresPremiumForMessaging):
case let .peer(lhsIndex, lhsPeer, lhsPresence, lhsHeader, lhsSelection, lhsTheme, lhsStrings, lhsTimeFormat, lhsSortOrder, lhsDisplayOrder, lhsDisplayCallIcons, lhsHasMoreButton, lhsEnabled, lhsStoryData, lhsRequiresPremiumForMessaging):
switch rhs {
case let .peer(rhsIndex, rhsPeer, rhsPresence, rhsHeader, rhsSelection, rhsTheme, rhsStrings, rhsTimeFormat, rhsSortOrder, rhsDisplayOrder, rhsDisplayCallIcons, rhsEnabled, rhsStoryData, rhsRequiresPremiumForMessaging):
case let .peer(rhsIndex, rhsPeer, rhsPresence, rhsHeader, rhsSelection, rhsTheme, rhsStrings, rhsTimeFormat, rhsSortOrder, rhsDisplayOrder, rhsDisplayCallIcons, rhsHasMoreButton, rhsEnabled, rhsStoryData, rhsRequiresPremiumForMessaging):
if lhsIndex != rhsIndex {
return false
}
@ -303,6 +308,9 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
if lhsDisplayCallIcons != rhsDisplayCallIcons {
return false
}
if lhsHasMoreButton != rhsHasMoreButton {
return false
}
if lhsEnabled != rhsEnabled {
return false
}
@ -353,11 +361,11 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
case .peer:
return true
}
case let .peer(lhsIndex, _, _, _, _, _, _, _, _, _, _, _, lhsStoryData, _):
case let .peer(lhsIndex, _, _, _, _, _, _, _, _, _, _, _, _, lhsStoryData, _):
switch rhs {
case .search, .sort, .permissionInfo, .permissionEnable, .option:
return false
case let .peer(rhsIndex, _, _, _, _, _, _, _, _, _, _, _, rhsStoryData, _):
case let .peer(rhsIndex, _, _, _, _, _, _, _, _, _, _, _, _, rhsStoryData, _):
if (lhsStoryData == nil) != (rhsStoryData == nil) {
if lhsStoryData != nil {
return true
@ -551,7 +559,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis
}
let presence = presences[peer.id]
entries.append(.peer(index, .peer(peer: peer._asPeer(), isGlobal: false, participantCount: nil), presence, header, selection, theme, strings, dateTimeFormat, sortOrder, displayOrder, false, true, nil, false))
entries.append(.peer(index, .peer(peer: peer._asPeer(), isGlobal: false, participantCount: nil), presence, header, selection, theme, strings, dateTimeFormat, sortOrder, displayOrder, false, false, true, nil, false))
index += 1
}
@ -608,7 +616,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis
}
let presence = presences[peer.id]
entries.append(.peer(index, .peer(peer: peer._asPeer(), isGlobal: false, participantCount: nil), presence, header, selection, theme, strings, dateTimeFormat, sortOrder, displayOrder, false, true, nil, false))
entries.append(.peer(index, .peer(peer: peer._asPeer(), isGlobal: false, participantCount: nil), presence, header, selection, theme, strings, dateTimeFormat, sortOrder, displayOrder, false, true, true, nil, false))
index += 1
}
@ -657,7 +665,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis
}
let presence = presences[peer.id]
entries.append(.peer(index, .peer(peer: peer._asPeer(), isGlobal: false, participantCount: nil), presence, header, selection, theme, strings, dateTimeFormat, sortOrder, displayOrder, false, true, nil, false))
entries.append(.peer(index, .peer(peer: peer._asPeer(), isGlobal: false, participantCount: nil), presence, header, selection, theme, strings, dateTimeFormat, sortOrder, displayOrder, false, false, true, nil, false))
index += 1
}
@ -701,7 +709,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis
enabled = true
}
entries.append(.peer(index, peer, presence, nil, selection, theme, strings, dateTimeFormat, sortOrder, displayOrder, displayCallIcons, enabled, nil, false))
entries.append(.peer(index, peer, presence, nil, selection, theme, strings, dateTimeFormat, sortOrder, displayOrder, displayCallIcons, false, enabled, nil, false))
index += 1
}
}
@ -748,7 +756,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis
}
entries.append(.peer(index, peer, presence, header, selection, theme, strings, dateTimeFormat, sortOrder, displayOrder, displayCallIcons, enabled, nil, requiresPremiumForMessaging))
entries.append(.peer(index, peer, presence, header, selection, theme, strings, dateTimeFormat, sortOrder, displayOrder, displayCallIcons, false, enabled, nil, requiresPremiumForMessaging))
index += 1
}
return entries
@ -772,7 +780,7 @@ private func preparedContactListNodeTransition(context: AccountContext, presenta
case .search:
//indexSections.apend(CollectionIndexNode.searchIndex)
break
case let .peer(_, _, _, header, _, _, _, _, _, _, _, _, _, _):
case let .peer(_, _, _, header, _, _, _, _, _, _, _, _, _, _, _):
if let header = header as? ContactListNameIndexHeader {
if !existingSections.contains(header.letter) {
existingSections.insert(header.letter)
@ -1036,7 +1044,7 @@ public final class ContactListNode: ASDisplayNode {
public var contentScrollingEnded: ((ListView) -> Bool)?
public var activateSearch: (() -> Void)?
public var openPeer: ((ContactListPeer, ContactListAction) -> Void)?
public var openPeer: ((ContactListPeer, ContactListAction, ASDisplayNode?, ContextGesture?) -> Void)?
public var openDisabledPeer: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?
public var deselectedAll: (() -> Void)?
public var updatedSelection: (([EnginePeer], Bool) -> Void)?
@ -1136,7 +1144,7 @@ public final class ContactListNode: ASDisplayNode {
authorizeImpl?()
}, suppressWarning: { [weak self] in
self?.suppressPermissionWarning?()
}, openPeer: { [weak self] peer, action in
}, openPeer: { [weak self] peer, action, sourceNode, gesture in
if let strongSelf = self {
if strongSelf.multipleSelection {
var updated = false
@ -1151,10 +1159,10 @@ public final class ContactListNode: ASDisplayNode {
}
})
if !updated {
strongSelf.openPeer?(peer, action)
strongSelf.openPeer?(peer, action, sourceNode, gesture)
}
} else {
strongSelf.openPeer?(peer, action)
strongSelf.openPeer?(peer, action, sourceNode, gesture)
}
}
}, openDisabledPeer: { [weak self] peer, reason in
@ -1224,7 +1232,7 @@ public final class ContactListNode: ASDisplayNode {
strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.PreferSynchronousDrawing, .PreferSynchronousResourceLoading], scrollToItem: ListViewScrollToItem(index: index, position: .top(-navigationBarSearchContentHeight), animated: false, curve: .Default(duration: nil), directionHint: .Down), additionalScrollDistance: 0.0, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
break loop
}
case let .peer(_, _, _, header, _, _, _, _, _, _, _, _, _, _):
case let .peer(_, _, _, header, _, _, _, _, _, _, _, _, _, _, _):
if let header = header as? ContactListNameIndexHeader {
if let scalar = UnicodeScalar(header.letter) {
let title = "\(Character(scalar))"

View File

@ -345,7 +345,7 @@ public class ContactsController: ViewController {
self?.activateSearch()
}
self.contactsNode.contactListNode.openPeer = { [weak self] peer, _ in
self.contactsNode.contactListNode.openPeer = { [weak self] peer, _, _, _ in
guard let self else {
return
}

View File

@ -31,6 +31,7 @@ swift_library(
"//submodules/TelegramUI/Components/MultiAnimationRenderer",
"//submodules/TelegramUI/Components/EmojiStatusComponent",
"//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent",
"//submodules/MoreButtonNode",
],
visibility = [
"//visibility:public",

View File

@ -20,6 +20,7 @@ import ComponentFlow
import AnimationCache
import MultiAnimationRenderer
import EmojiStatusComponent
import MoreButtonNode
public final class ContactItemHighlighting {
public var chatLocation: ChatLocation?
@ -88,13 +89,14 @@ public enum ContactsPeerItemActionIcon {
case add
case voiceCall
case videoCall
case more
}
public struct ContactsPeerItemAction {
public let icon: ContactsPeerItemActionIcon
public let action: ((ContactsPeerItemPeer) -> Void)?
public let action: ((ContactsPeerItemPeer, ASDisplayNode, ContextGesture?) -> Void)?
public init(icon: ContactsPeerItemActionIcon, action: @escaping (ContactsPeerItemPeer) -> Void) {
public init(icon: ContactsPeerItemActionIcon, action: @escaping (ContactsPeerItemPeer, ASDisplayNode, ContextGesture?) -> Void) {
self.icon = icon
self.action = action
}
@ -417,6 +419,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
private var badgeTextNode: TextNode?
private var selectionNode: CheckNode?
private var actionButtonNodes: [HighlightableButtonNode]?
private var moreButtonNode: MoreButtonNode?
private var arrowButtonNode: HighlightableButtonNode?
private var avatarTapRecognizer: UITapGestureRecognizer?
@ -744,10 +747,11 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
var actionButtons: [ActionButton]?
struct ActionButton {
let type: ContactsPeerItemActionIcon
let image: UIImage?
let action: ((ContactsPeerItemPeer) -> Void)?
let action: ((ContactsPeerItemPeer, ASDisplayNode, ContextGesture?) -> Void)?
init(theme: PresentationTheme, icon: ContactsPeerItemActionIcon, action: ((ContactsPeerItemPeer) -> Void)?) {
init(theme: PresentationTheme, icon: ContactsPeerItemActionIcon, action: ((ContactsPeerItemPeer, ASDisplayNode, ContextGesture?) -> Void)?) {
let image: UIImage?
switch icon {
case .none:
@ -758,7 +762,10 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
image = PresentationResourcesItemList.voiceCallIcon(theme)
case .videoCall:
image = PresentationResourcesItemList.videoCallIcon(theme)
case .more:
image = PresentationResourcesItemList.videoCallIcon(theme)
}
self.type = icon
self.image = image
self.action = action
}
@ -1357,7 +1364,23 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
verifiedIconView.removeFromSuperview()
}
if let actionButtons = actionButtons {
if let actionButtons, actionButtons.count == 1, let actionButton = actionButtons.first, case .more = actionButton.type {
let moreButtonNode: MoreButtonNode
if let current = strongSelf.moreButtonNode {
moreButtonNode = current
} else {
moreButtonNode = MoreButtonNode(theme: item.presentationData.theme)
moreButtonNode.iconNode.enqueueState(.more, animated: false)
moreButtonNode.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0)
strongSelf.offsetContainerNode.addSubnode(moreButtonNode)
strongSelf.moreButtonNode = moreButtonNode
}
moreButtonNode.action = { sourceNode, gesture in
actionButton.action?(item.peer, sourceNode, gesture)
}
let moreButtonSize = moreButtonNode.measure(CGSize(width: 100.0, height: nodeLayout.contentSize.height))
moreButtonNode.frame = CGRect(origin: CGPoint(x: revealOffset + params.width - params.rightInset - 18.0 - moreButtonSize.width, y:floor((nodeLayout.contentSize.height - moreButtonSize.height) / 2.0)), size: moreButtonSize)
} else if let actionButtons = actionButtons {
if strongSelf.actionButtonNodes == nil {
var actionButtonNodes: [HighlightableButtonNode] = []
for action in actionButtons {
@ -1524,7 +1547,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
guard let actionButtonNodes = self.actionButtonNodes, let index = actionButtonNodes.firstIndex(of: sender), let item = self.item, index < item.additionalActions.count else {
return
}
item.additionalActions[index].action?(item.peer)
item.additionalActions[index].action?(item.peer, sender, nil)
}
override public func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {

View File

@ -1599,7 +1599,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
videoNode.setBaseRate(self.playbackRate ?? 1.0)
}
} else {
if self.shouldAutoplayOnCentrality() {
if isAnimated {
self.playOnContentOwnership = true
} else if self.shouldAutoplayOnCentrality() {
self.playOnContentOwnership = true
}
}

View File

@ -5,7 +5,7 @@ import Display
private func generateHistogram(cgImage: CGImage) -> ([[vImagePixelCount]], Int)? {
var sourceBuffer = vImage_Buffer()
defer {
free(sourceBuffer.data)
sourceBuffer.data?.deallocate()
}
var cgImageFormat = vImage_CGImageFormat(

View File

@ -5,6 +5,7 @@ import Postbox
import SwiftSignalKit
import ImageCompression
import Accelerate.vImage
import CoreImage
private final class RequestId {
var id: PHImageRequestID?
@ -15,24 +16,43 @@ private func resizedImage(_ image: UIImage, for size: CGSize) -> UIImage? {
guard let cgImage = image.cgImage else {
return nil
}
if #available(iOS 14.1, *) {
if cgImage.bitsPerComponent == 10, let ciImage = CIImage(image: image, options: [.applyOrientationProperty: true, .toneMapHDRtoSDR: true]) {
let scaleX = size.width / ciImage.extent.width
let filter = CIFilter(name: "CILanczosScaleTransform")!
filter.setValue(ciImage, forKey: kCIInputImageKey)
filter.setValue(scaleX, forKey: kCIInputScaleKey)
filter.setValue(1.0, forKey: kCIInputAspectRatioKey)
guard let outputImage = filter.outputImage else { return nil }
let ciContext = CIContext()
guard let cgImage = ciContext.createCGImage(outputImage, from: outputImage.extent) else { return nil }
return UIImage(cgImage: cgImage)
}
}
var format = vImage_CGImageFormat(bitsPerComponent: 8,
bitsPerPixel: 32,
colorSpace: nil,
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.first.rawValue),
version: 0,
decode: nil,
renderingIntent: .defaultIntent)
renderingIntent: cgImage.renderingIntent)
var error: vImage_Error
var sourceBuffer = vImage_Buffer()
defer { sourceBuffer.data.deallocate() }
defer { sourceBuffer.data?.deallocate() }
error = vImageBuffer_InitWithCGImage(&sourceBuffer,
&format,
nil,
cgImage,
vImage_Flags(kvImageNoFlags))
guard error == kvImageNoError else { return nil }
guard error == kvImageNoError else {
return nil
}
var destinationBuffer = vImage_Buffer()
error = vImageBuffer_Init(&destinationBuffer,
@ -84,7 +104,7 @@ extension UIImage.Orientation {
private let fetchPhotoWorkers = ThreadPool(threadCount: 3, threadPriority: 0.2)
public func fetchPhotoLibraryResource(localIdentifier: String, width: Int32?, height: Int32?, format: MediaImageFormat?, quality: Int32?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
public func fetchPhotoLibraryResource(localIdentifier: String, width: Int32?, height: Int32?, format: MediaImageFormat?, quality: Int32?, useExif: Bool) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
return Signal { subscriber in
let queue = ThreadPoolQueue(threadPool: fetchPhotoWorkers)
@ -96,7 +116,7 @@ public func fetchPhotoLibraryResource(localIdentifier: String, width: Int32?, he
option.deliveryMode = .highQualityFormat
option.isNetworkAccessAllowed = true
option.isSynchronous = false
let size: CGSize
if let width, let height {
size = CGSize(width: CGFloat(width), height: CGFloat(height))
@ -104,11 +124,31 @@ public func fetchPhotoLibraryResource(localIdentifier: String, width: Int32?, he
size = CGSize(width: 1280.0, height: 1280.0)
}
var targetSize = PHImageManagerMaximumSize
//TODO: figure out how to manually read and resize some weird 10-bit heif photos from third-party cameras
if useExif, min(asset.pixelWidth, asset.pixelHeight) > 3800 {
func encodeText(string: String, key: Int16) -> String {
let nsString = string as NSString
let result = NSMutableString()
for i in 0 ..< nsString.length {
var c: unichar = nsString.character(at: i)
c = unichar(Int16(c) + key)
result.append(NSString(characters: &c, length: 1) as String)
}
return result as String
}
if let values = asset.value(forKeyPath: encodeText(string: "jnbhfQspqfsujft", key: -1)) as? [String: Any] {
if let depth = values["Depth"] as? Int, depth == 10 {
targetSize = size
}
}
}
queue.addTask(ThreadPoolTask({ _ in
let startTime = CACurrentMediaTime()
let semaphore = DispatchSemaphore(value: 0)
let requestIdValue = PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: option, resultHandler: { (image, info) -> Void in
let requestIdValue = PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFit, options: option, resultHandler: { (image, info) -> Void in
Queue.concurrentDefaultQueue().async {
requestId.with { current -> Void in
if !current.invalidated {

View File

@ -1592,11 +1592,14 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
if case let .noAccess(cameraAccess) = self.state {
var hasCamera = cameraAccess == .authorized
var story = false
if let subject = self.controller?.subject, case .assets(_, .story) = subject {
hasCamera = false
story = true
self.controller?.navigationItem.rightBarButtonItem = nil
if let subject = self.controller?.subject {
if case .assets(_, .story) = subject {
hasCamera = false
story = true
self.controller?.navigationItem.rightBarButtonItem = nil
} else if case .assets(_, .createSticker) = subject {
hasCamera = false
}
}
var placeholderTransition = transition

View File

@ -35,15 +35,11 @@ public enum PreparedShareItems {
}
private func scalePhotoImage(_ image: UIImage, dimensions: CGSize) -> UIImage? {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
let format = UIGraphicsImageRendererFormat()
format.scale = 1.0
let renderer = UIGraphicsImageRenderer(size: dimensions, format: format)
return renderer.image { _ in
image.draw(in: CGRect(origin: .zero, size: dimensions))
}
} else {
return TGScaleImageToPixelSize(image, dimensions)
let format = UIGraphicsImageRendererFormat()
format.scale = 1.0
let renderer = UIGraphicsImageRenderer(size: dimensions, format: format)
return renderer.image { _ in
image.draw(in: CGRect(origin: .zero, size: dimensions))
}
}
@ -234,7 +230,7 @@ private func preparedShareItem(postbox: Postbox, network: Network, to peerId: Pe
}
)
} else {
let scaledImage = TGScaleImageToPixelSize(image, CGSize(width: image.size.width * image.scale, height: image.size.height * image.scale).fitted(CGSize(width: 1280.0, height: 1280.0)))!
let scaledImage = scalePhotoImage(image, dimensions: CGSize(width: image.size.width * image.scale, height: image.size.height * image.scale).fitted(CGSize(width: 1280.0, height: 1280.0)))!
let imageData = scaledImage.jpegData(compressionQuality: 0.54)!
return .single(.preparing(false))
|> then(

View File

@ -166,7 +166,7 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode {
}
let textColor = strongSelf.theme.chat.inputPanel.primaryTextColor
if entities.count > 0 {
messageText = stringWithAppliedEntities(trimToLineCount(message.text, lineCount: 1), entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message)
messageText = stringWithAppliedEntities(trimToLineCount(message.text, lineCount: 1), entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message)
} else {
messageText = NSAttributedString(string: text, font: textFont, textColor: isMedia ? strongSelf.theme.chat.inputPanel.secondaryTextColor : strongSelf.theme.chat.inputPanel.primaryTextColor)
}

View File

@ -112,6 +112,10 @@ private final class MediaCutoutScreenComponent: Component {
x: location.x / controller.drawingView.bounds.width,
y: location.y / controller.drawingView.bounds.height
)
let validRange: Range<CGFloat> = 0.0 ..< 1.0
guard validRange.contains(point.x) && validRange.contains(point.y) else {
return
}
component.mediaEditor.processImage { [weak self] originalImage, _ in
cutoutImage(from: originalImage, values: nil, target: .point(point), includeExtracted: false, completion: { [weak self] results in

View File

@ -1177,8 +1177,10 @@ final class MediaEditorScreenComponent: Component {
let authorName = forwardAuthor.displayTitle(strings: environment.strings, displayOrder: .firstLast)
header = AnyComponent(
ForwardInfoPanelComponent(
context: component.context,
authorName: authorName,
text: forwardStory.text,
entities: forwardStory.entities,
isChannel: forwardAuthor.id.isGroupOrChannel,
isVibrant: true,
fillsWidth: true

View File

@ -228,20 +228,48 @@ func findEdgePoints(in pixelBuffer: CVPixelBuffer) -> [CGPoint] {
return pixel >= 235
}
let directions = [(1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1)]
var lastDirectionIndex = 0
var startPoint: Point? = nil
outerLoop: for y in 0..<height {
var visited = Set<Point>()
var componentSize = 0
func floodFill(from point: Point) -> Int {
var stack = [point]
var size = 0
while let current = stack.popLast() {
let x = Int(current.x)
let y = Int(current.y)
if x < 0 || x >= width || y < 0 || y >= height || visited.contains(current) || !isPixelWhiteAt(x: x, y: y) {
continue
}
visited.insert(current)
size += 1
stack.append(contentsOf: [Point(x: x+1, y: y), Point(x: x-1, y: y), Point(x: x, y: y+1), Point(x: x, y: y-1)])
}
return size
}
for y in 0..<height {
for x in 0..<width {
if isPixelWhiteAt(x: x, y: y) {
startPoint = Point(x: x, y: y)
break outerLoop
let point = Point(x: x, y: y)
if isPixelWhiteAt(x: x, y: y) && !visited.contains(point) {
let size = floodFill(from: point)
if size > componentSize {
componentSize = size
startPoint = point
}
}
}
}
guard let startingPoint = startPoint else { return [] }
let directions = [(1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1)]
var lastDirectionIndex = 0
guard let startingPoint = startPoint, componentSize > 60 else { return [] }
edgePoints.insert(startingPoint)
edgePath.append(startingPoint)
var currentPoint = startingPoint
@ -326,7 +354,7 @@ private func getEdgesBitmap(_ ciImage: CIImage) -> CVPixelBuffer? {
context.fill(CGRect(origin: .zero, size: size))
image.draw(in: CGRect(origin: .zero, size: size))
UIGraphicsPopContext()
return buffer
}

View File

@ -1365,7 +1365,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
contactListNode.activateSearch = { [weak self] in
self?.requestActivateSearch?()
}
contactListNode.openPeer = { [weak self] peer, _ in
contactListNode.openPeer = { [weak self] peer, _, _, _ in
if case let .peer(peer, _, _) = peer {
self?.contactListNode?.listNode.clearHighlightAnimated(true)
self?.requestOpenPeer?(EnginePeer(peer), nil)

View File

@ -12,9 +12,13 @@ swift_library(
deps = [
"//submodules/Display",
"//submodules/ComponentFlow",
"//submodules/TelegramCore",
"//submodules/TelegramPresentationData",
"//submodules/Components/MultilineTextComponent",
"//submodules/Components/MultilineTextWithEntitiesComponent",
"//submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView",
"//submodules/AccountContext",
"//submodules/TextFormat",
],
visibility = [
"//visibility:public",

View File

@ -2,25 +2,35 @@ import Foundation
import UIKit
import Display
import ComponentFlow
import TelegramCore
import MultilineTextComponent
import MultilineTextWithEntitiesComponent
import MessageInlineBlockBackgroundView
import AccountContext
import TextFormat
public final class ForwardInfoPanelComponent: Component {
public let context: AccountContext
public let authorName: String
public let text: String
public let entities: [MessageTextEntity]
public let isChannel: Bool
public let isVibrant: Bool
public let fillsWidth: Bool
public init(
context: AccountContext,
authorName: String,
text: String,
entities: [MessageTextEntity],
isChannel: Bool,
isVibrant: Bool,
fillsWidth: Bool
) {
self.context = context
self.authorName = authorName
self.text = text
self.entities = entities
self.isChannel = isChannel
self.isVibrant = isVibrant
self.fillsWidth = fillsWidth
@ -33,6 +43,9 @@ public final class ForwardInfoPanelComponent: Component {
if lhs.text != rhs.text {
return false
}
if lhs.entities != rhs.entities {
return false
}
if lhs.isChannel != rhs.isChannel {
return false
}
@ -47,7 +60,6 @@ public final class ForwardInfoPanelComponent: Component {
public final class View: UIView {
public let backgroundView: UIImageView
// private let blurBackgroundView: BlurredBackgroundView
private let blurBackgroundView: UIVisualEffectView
private let blockView: MessageInlineBlockBackgroundView
private var iconView: UIImageView?
@ -58,8 +70,6 @@ public final class ForwardInfoPanelComponent: Component {
private weak var state: EmptyComponentState?
override init(frame: CGRect) {
// self.blurBackgroundView = BlurredBackgroundView(color: UIColor(rgb: 0x000000, alpha: 0.4))
if #available(iOS 13.0, *) {
self.blurBackgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterialDark))
} else {
@ -102,7 +112,7 @@ public final class ForwardInfoPanelComponent: Component {
iconView.frame = CGRect(origin: CGPoint(x: sideInset + UIScreenPixel, y: 5.0), size: image.size)
}
titleOffset += 13.0
let titleSize = self.title.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
@ -124,14 +134,20 @@ public final class ForwardInfoPanelComponent: Component {
view.frame = titleFrame
}
let textFont = Font.regular(14.0)
let textColor = UIColor.white
let attributedText = stringWithAppliedEntities(component.text, entities: component.entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: nil)
let textSize = self.text.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(
string: component.text,
font: Font.regular(14.0),
textColor: .white
)),
component: AnyComponent(MultilineTextWithEntitiesComponent(
context: component.context,
animationCache: component.context.animationCache,
animationRenderer: component.context.animationRenderer,
placeholderColor: UIColor(rgb: 0xffffff, alpha: 0.4),
text: .plain(attributedText),
horizontalAlignment: .natural,
truncationType: .end,
maximumNumberOfLines: 1
)),
environment: {},

View File

@ -698,6 +698,7 @@ final class StoryContentCaptionComponent: Component {
let authorName: String
let isChannel: Bool
let text: String?
let entities: [MessageTextEntity]
switch forwardInfo {
case let .known(peer, _, _):
@ -706,6 +707,7 @@ final class StoryContentCaptionComponent: Component {
if let story = self.forwardInfoStory {
text = story.text
entities = story.entities
} else if self.forwardInfoDisposable == nil, let forwardInfoStory = component.forwardInfoStory {
self.forwardInfoDisposable = (forwardInfoStory
|> deliverOnMainQueue).start(next: { story in
@ -717,13 +719,16 @@ final class StoryContentCaptionComponent: Component {
}
})
text = ""
entities = []
} else {
text = ""
entities = []
}
case let .unknown(name, _):
authorName = name
isChannel = false
text = ""
entities = []
}
if let text {
@ -741,8 +746,10 @@ final class StoryContentCaptionComponent: Component {
PlainButtonComponent(
content: AnyComponent(
ForwardInfoPanelComponent(
context: component.context,
authorName: authorName,
text: text,
entities: entities,
isChannel: isChannel,
isVibrant: false,
fillsWidth: false

View File

@ -757,15 +757,11 @@ final class StoryItemSetContainerSendMessage {
let size = image.size.aspectFitted(CGSize(width: 512.0, height: 512.0))
func scaleImage(_ image: UIImage, size: CGSize, boundiingSize: CGSize) -> UIImage? {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
let format = UIGraphicsImageRendererFormat()
format.scale = 1.0
let renderer = UIGraphicsImageRenderer(size: size, format: format)
return renderer.image { _ in
image.draw(in: CGRect(origin: .zero, size: size))
}
} else {
return TGScaleImageToPixelSize(image, size)
let format = UIGraphicsImageRendererFormat()
format.scale = 1.0
let renderer = UIGraphicsImageRenderer(size: size, format: format)
return renderer.image { _ in
image.draw(in: CGRect(origin: .zero, size: size))
}
}

View File

@ -41,12 +41,23 @@ public func makeTelegramAccountAuxiliaryMethods(uploadInBackground: ((Postbox, M
}
|> castError(MediaResourceDataFetchError.self)
|> mapToSignal { useModernPipeline -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> in
fetchLocalFileVideoMediaResource(postbox: postbox, resource: resource, alwaysUseModernPipeline: useModernPipeline)
return fetchLocalFileVideoMediaResource(postbox: postbox, resource: resource, alwaysUseModernPipeline: useModernPipeline)
}
} else if let resource = resource as? LocalFileGifMediaResource {
return fetchLocalFileGifMediaResource(resource: resource)
} else if let photoLibraryResource = resource as? PhotoLibraryMediaResource {
return fetchPhotoLibraryResource(localIdentifier: photoLibraryResource.localIdentifier, width: photoLibraryResource.width, height: photoLibraryResource.height, format: photoLibraryResource.format, quality: photoLibraryResource.quality)
return postbox.transaction { transaction -> Bool in
var useExif = true
let appConfig = currentAppConfiguration(transaction: transaction)
if let data = appConfig.data, let _ = data["ios_killswitch_disable_use_photo_exif"] {
useExif = false
}
return useExif
}
|> castError(MediaResourceDataFetchError.self)
|> mapToSignal { useExif -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> in
return fetchPhotoLibraryResource(localIdentifier: photoLibraryResource.localIdentifier, width: photoLibraryResource.width, height: photoLibraryResource.height, format: photoLibraryResource.format, quality: photoLibraryResource.quality, useExif: useExif)
}
} else if let resource = resource as? ICloudFileResource {
return fetchICloudFileResource(resource: resource)
} else if let resource = resource as? SecureIdLocalImageResource {

View File

@ -1163,6 +1163,8 @@ extension ChatControllerImpl {
controller.openCamera = { [weak self] cameraView in
if let cameraView = cameraView as? TGAttachmentCameraView {
self?.openCamera(cameraView: cameraView)
} else {
self?.openCamera(cameraView: nil)
}
}
controller.presentWebSearch = { [weak self, weak controller] mediaGroups, activateOnDisplay in

View File

@ -670,10 +670,8 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
if messages.count == 1 {
for media in messages[0].media {
if let file = media as? TelegramMediaFile {
for attribute in file.attributes {
if case let .Sticker(_, packInfo, _) = attribute, packInfo != nil {
loadStickerSaveStatus = file.fileId
}
if file.isSticker {
loadStickerSaveStatus = file.fileId
}
if loadStickerSaveStatus == nil {
loadCopyMediaResource = file.resource
@ -1160,16 +1158,6 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
}
}
for attribute in message.attributes {
if hasExpandedAudioTranscription, let attribute = attribute as? AudioTranscriptionMessageAttribute {
if !messageText.isEmpty {
messageText.append("\n")
}
messageText.append(attribute.text)
break
}
}
var isPoll = false
if messageText.isEmpty {
for media in message.media {

View File

@ -116,7 +116,7 @@ public class ComposeControllerImpl: ViewController, ComposeController {
self?.activateSearch()
}
self.contactsNode.contactListNode.openPeer = { [weak self] peer, _ in
self.contactsNode.contactListNode.openPeer = { [weak self] peer, _, _, _ in
if case let .peer(peer, _, _) = peer {
self?.openPeer(peerId: peer.id)
}

View File

@ -16,6 +16,7 @@ import CounterContollerTitleView
import EditableTokenListNode
import PremiumUI
import UndoUI
import ContextUI
private func peerTokenTitle(accountPeerId: PeerId, peer: Peer, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) -> String {
if peer.id == accountPeerId {
@ -415,6 +416,38 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
}
}
self.contactsNode.openPeerMore = { [weak self] peer, node, gesture in
guard let self, case let .peer(peer, _, _) = peer, let node = node as? ContextReferenceContentNode else {
return
}
let presentationData = self.presentationData
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Premium_Gift_ContactSelection_SendMessage, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MessageBubble"), color: theme.contextMenu.primaryColor)
}, iconPosition: .left, action: { [weak self] _, a in
a(.default)
if let self {
self.params.sendMessage?(EnginePeer(peer))
}
})))
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Premium_Gift_ContactSelection_OpenProfile, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.contextMenu.primaryColor)
}, iconPosition: .left, action: { [weak self] _, a in
a(.default)
if let self {
self.params.openProfile?(EnginePeer(peer))
}
})))
let contextController = ContextController(presentationData: presentationData, source: .reference(ContactContextReferenceContentSource(controller: self, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
self.present(contextController, in: .window(.root))
}
self.contactsNode.openDisabledPeer = { [weak self] peer, reason in
guard let self else {
return
@ -765,3 +798,17 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
self._result.set(.single(.result(peerIds: peerIds, additionalOptionIds: additionalOptionIds)))
}
}
private final class ContactContextReferenceContentSource: ContextReferenceContentSource {
private let controller: ViewController
private let sourceNode: ContextReferenceContentNode
init(controller: ViewController, sourceNode: ContextReferenceContentNode) {
self.controller = controller
self.sourceNode = sourceNode
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

View File

@ -13,6 +13,7 @@ import AnimationCache
import MultiAnimationRenderer
import EditableTokenListNode
import SolidRoundedButtonNode
import ContextUI
private struct SearchResultEntry: Identifiable {
let index: Int
@ -58,6 +59,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
var requestDeactivateSearch: (() -> Void)?
var requestOpenPeerFromSearch: ((ContactListPeerId) -> Void)?
var openPeer: ((ContactListPeer) -> Void)?
var openPeerMore: ((ContactListPeer, ASDisplayNode?, ContextGesture?) -> Void)?
var openDisabledPeer: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?
var removeSelectedPeer: ((ContactListPeerId) -> Void)?
var removeSelectedCategory: ((Int) -> Void)?
@ -262,8 +264,12 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
switch self.contentNode {
case let .contacts(contactsNode):
contactsNode.openPeer = { [weak self] peer, _ in
self?.openPeer?(peer)
contactsNode.openPeer = { [weak self] peer, action, sourceNode, gesture in
if case .more = action {
self?.openPeerMore?(peer, sourceNode, gesture)
} else {
self?.openPeer?(peer)
}
}
contactsNode.openDisabledPeer = { [weak self] peer, reason in
guard let self else {
@ -362,7 +368,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
globalSearch: globalSearch,
displaySavedMessages: displaySavedMessages
))), filters: filters, onlyWriteable: strongSelf.onlyWriteable, isGroupInvitation: strongSelf.isGroupInvitation, isPeerEnabled: strongSelf.isPeerEnabled, selectionState: selectionState, isSearch: true)
searchResultsNode.openPeer = { peer, _ in
searchResultsNode.openPeer = { peer, _, _, _ in
self?.tokenListNode.setText("")
self?.openPeer?(peer)
}

View File

@ -197,7 +197,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
self?.activateSearch()
}
self.contactsNode.contactListNode.openPeer = { [weak self] peer, action in
self.contactsNode.contactListNode.openPeer = { [weak self] peer, action, _, _ in
self?.openPeer(peer: peer, action: action)
}

View File

@ -1,19 +1,14 @@
import UIKit
import SwiftSignalKit
import LegacyComponents
import Display
import WebPBinding
private func scaleImage(_ image: UIImage, size: CGSize, boundiingSize: CGSize) -> UIImage? {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
let format = UIGraphicsImageRendererFormat()
format.scale = 1.0
let renderer = UIGraphicsImageRenderer(size: size, format: format)
return renderer.image { _ in
image.draw(in: CGRect(origin: .zero, size: size))
}
} else {
return TGScaleImageToPixelSize(image, size)
let format = UIGraphicsImageRendererFormat()
format.scale = 1.0
let renderer = UIGraphicsImageRenderer(size: size, format: format)
return renderer.image { _ in
image.draw(in: CGRect(origin: .zero, size: size))
}
}

View File

@ -2164,15 +2164,33 @@ public final class SharedAccountContextImpl: SharedAccountContext {
mode = .premiumGifting(birthdays: nil, selectToday: false)
}
let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: mode, options: [], isPeerEnabled: { peer in
if case let .user(user) = peer, user.botInfo == nil && !peer.isService && !user.flags.contains(.isSupport) {
return true
} else {
return false
}
}, limit: limit, reachedLimit: { limit in
reachedLimitImpl?(limit)
}))
var openProfileImpl: ((EnginePeer) -> Void)?
var sendMessageImpl: ((EnginePeer) -> Void)?
let controller = context.sharedContext.makeContactMultiselectionController(
ContactMultiselectionControllerParams(
context: context,
mode: mode,
options: [],
isPeerEnabled: { peer in
if case let .user(user) = peer, user.botInfo == nil && !peer.isService && !user.flags.contains(.isSupport) {
return true
} else {
return false
}
},
limit: limit,
reachedLimit: { limit in
reachedLimitImpl?(limit)
},
openProfile: { peer in
openProfileImpl?(peer)
},
sendMessage: { peer in
sendMessageImpl?(peer)
}
)
)
reachedLimitImpl = { [weak controller] limit in
guard let controller else {
@ -2227,6 +2245,36 @@ public final class SharedAccountContextImpl: SharedAccountContext {
controller.push(giftController)
})
sendMessageImpl = { [weak self, weak controller] peer in
guard let self, let controller, let navigationController = controller.navigationController as? NavigationController else {
return
}
self.navigateToChatController(
NavigateToChatControllerParams(
navigationController: navigationController,
context: context,
chatLocation: .peer(peer)
)
)
}
openProfileImpl = { [weak self, weak controller] peer in
guard let self, let controller else {
return
}
if let infoController = self.makePeerInfoController(
context: context,
updatedPresentationData: nil,
peer: peer._asPeer(),
mode: .generic,
avatarInitiallyExpanded: true,
fromChat: false,
requestsContext: nil
) {
controller.replace(with: infoController)
}
}
return controller
}