mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
0d1d0c265d
commit
ead45405b7
@ -12050,3 +12050,5 @@ Sorry for the inconvenience.";
|
|||||||
"MediaEditor.NewStickerPack.Title" = "New Sticker Set";
|
"MediaEditor.NewStickerPack.Title" = "New Sticker Set";
|
||||||
"MediaEditor.NewStickerPack.Text" = "Choose a name for your sticker set.";
|
"MediaEditor.NewStickerPack.Text" = "Choose a name for your sticker set.";
|
||||||
|
|
||||||
|
"Premium.Gift.ContactSelection.SendMessage" = "Send Message";
|
||||||
|
"Premium.Gift.ContactSelection.OpenProfile" = "Open Profile";
|
||||||
|
@ -637,6 +637,7 @@ public enum ContactListAction: Equatable {
|
|||||||
case generic
|
case generic
|
||||||
case voiceCall
|
case voiceCall
|
||||||
case videoCall
|
case videoCall
|
||||||
|
case more
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ContactListPeer: Equatable {
|
public enum ContactListPeer: Equatable {
|
||||||
|
@ -102,8 +102,10 @@ public final class ContactMultiselectionControllerParams {
|
|||||||
public let alwaysEnabled: Bool
|
public let alwaysEnabled: Bool
|
||||||
public let limit: Int32?
|
public let limit: Int32?
|
||||||
public let reachedLimit: ((Int32) -> Void)?
|
public let reachedLimit: ((Int32) -> Void)?
|
||||||
|
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) {
|
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.context = context
|
||||||
self.updatedPresentationData = updatedPresentationData
|
self.updatedPresentationData = updatedPresentationData
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
@ -116,6 +118,8 @@ public final class ContactMultiselectionControllerParams {
|
|||||||
self.alwaysEnabled = alwaysEnabled
|
self.alwaysEnabled = alwaysEnabled
|
||||||
self.limit = limit
|
self.limit = limit
|
||||||
self.reachedLimit = reachedLimit
|
self.reachedLimit = reachedLimit
|
||||||
|
self.openProfile = openProfile
|
||||||
|
self.sendMessage = sendMessage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ swift_library(
|
|||||||
"//submodules/TooltipUI",
|
"//submodules/TooltipUI",
|
||||||
"//submodules/UndoUI",
|
"//submodules/UndoUI",
|
||||||
"//submodules/TelegramIntents",
|
"//submodules/TelegramIntents",
|
||||||
|
"//submodules/ContextUI",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -23,6 +23,7 @@ import AppBundle
|
|||||||
import ContextUI
|
import ContextUI
|
||||||
import PhoneNumberFormat
|
import PhoneNumberFormat
|
||||||
import LocalizedPeerData
|
import LocalizedPeerData
|
||||||
|
import ContextUI
|
||||||
|
|
||||||
private let dropDownIcon = { () -> UIImage in
|
private let dropDownIcon = { () -> UIImage in
|
||||||
UIGraphicsBeginImageContextWithOptions(CGSize(width: 12.0, height: 12.0), false, 0.0)
|
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 activateSearch: () -> Void
|
||||||
fileprivate let authorize: () -> Void
|
fileprivate let authorize: () -> Void
|
||||||
fileprivate let suppressWarning: () -> 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 openDisabledPeer: (EnginePeer, ChatListDisabledPeerReason) -> Void
|
||||||
fileprivate let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?, Bool) -> Void)?
|
fileprivate let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?, Bool) -> Void)?
|
||||||
fileprivate let openStories: (EnginePeer, ASDisplayNode) -> Void
|
fileprivate let openStories: (EnginePeer, ASDisplayNode) -> Void
|
||||||
@ -65,7 +66,7 @@ private final class ContactListNodeInteraction {
|
|||||||
|
|
||||||
let itemHighlighting = ContactItemHighlighting()
|
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.activateSearch = activateSearch
|
||||||
self.authorize = authorize
|
self.authorize = authorize
|
||||||
self.suppressWarning = suppressWarning
|
self.suppressWarning = suppressWarning
|
||||||
@ -96,7 +97,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
|||||||
case permissionInfo(PresentationTheme, String, String, Bool)
|
case permissionInfo(PresentationTheme, String, String, Bool)
|
||||||
case permissionEnable(PresentationTheme, String)
|
case permissionEnable(PresentationTheme, String)
|
||||||
case option(Int, ContactListAdditionalOption, ListViewItemHeader?, PresentationTheme, PresentationStrings)
|
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 {
|
var stableId: ContactListNodeEntryId {
|
||||||
switch self {
|
switch self {
|
||||||
@ -110,7 +111,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
|||||||
return .permission(action: true)
|
return .permission(action: true)
|
||||||
case let .option(index, _, _, _, _):
|
case let .option(index, _, _, _, _):
|
||||||
return .option(index: index)
|
return .option(index: index)
|
||||||
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, storyData, _):
|
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, storyData, _):
|
||||||
switch peer {
|
switch peer {
|
||||||
case let .peer(peer, _, _):
|
case let .peer(peer, _, _):
|
||||||
return .peerId(peerId: peer.id.toInt64(), section: storyData != nil ? .stories : .contacts)
|
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, _, _):
|
case let .option(_, option, header, _, _):
|
||||||
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, icon: option.icon, clearHighlightAutomatically: option.clearHighlightAutomatically, header: header, action: option.action)
|
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
|
var status: ContactsPeerItemStatus
|
||||||
let itemPeer: ContactsPeerItemPeer
|
let itemPeer: ContactsPeerItemPeer
|
||||||
var isContextActionEnabled = false
|
var isContextActionEnabled = false
|
||||||
@ -200,11 +201,15 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var additionalActions: [ContactsPeerItemAction] = []
|
var additionalActions: [ContactsPeerItemAction] = []
|
||||||
if displayCallIcons {
|
if hasMoreButton {
|
||||||
additionalActions = [ContactsPeerItemAction(icon: .voiceCall, action: { _ in
|
additionalActions = [ContactsPeerItemAction(icon: .more, action: { _, sourceNode, gesture in
|
||||||
interaction.openPeer(peer, .voiceCall)
|
interaction.openPeer(peer, .more, sourceNode, gesture)
|
||||||
}), ContactsPeerItemAction(icon: .videoCall, action: { _ in
|
})]
|
||||||
interaction.openPeer(peer, .videoCall)
|
} 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
|
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
|
}, disabledAction: { _ in
|
||||||
if case let .peer(peer, _, _) = peer {
|
if case let .peer(peer, _, _) = peer {
|
||||||
interaction.openDisabledPeer(EnginePeer(peer), requiresPremiumForMessaging ? .premiumRequired : .generic)
|
interaction.openDisabledPeer(EnginePeer(peer), requiresPremiumForMessaging ? .premiumRequired : .generic)
|
||||||
@ -263,9 +268,9 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
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 {
|
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 {
|
if lhsIndex != rhsIndex {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -303,6 +308,9 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
|||||||
if lhsDisplayCallIcons != rhsDisplayCallIcons {
|
if lhsDisplayCallIcons != rhsDisplayCallIcons {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhsHasMoreButton != rhsHasMoreButton {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhsEnabled != rhsEnabled {
|
if lhsEnabled != rhsEnabled {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -353,11 +361,11 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
|||||||
case .peer:
|
case .peer:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case let .peer(lhsIndex, _, _, _, _, _, _, _, _, _, _, _, lhsStoryData, _):
|
case let .peer(lhsIndex, _, _, _, _, _, _, _, _, _, _, _, _, lhsStoryData, _):
|
||||||
switch rhs {
|
switch rhs {
|
||||||
case .search, .sort, .permissionInfo, .permissionEnable, .option:
|
case .search, .sort, .permissionInfo, .permissionEnable, .option:
|
||||||
return false
|
return false
|
||||||
case let .peer(rhsIndex, _, _, _, _, _, _, _, _, _, _, _, rhsStoryData, _):
|
case let .peer(rhsIndex, _, _, _, _, _, _, _, _, _, _, _, _, rhsStoryData, _):
|
||||||
if (lhsStoryData == nil) != (rhsStoryData == nil) {
|
if (lhsStoryData == nil) != (rhsStoryData == nil) {
|
||||||
if lhsStoryData != nil {
|
if lhsStoryData != nil {
|
||||||
return true
|
return true
|
||||||
@ -551,7 +559,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis
|
|||||||
}
|
}
|
||||||
|
|
||||||
let presence = presences[peer.id]
|
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
|
index += 1
|
||||||
}
|
}
|
||||||
@ -608,7 +616,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis
|
|||||||
}
|
}
|
||||||
|
|
||||||
let presence = presences[peer.id]
|
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
|
index += 1
|
||||||
}
|
}
|
||||||
@ -657,7 +665,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis
|
|||||||
}
|
}
|
||||||
|
|
||||||
let presence = presences[peer.id]
|
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
|
index += 1
|
||||||
}
|
}
|
||||||
@ -701,7 +709,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis
|
|||||||
enabled = true
|
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
|
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
|
index += 1
|
||||||
}
|
}
|
||||||
return entries
|
return entries
|
||||||
@ -772,7 +780,7 @@ private func preparedContactListNodeTransition(context: AccountContext, presenta
|
|||||||
case .search:
|
case .search:
|
||||||
//indexSections.apend(CollectionIndexNode.searchIndex)
|
//indexSections.apend(CollectionIndexNode.searchIndex)
|
||||||
break
|
break
|
||||||
case let .peer(_, _, _, header, _, _, _, _, _, _, _, _, _, _):
|
case let .peer(_, _, _, header, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
if let header = header as? ContactListNameIndexHeader {
|
if let header = header as? ContactListNameIndexHeader {
|
||||||
if !existingSections.contains(header.letter) {
|
if !existingSections.contains(header.letter) {
|
||||||
existingSections.insert(header.letter)
|
existingSections.insert(header.letter)
|
||||||
@ -1036,7 +1044,7 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
public var contentScrollingEnded: ((ListView) -> Bool)?
|
public var contentScrollingEnded: ((ListView) -> Bool)?
|
||||||
|
|
||||||
public var activateSearch: (() -> Void)?
|
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 openDisabledPeer: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?
|
||||||
public var deselectedAll: (() -> Void)?
|
public var deselectedAll: (() -> Void)?
|
||||||
public var updatedSelection: (([EnginePeer], Bool) -> Void)?
|
public var updatedSelection: (([EnginePeer], Bool) -> Void)?
|
||||||
@ -1136,7 +1144,7 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
authorizeImpl?()
|
authorizeImpl?()
|
||||||
}, suppressWarning: { [weak self] in
|
}, suppressWarning: { [weak self] in
|
||||||
self?.suppressPermissionWarning?()
|
self?.suppressPermissionWarning?()
|
||||||
}, openPeer: { [weak self] peer, action in
|
}, openPeer: { [weak self] peer, action, sourceNode, gesture in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if strongSelf.multipleSelection {
|
if strongSelf.multipleSelection {
|
||||||
var updated = false
|
var updated = false
|
||||||
@ -1151,10 +1159,10 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if !updated {
|
if !updated {
|
||||||
strongSelf.openPeer?(peer, action)
|
strongSelf.openPeer?(peer, action, sourceNode, gesture)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
strongSelf.openPeer?(peer, action)
|
strongSelf.openPeer?(peer, action, sourceNode, gesture)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, openDisabledPeer: { [weak self] peer, reason in
|
}, 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 })
|
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
|
break loop
|
||||||
}
|
}
|
||||||
case let .peer(_, _, _, header, _, _, _, _, _, _, _, _, _, _):
|
case let .peer(_, _, _, header, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
if let header = header as? ContactListNameIndexHeader {
|
if let header = header as? ContactListNameIndexHeader {
|
||||||
if let scalar = UnicodeScalar(header.letter) {
|
if let scalar = UnicodeScalar(header.letter) {
|
||||||
let title = "\(Character(scalar))"
|
let title = "\(Character(scalar))"
|
||||||
|
@ -345,7 +345,7 @@ public class ContactsController: ViewController {
|
|||||||
self?.activateSearch()
|
self?.activateSearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.contactsNode.contactListNode.openPeer = { [weak self] peer, _ in
|
self.contactsNode.contactListNode.openPeer = { [weak self] peer, _, _, _ in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer",
|
"//submodules/TelegramUI/Components/MultiAnimationRenderer",
|
||||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||||
"//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent",
|
"//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent",
|
||||||
|
"//submodules/MoreButtonNode",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -20,6 +20,7 @@ import ComponentFlow
|
|||||||
import AnimationCache
|
import AnimationCache
|
||||||
import MultiAnimationRenderer
|
import MultiAnimationRenderer
|
||||||
import EmojiStatusComponent
|
import EmojiStatusComponent
|
||||||
|
import MoreButtonNode
|
||||||
|
|
||||||
public final class ContactItemHighlighting {
|
public final class ContactItemHighlighting {
|
||||||
public var chatLocation: ChatLocation?
|
public var chatLocation: ChatLocation?
|
||||||
@ -88,13 +89,14 @@ public enum ContactsPeerItemActionIcon {
|
|||||||
case add
|
case add
|
||||||
case voiceCall
|
case voiceCall
|
||||||
case videoCall
|
case videoCall
|
||||||
|
case more
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ContactsPeerItemAction {
|
public struct ContactsPeerItemAction {
|
||||||
public let icon: ContactsPeerItemActionIcon
|
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.icon = icon
|
||||||
self.action = action
|
self.action = action
|
||||||
}
|
}
|
||||||
@ -417,6 +419,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||||||
private var badgeTextNode: TextNode?
|
private var badgeTextNode: TextNode?
|
||||||
private var selectionNode: CheckNode?
|
private var selectionNode: CheckNode?
|
||||||
private var actionButtonNodes: [HighlightableButtonNode]?
|
private var actionButtonNodes: [HighlightableButtonNode]?
|
||||||
|
private var moreButtonNode: MoreButtonNode?
|
||||||
private var arrowButtonNode: HighlightableButtonNode?
|
private var arrowButtonNode: HighlightableButtonNode?
|
||||||
|
|
||||||
private var avatarTapRecognizer: UITapGestureRecognizer?
|
private var avatarTapRecognizer: UITapGestureRecognizer?
|
||||||
@ -744,10 +747,11 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
var actionButtons: [ActionButton]?
|
var actionButtons: [ActionButton]?
|
||||||
struct ActionButton {
|
struct ActionButton {
|
||||||
|
let type: ContactsPeerItemActionIcon
|
||||||
let image: UIImage?
|
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?
|
let image: UIImage?
|
||||||
switch icon {
|
switch icon {
|
||||||
case .none:
|
case .none:
|
||||||
@ -758,7 +762,10 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||||||
image = PresentationResourcesItemList.voiceCallIcon(theme)
|
image = PresentationResourcesItemList.voiceCallIcon(theme)
|
||||||
case .videoCall:
|
case .videoCall:
|
||||||
image = PresentationResourcesItemList.videoCallIcon(theme)
|
image = PresentationResourcesItemList.videoCallIcon(theme)
|
||||||
|
case .more:
|
||||||
|
image = PresentationResourcesItemList.videoCallIcon(theme)
|
||||||
}
|
}
|
||||||
|
self.type = icon
|
||||||
self.image = image
|
self.image = image
|
||||||
self.action = action
|
self.action = action
|
||||||
}
|
}
|
||||||
@ -1357,7 +1364,23 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||||||
verifiedIconView.removeFromSuperview()
|
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 {
|
if strongSelf.actionButtonNodes == nil {
|
||||||
var actionButtonNodes: [HighlightableButtonNode] = []
|
var actionButtonNodes: [HighlightableButtonNode] = []
|
||||||
for action in actionButtons {
|
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 {
|
guard let actionButtonNodes = self.actionButtonNodes, let index = actionButtonNodes.firstIndex(of: sender), let item = self.item, index < item.additionalActions.count else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item.additionalActions[index].action?(item.peer)
|
item.additionalActions[index].action?(item.peer, sender, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
override public func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
|
@ -1599,7 +1599,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
videoNode.setBaseRate(self.playbackRate ?? 1.0)
|
videoNode.setBaseRate(self.playbackRate ?? 1.0)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if self.shouldAutoplayOnCentrality() {
|
if isAnimated {
|
||||||
|
self.playOnContentOwnership = true
|
||||||
|
} else if self.shouldAutoplayOnCentrality() {
|
||||||
self.playOnContentOwnership = true
|
self.playOnContentOwnership = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1592,11 +1592,14 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
if case let .noAccess(cameraAccess) = self.state {
|
if case let .noAccess(cameraAccess) = self.state {
|
||||||
var hasCamera = cameraAccess == .authorized
|
var hasCamera = cameraAccess == .authorized
|
||||||
var story = false
|
var story = false
|
||||||
if let subject = self.controller?.subject, case .assets(_, .story) = subject {
|
if let subject = self.controller?.subject {
|
||||||
|
if case .assets(_, .story) = subject {
|
||||||
hasCamera = false
|
hasCamera = false
|
||||||
story = true
|
story = true
|
||||||
|
|
||||||
self.controller?.navigationItem.rightBarButtonItem = nil
|
self.controller?.navigationItem.rightBarButtonItem = nil
|
||||||
|
} else if case .assets(_, .createSticker) = subject {
|
||||||
|
hasCamera = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var placeholderTransition = transition
|
var placeholderTransition = transition
|
||||||
|
@ -112,6 +112,10 @@ private final class MediaCutoutScreenComponent: Component {
|
|||||||
x: location.x / controller.drawingView.bounds.width,
|
x: location.x / controller.drawingView.bounds.width,
|
||||||
y: location.y / controller.drawingView.bounds.height
|
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
|
component.mediaEditor.processImage { [weak self] originalImage, _ in
|
||||||
cutoutImage(from: originalImage, values: nil, target: .point(point), includeExtracted: false, completion: { [weak self] results in
|
cutoutImage(from: originalImage, values: nil, target: .point(point), includeExtracted: false, completion: { [weak self] results in
|
||||||
|
@ -1177,8 +1177,10 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
let authorName = forwardAuthor.displayTitle(strings: environment.strings, displayOrder: .firstLast)
|
let authorName = forwardAuthor.displayTitle(strings: environment.strings, displayOrder: .firstLast)
|
||||||
header = AnyComponent(
|
header = AnyComponent(
|
||||||
ForwardInfoPanelComponent(
|
ForwardInfoPanelComponent(
|
||||||
|
context: component.context,
|
||||||
authorName: authorName,
|
authorName: authorName,
|
||||||
text: forwardStory.text,
|
text: forwardStory.text,
|
||||||
|
entities: forwardStory.entities,
|
||||||
isChannel: forwardAuthor.id.isGroupOrChannel,
|
isChannel: forwardAuthor.id.isGroupOrChannel,
|
||||||
isVibrant: true,
|
isVibrant: true,
|
||||||
fillsWidth: true
|
fillsWidth: true
|
||||||
|
@ -228,20 +228,48 @@ func findEdgePoints(in pixelBuffer: CVPixelBuffer) -> [CGPoint] {
|
|||||||
return pixel >= 235
|
return pixel >= 235
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var startPoint: Point? = nil
|
||||||
|
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 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let directions = [(1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1)]
|
let directions = [(1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1)]
|
||||||
var lastDirectionIndex = 0
|
var lastDirectionIndex = 0
|
||||||
|
|
||||||
var startPoint: Point? = nil
|
guard let startingPoint = startPoint, componentSize > 60 else { return [] }
|
||||||
outerLoop: for y in 0..<height {
|
|
||||||
for x in 0..<width {
|
|
||||||
if isPixelWhiteAt(x: x, y: y) {
|
|
||||||
startPoint = Point(x: x, y: y)
|
|
||||||
break outerLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let startingPoint = startPoint else { return [] }
|
|
||||||
edgePoints.insert(startingPoint)
|
edgePoints.insert(startingPoint)
|
||||||
edgePath.append(startingPoint)
|
edgePath.append(startingPoint)
|
||||||
var currentPoint = startingPoint
|
var currentPoint = startingPoint
|
||||||
|
@ -1365,7 +1365,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
|||||||
contactListNode.activateSearch = { [weak self] in
|
contactListNode.activateSearch = { [weak self] in
|
||||||
self?.requestActivateSearch?()
|
self?.requestActivateSearch?()
|
||||||
}
|
}
|
||||||
contactListNode.openPeer = { [weak self] peer, _ in
|
contactListNode.openPeer = { [weak self] peer, _, _, _ in
|
||||||
if case let .peer(peer, _, _) = peer {
|
if case let .peer(peer, _, _) = peer {
|
||||||
self?.contactListNode?.listNode.clearHighlightAnimated(true)
|
self?.contactListNode?.listNode.clearHighlightAnimated(true)
|
||||||
self?.requestOpenPeer?(EnginePeer(peer), nil)
|
self?.requestOpenPeer?(EnginePeer(peer), nil)
|
||||||
|
@ -12,9 +12,13 @@ swift_library(
|
|||||||
deps = [
|
deps = [
|
||||||
"//submodules/Display",
|
"//submodules/Display",
|
||||||
"//submodules/ComponentFlow",
|
"//submodules/ComponentFlow",
|
||||||
|
"//submodules/TelegramCore",
|
||||||
"//submodules/TelegramPresentationData",
|
"//submodules/TelegramPresentationData",
|
||||||
"//submodules/Components/MultilineTextComponent",
|
"//submodules/Components/MultilineTextComponent",
|
||||||
|
"//submodules/Components/MultilineTextWithEntitiesComponent",
|
||||||
"//submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView",
|
"//submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView",
|
||||||
|
"//submodules/AccountContext",
|
||||||
|
"//submodules/TextFormat",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -2,25 +2,35 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import Display
|
import Display
|
||||||
import ComponentFlow
|
import ComponentFlow
|
||||||
|
import TelegramCore
|
||||||
import MultilineTextComponent
|
import MultilineTextComponent
|
||||||
|
import MultilineTextWithEntitiesComponent
|
||||||
import MessageInlineBlockBackgroundView
|
import MessageInlineBlockBackgroundView
|
||||||
|
import AccountContext
|
||||||
|
import TextFormat
|
||||||
|
|
||||||
public final class ForwardInfoPanelComponent: Component {
|
public final class ForwardInfoPanelComponent: Component {
|
||||||
|
public let context: AccountContext
|
||||||
public let authorName: String
|
public let authorName: String
|
||||||
public let text: String
|
public let text: String
|
||||||
|
public let entities: [MessageTextEntity]
|
||||||
public let isChannel: Bool
|
public let isChannel: Bool
|
||||||
public let isVibrant: Bool
|
public let isVibrant: Bool
|
||||||
public let fillsWidth: Bool
|
public let fillsWidth: Bool
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
|
context: AccountContext,
|
||||||
authorName: String,
|
authorName: String,
|
||||||
text: String,
|
text: String,
|
||||||
|
entities: [MessageTextEntity],
|
||||||
isChannel: Bool,
|
isChannel: Bool,
|
||||||
isVibrant: Bool,
|
isVibrant: Bool,
|
||||||
fillsWidth: Bool
|
fillsWidth: Bool
|
||||||
) {
|
) {
|
||||||
|
self.context = context
|
||||||
self.authorName = authorName
|
self.authorName = authorName
|
||||||
self.text = text
|
self.text = text
|
||||||
|
self.entities = entities
|
||||||
self.isChannel = isChannel
|
self.isChannel = isChannel
|
||||||
self.isVibrant = isVibrant
|
self.isVibrant = isVibrant
|
||||||
self.fillsWidth = fillsWidth
|
self.fillsWidth = fillsWidth
|
||||||
@ -33,6 +43,9 @@ public final class ForwardInfoPanelComponent: Component {
|
|||||||
if lhs.text != rhs.text {
|
if lhs.text != rhs.text {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.entities != rhs.entities {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.isChannel != rhs.isChannel {
|
if lhs.isChannel != rhs.isChannel {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -47,7 +60,6 @@ public final class ForwardInfoPanelComponent: Component {
|
|||||||
|
|
||||||
public final class View: UIView {
|
public final class View: UIView {
|
||||||
public let backgroundView: UIImageView
|
public let backgroundView: UIImageView
|
||||||
// private let blurBackgroundView: BlurredBackgroundView
|
|
||||||
private let blurBackgroundView: UIVisualEffectView
|
private let blurBackgroundView: UIVisualEffectView
|
||||||
private let blockView: MessageInlineBlockBackgroundView
|
private let blockView: MessageInlineBlockBackgroundView
|
||||||
private var iconView: UIImageView?
|
private var iconView: UIImageView?
|
||||||
@ -58,8 +70,6 @@ public final class ForwardInfoPanelComponent: Component {
|
|||||||
private weak var state: EmptyComponentState?
|
private weak var state: EmptyComponentState?
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
// self.blurBackgroundView = BlurredBackgroundView(color: UIColor(rgb: 0x000000, alpha: 0.4))
|
|
||||||
|
|
||||||
if #available(iOS 13.0, *) {
|
if #available(iOS 13.0, *) {
|
||||||
self.blurBackgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterialDark))
|
self.blurBackgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterialDark))
|
||||||
} else {
|
} else {
|
||||||
@ -124,14 +134,20 @@ public final class ForwardInfoPanelComponent: Component {
|
|||||||
view.frame = titleFrame
|
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(
|
let textSize = self.text.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(MultilineTextComponent(
|
component: AnyComponent(MultilineTextWithEntitiesComponent(
|
||||||
text: .plain(NSAttributedString(
|
context: component.context,
|
||||||
string: component.text,
|
animationCache: component.context.animationCache,
|
||||||
font: Font.regular(14.0),
|
animationRenderer: component.context.animationRenderer,
|
||||||
textColor: .white
|
placeholderColor: UIColor(rgb: 0xffffff, alpha: 0.4),
|
||||||
)),
|
text: .plain(attributedText),
|
||||||
|
horizontalAlignment: .natural,
|
||||||
|
truncationType: .end,
|
||||||
maximumNumberOfLines: 1
|
maximumNumberOfLines: 1
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
|
@ -698,6 +698,7 @@ final class StoryContentCaptionComponent: Component {
|
|||||||
let authorName: String
|
let authorName: String
|
||||||
let isChannel: Bool
|
let isChannel: Bool
|
||||||
let text: String?
|
let text: String?
|
||||||
|
let entities: [MessageTextEntity]
|
||||||
|
|
||||||
switch forwardInfo {
|
switch forwardInfo {
|
||||||
case let .known(peer, _, _):
|
case let .known(peer, _, _):
|
||||||
@ -706,6 +707,7 @@ final class StoryContentCaptionComponent: Component {
|
|||||||
|
|
||||||
if let story = self.forwardInfoStory {
|
if let story = self.forwardInfoStory {
|
||||||
text = story.text
|
text = story.text
|
||||||
|
entities = story.entities
|
||||||
} else if self.forwardInfoDisposable == nil, let forwardInfoStory = component.forwardInfoStory {
|
} else if self.forwardInfoDisposable == nil, let forwardInfoStory = component.forwardInfoStory {
|
||||||
self.forwardInfoDisposable = (forwardInfoStory
|
self.forwardInfoDisposable = (forwardInfoStory
|
||||||
|> deliverOnMainQueue).start(next: { story in
|
|> deliverOnMainQueue).start(next: { story in
|
||||||
@ -717,13 +719,16 @@ final class StoryContentCaptionComponent: Component {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
text = ""
|
text = ""
|
||||||
|
entities = []
|
||||||
} else {
|
} else {
|
||||||
text = ""
|
text = ""
|
||||||
|
entities = []
|
||||||
}
|
}
|
||||||
case let .unknown(name, _):
|
case let .unknown(name, _):
|
||||||
authorName = name
|
authorName = name
|
||||||
isChannel = false
|
isChannel = false
|
||||||
text = ""
|
text = ""
|
||||||
|
entities = []
|
||||||
}
|
}
|
||||||
|
|
||||||
if let text {
|
if let text {
|
||||||
@ -741,8 +746,10 @@ final class StoryContentCaptionComponent: Component {
|
|||||||
PlainButtonComponent(
|
PlainButtonComponent(
|
||||||
content: AnyComponent(
|
content: AnyComponent(
|
||||||
ForwardInfoPanelComponent(
|
ForwardInfoPanelComponent(
|
||||||
|
context: component.context,
|
||||||
authorName: authorName,
|
authorName: authorName,
|
||||||
text: text,
|
text: text,
|
||||||
|
entities: entities,
|
||||||
isChannel: isChannel,
|
isChannel: isChannel,
|
||||||
isVibrant: false,
|
isVibrant: false,
|
||||||
fillsWidth: false
|
fillsWidth: false
|
||||||
|
@ -1164,6 +1164,8 @@ extension ChatControllerImpl {
|
|||||||
controller.openCamera = { [weak self] cameraView in
|
controller.openCamera = { [weak self] cameraView in
|
||||||
if let cameraView = cameraView as? TGAttachmentCameraView {
|
if let cameraView = cameraView as? TGAttachmentCameraView {
|
||||||
self?.openCamera(cameraView: cameraView)
|
self?.openCamera(cameraView: cameraView)
|
||||||
|
} else {
|
||||||
|
self?.openCamera(cameraView: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
controller.presentWebSearch = { [weak self, weak controller] mediaGroups, activateOnDisplay in
|
controller.presentWebSearch = { [weak self, weak controller] mediaGroups, activateOnDisplay in
|
||||||
|
@ -670,11 +670,9 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
if messages.count == 1 {
|
if messages.count == 1 {
|
||||||
for media in messages[0].media {
|
for media in messages[0].media {
|
||||||
if let file = media as? TelegramMediaFile {
|
if let file = media as? TelegramMediaFile {
|
||||||
for attribute in file.attributes {
|
if file.isSticker {
|
||||||
if case let .Sticker(_, packInfo, _) = attribute, packInfo != nil {
|
|
||||||
loadStickerSaveStatus = file.fileId
|
loadStickerSaveStatus = file.fileId
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if loadStickerSaveStatus == nil {
|
if loadStickerSaveStatus == nil {
|
||||||
loadCopyMediaResource = file.resource
|
loadCopyMediaResource = file.resource
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ public class ComposeControllerImpl: ViewController, ComposeController {
|
|||||||
self?.activateSearch()
|
self?.activateSearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.contactsNode.contactListNode.openPeer = { [weak self] peer, _ in
|
self.contactsNode.contactListNode.openPeer = { [weak self] peer, _, _, _ in
|
||||||
if case let .peer(peer, _, _) = peer {
|
if case let .peer(peer, _, _) = peer {
|
||||||
self?.openPeer(peerId: peer.id)
|
self?.openPeer(peerId: peer.id)
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import CounterContollerTitleView
|
|||||||
import EditableTokenListNode
|
import EditableTokenListNode
|
||||||
import PremiumUI
|
import PremiumUI
|
||||||
import UndoUI
|
import UndoUI
|
||||||
|
import ContextUI
|
||||||
|
|
||||||
private func peerTokenTitle(accountPeerId: PeerId, peer: Peer, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) -> String {
|
private func peerTokenTitle(accountPeerId: PeerId, peer: Peer, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) -> String {
|
||||||
if peer.id == accountPeerId {
|
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.stings.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.stings.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
|
self.contactsNode.openDisabledPeer = { [weak self] peer, reason in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -765,3 +798,17 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
|||||||
self._result.set(.single(.result(peerIds: peerIds, additionalOptionIds: additionalOptionIds)))
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,6 +13,7 @@ import AnimationCache
|
|||||||
import MultiAnimationRenderer
|
import MultiAnimationRenderer
|
||||||
import EditableTokenListNode
|
import EditableTokenListNode
|
||||||
import SolidRoundedButtonNode
|
import SolidRoundedButtonNode
|
||||||
|
import ContextUI
|
||||||
|
|
||||||
private struct SearchResultEntry: Identifiable {
|
private struct SearchResultEntry: Identifiable {
|
||||||
let index: Int
|
let index: Int
|
||||||
@ -58,6 +59,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
|||||||
var requestDeactivateSearch: (() -> Void)?
|
var requestDeactivateSearch: (() -> Void)?
|
||||||
var requestOpenPeerFromSearch: ((ContactListPeerId) -> Void)?
|
var requestOpenPeerFromSearch: ((ContactListPeerId) -> Void)?
|
||||||
var openPeer: ((ContactListPeer) -> Void)?
|
var openPeer: ((ContactListPeer) -> Void)?
|
||||||
|
var openPeerMore: ((ContactListPeer, ASDisplayNode?, ContextGesture?) -> Void)?
|
||||||
var openDisabledPeer: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?
|
var openDisabledPeer: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?
|
||||||
var removeSelectedPeer: ((ContactListPeerId) -> Void)?
|
var removeSelectedPeer: ((ContactListPeerId) -> Void)?
|
||||||
var removeSelectedCategory: ((Int) -> Void)?
|
var removeSelectedCategory: ((Int) -> Void)?
|
||||||
@ -262,9 +264,13 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
switch self.contentNode {
|
switch self.contentNode {
|
||||||
case let .contacts(contactsNode):
|
case let .contacts(contactsNode):
|
||||||
contactsNode.openPeer = { [weak self] peer, _ in
|
contactsNode.openPeer = { [weak self] peer, action, sourceNode, gesture in
|
||||||
|
if case .more = action {
|
||||||
|
self?.openPeerMore?(peer, sourceNode, gesture)
|
||||||
|
} else {
|
||||||
self?.openPeer?(peer)
|
self?.openPeer?(peer)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
contactsNode.openDisabledPeer = { [weak self] peer, reason in
|
contactsNode.openDisabledPeer = { [weak self] peer, reason in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -362,7 +368,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
|||||||
globalSearch: globalSearch,
|
globalSearch: globalSearch,
|
||||||
displaySavedMessages: displaySavedMessages
|
displaySavedMessages: displaySavedMessages
|
||||||
))), filters: filters, onlyWriteable: strongSelf.onlyWriteable, isGroupInvitation: strongSelf.isGroupInvitation, isPeerEnabled: strongSelf.isPeerEnabled, selectionState: selectionState, isSearch: true)
|
))), 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?.tokenListNode.setText("")
|
||||||
self?.openPeer?(peer)
|
self?.openPeer?(peer)
|
||||||
}
|
}
|
||||||
|
@ -197,7 +197,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
|||||||
self?.activateSearch()
|
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)
|
self?.openPeer(peer: peer, action: action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2164,15 +2164,33 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
mode = .premiumGifting(birthdays: nil, selectToday: false)
|
mode = .premiumGifting(birthdays: nil, selectToday: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: mode, options: [], isPeerEnabled: { peer in
|
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) {
|
if case let .user(user) = peer, user.botInfo == nil && !peer.isService && !user.flags.contains(.isSupport) {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}, limit: limit, reachedLimit: { limit in
|
},
|
||||||
|
limit: limit,
|
||||||
|
reachedLimit: { limit in
|
||||||
reachedLimitImpl?(limit)
|
reachedLimitImpl?(limit)
|
||||||
}))
|
},
|
||||||
|
openProfile: { peer in
|
||||||
|
openProfileImpl?(peer)
|
||||||
|
},
|
||||||
|
sendMessage: { peer in
|
||||||
|
sendMessageImpl?(peer)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
reachedLimitImpl = { [weak controller] limit in
|
reachedLimitImpl = { [weak controller] limit in
|
||||||
guard let controller else {
|
guard let controller else {
|
||||||
@ -2227,6 +2245,36 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
controller.push(giftController)
|
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
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user