mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-23 14:05:33 +00:00
[WIP] Stickers editor
This commit is contained in:
parent
bd8d299a58
commit
6906df0243
@ -997,7 +997,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
|
||||
func makeMediaPickerScreen(context: AccountContext, hasSearch: Bool, completion: @escaping (Any) -> Void) -> ViewController
|
||||
|
||||
func makeStickerEditorScreen(context: AccountContext, source: Any, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile) -> Void) -> ViewController
|
||||
func makeStickerEditorScreen(context: AccountContext, source: Any, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile, @escaping () -> Void) -> Void) -> ViewController
|
||||
|
||||
func makeStickerMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController
|
||||
func makeStoryMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void, groupsPresented: @escaping () -> Void) -> ViewController
|
||||
|
@ -72,7 +72,7 @@ public enum ContactMultiselectionControllerMode {
|
||||
case peerSelection(searchChatList: Bool, searchGroups: Bool, searchChannels: Bool)
|
||||
case channelCreation
|
||||
case chatSelection(ChatSelection)
|
||||
case premiumGifting
|
||||
case premiumGifting(topSectionTitle: String?, topSectionPeers: [EnginePeer.Id])
|
||||
case requestedUsersSelection
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ public enum PremiumGiftSource: Equatable {
|
||||
case profile
|
||||
case attachMenu
|
||||
case settings
|
||||
case chatList
|
||||
case chatList([EnginePeer.Id])
|
||||
case channelBoost
|
||||
case deeplink(String?)
|
||||
}
|
||||
|
@ -1697,7 +1697,7 @@ public final class ChatListNode: ListView {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .chatList, completion: nil)
|
||||
let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .chatList(peerIds), completion: nil)
|
||||
self.push?(controller)
|
||||
}, openActiveSessions: { [weak self] in
|
||||
guard let self else {
|
||||
|
@ -369,7 +369,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactListPeer], presences: [EnginePeer.Id: EnginePeer.Presence], presentation: ContactListPresentation, selectionState: ContactListNodeGroupSelectionState?, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, disabledPeerIds: Set<EnginePeer.Id>, peerRequiresPremiumForMessaging: [EnginePeer.Id: Bool], authorizationStatus: AccessType, warningSuppressed: (Bool, Bool), displaySortOptions: Bool, displayCallIcons: Bool, storySubscriptions: EngineStorySubscriptions?, topPeers: [EnginePeer], interaction: ContactListNodeInteraction) -> [ContactListNodeEntry] {
|
||||
private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactListPeer], presences: [EnginePeer.Id: EnginePeer.Presence], presentation: ContactListPresentation, selectionState: ContactListNodeGroupSelectionState?, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, disabledPeerIds: Set<EnginePeer.Id>, peerRequiresPremiumForMessaging: [EnginePeer.Id: Bool], authorizationStatus: AccessType, warningSuppressed: (Bool, Bool), displaySortOptions: Bool, displayCallIcons: Bool, storySubscriptions: EngineStorySubscriptions?, topPeers: [EnginePeer], topSectionTitle: String?, interaction: ContactListNodeInteraction) -> [ContactListNodeEntry] {
|
||||
var entries: [ContactListNodeEntry] = []
|
||||
|
||||
var commonHeader: ListViewItemHeader?
|
||||
@ -528,7 +528,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis
|
||||
if !topPeers.isEmpty {
|
||||
let hasDeselectAll = !(selectionState?.selectedPeerIndices ?? [:]).isEmpty
|
||||
|
||||
let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text(strings.Premium_Gift_ContactSelection_FrequentContacts.uppercased(), AnyHashable(hasDeselectAll ? 1 : 0)), theme: theme, strings: strings, actionTitle: hasDeselectAll ? strings.Premium_Gift_ContactSelection_DeselectAll.uppercased() : nil, action: {
|
||||
let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text(topSectionTitle ?? strings.Premium_Gift_ContactSelection_FrequentContacts.uppercased(), AnyHashable(hasDeselectAll ? 1 : 0)), theme: theme, strings: strings, actionTitle: hasDeselectAll ? strings.Premium_Gift_ContactSelection_DeselectAll.uppercased() : nil, action: {
|
||||
interaction.deselectAll()
|
||||
})
|
||||
|
||||
@ -722,8 +722,14 @@ public enum ContactListPresentation {
|
||||
}
|
||||
}
|
||||
|
||||
public enum TopPeers {
|
||||
case none
|
||||
case recent
|
||||
case custom(title: String, peerIds: [EnginePeer.Id])
|
||||
}
|
||||
|
||||
case orderedByPresence(options: [ContactListAdditionalOption])
|
||||
case natural(options: [ContactListAdditionalOption], includeChatList: Bool, topPeers: Bool)
|
||||
case natural(options: [ContactListAdditionalOption], includeChatList: Bool, topPeers: TopPeers)
|
||||
case search(Search)
|
||||
|
||||
public var sortOrder: ContactsSortOrder? {
|
||||
@ -1110,11 +1116,11 @@ public final class ContactListNode: ASDisplayNode {
|
||||
|> mapToSignal { presentation in
|
||||
var generateSections = false
|
||||
var includeChatList = false
|
||||
var displayTopPeers = false
|
||||
if case let .natural(_, includeChatListValue, displayTopPeersValue) = presentation {
|
||||
var displayTopPeers: ContactListPresentation.TopPeers = .none
|
||||
if case let .natural(_, includeChatListValue, topPeersValue) = presentation {
|
||||
generateSections = true
|
||||
includeChatList = includeChatListValue
|
||||
displayTopPeers = displayTopPeersValue
|
||||
displayTopPeers = topPeersValue
|
||||
}
|
||||
|
||||
if case let .search(search) = presentation {
|
||||
@ -1421,7 +1427,7 @@ public final class ContactListNode: ASDisplayNode {
|
||||
peers.append(.deviceContact(stableId, contact.0))
|
||||
}
|
||||
|
||||
let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: localPeersAndStatuses.1, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, peerRequiresPremiumForMessaging: peerRequiresPremiumForMessaging, authorizationStatus: .allowed, warningSuppressed: (true, true), displaySortOptions: false, displayCallIcons: displayCallIcons, storySubscriptions: nil, topPeers: [], interaction: interaction)
|
||||
let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: localPeersAndStatuses.1, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, peerRequiresPremiumForMessaging: peerRequiresPremiumForMessaging, authorizationStatus: .allowed, warningSuppressed: (true, true), displaySortOptions: false, displayCallIcons: displayCallIcons, storySubscriptions: nil, topPeers: [], topSectionTitle: nil, interaction: interaction)
|
||||
let previous = previousEntries.swap(entries)
|
||||
return .single(preparedContactListNodeTransition(context: context, presentationData: presentationData, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: false, generateIndexSections: generateSections, animation: .none, isSearch: isSearch))
|
||||
}
|
||||
@ -1481,11 +1487,36 @@ public final class ContactListNode: ASDisplayNode {
|
||||
chatListSignal = .single([])
|
||||
}
|
||||
|
||||
let recentPeers: Signal<RecentPeers, NoError>
|
||||
if displayTopPeers {
|
||||
recentPeers = context.engine.peers.recentPeers()
|
||||
} else {
|
||||
recentPeers = .single(.disabled)
|
||||
let topPeers: Signal<[EnginePeer], NoError>
|
||||
let topPeersSectionTitle: String?
|
||||
switch displayTopPeers {
|
||||
case .recent:
|
||||
topPeers = context.engine.peers.recentPeers()
|
||||
|> map { recentPeers -> [EnginePeer] in
|
||||
var topPeers: [EnginePeer] = []
|
||||
if case let .peers(peers) = recentPeers {
|
||||
topPeers = peers.map(EnginePeer.init)
|
||||
}
|
||||
return topPeers
|
||||
}
|
||||
topPeersSectionTitle = nil
|
||||
case let .custom(title, peerIds):
|
||||
topPeers = context.engine.data.get(
|
||||
EngineDataMap(peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)))
|
||||
)
|
||||
|> map { peers in
|
||||
var result: [EnginePeer] = []
|
||||
for peer in peers.values {
|
||||
if let peer {
|
||||
result.append(peer)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
topPeersSectionTitle = title
|
||||
case .none:
|
||||
topPeers = .single([])
|
||||
topPeersSectionTitle = nil
|
||||
}
|
||||
|
||||
return (combineLatest(
|
||||
@ -1497,9 +1528,9 @@ public final class ContactListNode: ASDisplayNode {
|
||||
contactsAuthorization.get(),
|
||||
contactsWarningSuppressed.get(),
|
||||
self.storySubscriptions.get(),
|
||||
recentPeers
|
||||
topPeers
|
||||
)
|
||||
|> mapToQueue { view, chatListPeers, selectionState, pendingRemovalPeerIds, presentationData, authorizationStatus, warningSuppressed, storySubscriptions, recentPeers -> Signal<ContactsListNodeTransition, NoError> in
|
||||
|> mapToQueue { view, chatListPeers, selectionState, pendingRemovalPeerIds, presentationData, authorizationStatus, warningSuppressed, storySubscriptions, topPeers -> Signal<ContactsListNodeTransition, NoError> in
|
||||
let signal = deferred { () -> Signal<ContactsListNodeTransition, NoError> in
|
||||
if !view.2.isEmpty {
|
||||
context.account.viewTracker.refreshCanSendMessagesForPeerIds(peerIds: Array(view.2.keys))
|
||||
@ -1540,16 +1571,11 @@ public final class ContactListNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
var topPeers: [EnginePeer] = []
|
||||
if case let .peers(peers) = recentPeers {
|
||||
topPeers = peers.map(EnginePeer.init)
|
||||
}
|
||||
|
||||
var isEmpty = false
|
||||
if (authorizationStatus == .notDetermined || authorizationStatus == .denied) && peers.isEmpty {
|
||||
isEmpty = true
|
||||
}
|
||||
let entries = contactListNodeEntries(accountPeer: view.1, peers: peers, presences: view.0.presences, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, peerRequiresPremiumForMessaging: view.2, authorizationStatus: authorizationStatus, warningSuppressed: warningSuppressed, displaySortOptions: displaySortOptions, displayCallIcons: displayCallIcons, storySubscriptions: storySubscriptions, topPeers: topPeers, interaction: interaction)
|
||||
let entries = contactListNodeEntries(accountPeer: view.1, peers: peers, presences: view.0.presences, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, peerRequiresPremiumForMessaging: view.2, authorizationStatus: authorizationStatus, warningSuppressed: warningSuppressed, displaySortOptions: displaySortOptions, displayCallIcons: displayCallIcons, storySubscriptions: storySubscriptions, topPeers: topPeers, topSectionTitle: topPeersSectionTitle, interaction: interaction)
|
||||
let previous = previousEntries.swap(entries)
|
||||
let previousSelection = previousSelectionState.swap(selectionState)
|
||||
let previousPendingRemovalPeerIds = previousPendingRemovalPeerIds.swap(pendingRemovalPeerIds)
|
||||
|
@ -108,7 +108,7 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
case .presence:
|
||||
return .orderedByPresence(options: options)
|
||||
case .natural:
|
||||
return .natural(options: options, includeChatList: false, topPeers: false)
|
||||
return .natural(options: options, includeChatList: false, topPeers: .none)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,8 +48,6 @@ public enum ContextMenuActionItemTextColor {
|
||||
public enum ContextMenuActionResult {
|
||||
case `default`
|
||||
case dismissWithoutContent
|
||||
/// Temporary
|
||||
static var safeStreamRecordingDismissWithoutContent: ContextMenuActionResult { .dismissWithoutContent }
|
||||
|
||||
case custom(ContainedViewLayoutTransition)
|
||||
}
|
||||
@ -116,9 +114,11 @@ public final class ContextMenuActionItem {
|
||||
|
||||
public struct IconAnimation: Equatable {
|
||||
public var name: String
|
||||
public var loop: Bool
|
||||
|
||||
public init(name: String) {
|
||||
public init(name: String, loop: Bool = false) {
|
||||
self.name = name
|
||||
self.loop = loop
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ public protocol ContextControllerActionsStackItem: AnyObject {
|
||||
var dismissed: (() -> Void)? { get }
|
||||
}
|
||||
|
||||
protocol ContextControllerActionsListItemNode: ASDisplayNode {
|
||||
public protocol ContextControllerActionsListItemNode: ASDisplayNode {
|
||||
func update(presentationData: PresentationData, constrainedSize: CGSize) -> (minSize: CGSize, apply: (_ size: CGSize, _ transition: ContainedViewLayoutTransition) -> Void)
|
||||
|
||||
func canBeHighlighted() -> Bool
|
||||
@ -82,7 +82,7 @@ protocol ContextControllerActionsListItemNode: ASDisplayNode {
|
||||
func performAction()
|
||||
}
|
||||
|
||||
private final class ContextControllerActionsListActionItemNode: HighlightTrackingButtonNode, ContextControllerActionsListItemNode {
|
||||
public final class ContextControllerActionsListActionItemNode: HighlightTrackingButtonNode, ContextControllerActionsListItemNode {
|
||||
private let getController: () -> ContextControllerProtocol?
|
||||
private let requestDismiss: (ContextMenuActionResult) -> Void
|
||||
private let requestUpdateAction: (AnyHashable, ContextMenuActionItem) -> Void
|
||||
@ -103,7 +103,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
|
||||
|
||||
private var iconDisposable: Disposable?
|
||||
|
||||
init(
|
||||
public init(
|
||||
getController: @escaping () -> ContextControllerProtocol?,
|
||||
requestDismiss: @escaping (ContextMenuActionResult) -> Void,
|
||||
requestUpdateAction: @escaping (AnyHashable, ContextMenuActionItem) -> Void,
|
||||
@ -168,7 +168,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
|
||||
self.iconDisposable?.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
public override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.view.isExclusiveTouch = true
|
||||
@ -196,19 +196,19 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
|
||||
))
|
||||
}
|
||||
|
||||
func canBeHighlighted() -> Bool {
|
||||
public func canBeHighlighted() -> Bool {
|
||||
return self.item.action != nil
|
||||
}
|
||||
|
||||
func updateIsHighlighted(isHighlighted: Bool) {
|
||||
public func updateIsHighlighted(isHighlighted: Bool) {
|
||||
self.highlightBackgroundNode.alpha = isHighlighted ? 1.0 : 0.0
|
||||
}
|
||||
|
||||
func performAction() {
|
||||
public func performAction() {
|
||||
self.pressed()
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if self.titleLabelNode.tapAttributeAction != nil {
|
||||
if let result = self.titleLabelNode.hitTest(self.view.convert(point, to: self.titleLabelNode.view), with: event) {
|
||||
return result
|
||||
@ -223,7 +223,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
|
||||
self.accessibilityLabel = item.text
|
||||
}
|
||||
|
||||
func update(presentationData: PresentationData, constrainedSize: CGSize) -> (minSize: CGSize, apply: (_ size: CGSize, _ transition: ContainedViewLayoutTransition) -> Void) {
|
||||
public func update(presentationData: PresentationData, constrainedSize: CGSize) -> (minSize: CGSize, apply: (_ size: CGSize, _ transition: ContainedViewLayoutTransition) -> Void) {
|
||||
let sideInset: CGFloat = 16.0
|
||||
let verticalInset: CGFloat = 11.0
|
||||
let titleSubtitleSpacing: CGFloat = 1.0
|
||||
@ -365,8 +365,8 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
|
||||
component: AnyComponent(LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(name: iconAnimation.name),
|
||||
color: titleColor,
|
||||
startingPosition: .end,
|
||||
loop: false
|
||||
startingPosition: iconAnimation.loop ? .begin : .end,
|
||||
loop: iconAnimation.loop
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: animatedIconSize
|
||||
|
@ -719,7 +719,7 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll
|
||||
|
||||
@objc private func createActionButtonPressed() {
|
||||
var proceedImpl: ((String, String?) -> Void)?
|
||||
let titleController = stickerPackEditTitleController(context: self.context, title: self.presentationData.strings.ImportStickerPack_ChooseName, text: self.presentationData.strings.ImportStickerPack_ChooseNameDescription, placeholder: self.presentationData.strings.ImportStickerPack_NamePlaceholder, value: nil, maxLength: 128, apply: { [weak self] title in
|
||||
let titleController = stickerPackEditTitleController(context: self.context, title: self.presentationData.strings.ImportStickerPack_ChooseName, text: self.presentationData.strings.ImportStickerPack_ChooseNameDescription, placeholder: self.presentationData.strings.ImportStickerPack_NamePlaceholder, value: nil, maxLength: 64, apply: { [weak self] title in
|
||||
if let strongSelf = self, let title = title {
|
||||
strongSelf.shortNameSuggestionDisposable.set((strongSelf.context.engine.stickers.getStickerSetShortNameSuggestion(title: title)
|
||||
|> deliverOnMainQueue).start(next: { suggestedShortName in
|
||||
@ -735,7 +735,7 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let controller = importStickerPackShortNameController(context: strongSelf.context, title: strongSelf.presentationData.strings.ImportStickerPack_ChooseLink, text: strongSelf.presentationData.strings.ImportStickerPack_ChooseLinkDescription, placeholder: "", value: suggestedShortName, maxLength: 60, existingAlertController: titleController, apply: { [weak self] shortName in
|
||||
let controller = importStickerPackShortNameController(context: strongSelf.context, title: strongSelf.presentationData.strings.ImportStickerPack_ChooseLink, text: strongSelf.presentationData.strings.ImportStickerPack_ChooseLinkDescription, placeholder: "", value: suggestedShortName, maxLength: 64, existingAlertController: titleController, apply: { [weak self] shortName in
|
||||
if let shortName = shortName {
|
||||
self?.createStickerSet(title: title, shortName: shortName)
|
||||
}
|
||||
|
@ -488,8 +488,8 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
||||
var thumbnailItem: StickerPackThumbnailItem?
|
||||
var resourceReference: MediaResourceReference?
|
||||
if let thumbnail = item.packInfo.thumbnail {
|
||||
if item.packInfo.flags.contains(.isAnimated) || item.packInfo.flags.contains(.isVideo) {
|
||||
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions, item.packInfo.flags.contains(.isVideo), item.packInfo.flags.contains(.isCustomTemplateEmoji))
|
||||
if thumbnail.typeHint != .generic {
|
||||
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions, thumbnail.typeHint == .video, item.packInfo.flags.contains(.isCustomTemplateEmoji))
|
||||
} else {
|
||||
thumbnailItem = .still(thumbnail)
|
||||
}
|
||||
@ -844,7 +844,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
||||
var imageSize = PixelDimensions(width: 512, height: 512)
|
||||
var immediateThumbnailData: Data?
|
||||
if let data = item.packInfo.immediateThumbnailData {
|
||||
if item.packInfo.flags.contains(.isVideo) {
|
||||
if item.packInfo.thumbnail?.typeHint == .video || item.topItem?.file.isVideoSticker == true {
|
||||
imageSize = PixelDimensions(width: 100, height: 100)
|
||||
}
|
||||
immediateThumbnailData = data
|
||||
|
@ -2248,7 +2248,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
if !items.isEmpty {
|
||||
items.append(.separator)
|
||||
}
|
||||
items.append(.action(ContextMenuActionItem(text: hasGeneric ? strings.Attachment_EnableSpoiler : strings.Attachment_DisableSpoiler, icon: { _ in return nil }, animationName: "anim_spoiler", action: { [weak self] _, f in
|
||||
items.append(.action(ContextMenuActionItem(text: hasGeneric ? strings.Attachment_EnableSpoiler : strings.Attachment_DisableSpoiler, icon: { _ in return nil }, iconAnimation: ContextMenuActionItem.IconAnimation(
|
||||
name: "anim_spoiler",
|
||||
loop: true
|
||||
), action: { [weak self] _, f in
|
||||
f(.default)
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
@ -332,6 +332,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
public var displayTail: Bool = true
|
||||
public var forceTailToRight: Bool = false
|
||||
public var forceDark: Bool = false
|
||||
public var hideBackground: Bool = false
|
||||
|
||||
private var didAnimateIn: Bool = false
|
||||
public private(set) var isAnimatingOut: Bool = false
|
||||
@ -1900,7 +1901,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
externalExpansionView: self.view,
|
||||
customContentView: nil,
|
||||
useOpaqueTheme: false,
|
||||
hideBackground: false,
|
||||
hideBackground: self.hideBackground,
|
||||
stateContext: nil,
|
||||
addImage: nil
|
||||
)
|
||||
|
@ -297,8 +297,8 @@ public func preloadedStickerPackThumbnail(account: Account, info: StickerPackCol
|
||||
let signal = Signal<Bool, NoError> { subscriber in
|
||||
let fetched = fetchedMediaResource(mediaBox: account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)).start()
|
||||
let dataDisposable: Disposable
|
||||
if info.flags.contains(.isAnimated) || info.flags.contains(.isVideo) {
|
||||
dataDisposable = chatMessageAnimationData(mediaBox: account.postbox.mediaBox, resource: thumbnail.resource, isVideo: info.flags.contains(.isVideo), width: 80, height: 80, synchronousLoad: false).start(next: { data in
|
||||
if thumbnail.typeHint != .generic {
|
||||
dataDisposable = chatMessageAnimationData(mediaBox: account.postbox.mediaBox, resource: thumbnail.resource, isVideo: thumbnail.typeHint == .video, width: 80, height: 80, synchronousLoad: false).start(next: { data in
|
||||
if data.complete {
|
||||
subscriber.putNext(true)
|
||||
subscriber.putCompletion()
|
||||
|
@ -1140,7 +1140,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
|
||||
private let stickerPickerInputData = Promise<StickerPickerInput>()
|
||||
private func presentAddStickerOptions() {
|
||||
|
||||
//TODO:localize
|
||||
let actionSheet = ActionSheetController(presentationData: self.presentationData)
|
||||
var items: [ActionSheetItem] = []
|
||||
items.append(ActionSheetButtonItem(title: "Create a New Sticker", color: .accent, action: { [weak actionSheet, weak self] in
|
||||
@ -1207,7 +1207,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
context: context,
|
||||
source: result,
|
||||
transitionArguments: (transitionView, transitionRect, transitionImage),
|
||||
completion: { file in
|
||||
completion: { file, commit in
|
||||
dismissImpl?()
|
||||
let sticker = ImportSticker(
|
||||
resource: file.resource,
|
||||
@ -1219,6 +1219,8 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash)
|
||||
let _ = (context.engine.stickers.addStickerToStickerSet(packReference: packReference, sticker: sticker)
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
commit()
|
||||
|
||||
let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], parentNavigationController: navigationController, sendSticker: nil, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil)
|
||||
(navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root))
|
||||
|
||||
@ -1283,7 +1285,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
context: context,
|
||||
source: initialFile,
|
||||
transitionArguments: nil,
|
||||
completion: { file in
|
||||
completion: { file, commit in
|
||||
let sticker = ImportSticker(
|
||||
resource: file.resource,
|
||||
emojis: ["😀"],
|
||||
@ -1295,6 +1297,8 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
|
||||
let _ = (context.engine.stickers.replaceSticker(previousSticker: .stickerPack(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), media: initialFile), sticker: sticker)
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
commit()
|
||||
|
||||
let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], parentNavigationController: navigationController, sendSticker: nil, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil)
|
||||
(navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root))
|
||||
})
|
||||
@ -1310,7 +1314,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
let context = self.context
|
||||
//TODO:localize
|
||||
var dismissImpl: (() -> Void)?
|
||||
let controller = stickerPackEditTitleController(context: context, title: "Edit Sticker Set Name", text: "Choose a new name for your set.", placeholder: self.presentationData.strings.ImportStickerPack_NamePlaceholder, actionTitle: presentationData.strings.Common_Done, value: self.updatedTitle ?? info.title, maxLength: 128, apply: { [weak self] title in
|
||||
let controller = stickerPackEditTitleController(context: context, title: "Edit Sticker Set Name", text: "Choose a new name for your set.", placeholder: self.presentationData.strings.ImportStickerPack_NamePlaceholder, actionTitle: presentationData.strings.Common_Done, value: self.updatedTitle ?? info.title, maxLength: 64, apply: { [weak self] title in
|
||||
guard let self, let title else {
|
||||
return
|
||||
}
|
||||
|
@ -392,7 +392,7 @@ final class EmojiStickerAccessoryNode: SparseNode, PeekControllerAccessoryNode {
|
||||
items.append(reaction)
|
||||
}
|
||||
|
||||
let selectedItems = ValuePromise<Set<MessageReaction.Reaction>>()
|
||||
let selectedItems = ValuePromise<Set<MessageReaction.Reaction>>(Set())
|
||||
//TODO:localize
|
||||
let reactionContextNode = ReactionContextNode(
|
||||
context: self.context,
|
||||
@ -440,6 +440,7 @@ final class EmojiStickerAccessoryNode: SparseNode, PeekControllerAccessoryNode {
|
||||
layoutImpl?(transition)
|
||||
}
|
||||
)
|
||||
reactionContextNode.hideBackground = true
|
||||
reactionContextNode.displayTail = true
|
||||
reactionContextNode.forceTailToRight = true
|
||||
reactionContextNode.forceDark = true
|
||||
|
@ -21,12 +21,6 @@ public struct StickerPackCollectionInfoFlags: OptionSet {
|
||||
if flags.contains(StickerPackCollectionInfoFlags.isOfficial) {
|
||||
rawValue |= StickerPackCollectionInfoFlags.isOfficial.rawValue
|
||||
}
|
||||
if flags.contains(StickerPackCollectionInfoFlags.isAnimated) {
|
||||
rawValue |= StickerPackCollectionInfoFlags.isAnimated.rawValue
|
||||
}
|
||||
if flags.contains(StickerPackCollectionInfoFlags.isVideo) {
|
||||
rawValue |= StickerPackCollectionInfoFlags.isVideo.rawValue
|
||||
}
|
||||
if flags.contains(StickerPackCollectionInfoFlags.isEmoji) {
|
||||
rawValue |= StickerPackCollectionInfoFlags.isEmoji.rawValue
|
||||
}
|
||||
@ -39,8 +33,6 @@ public struct StickerPackCollectionInfoFlags: OptionSet {
|
||||
|
||||
public static let isMasks = StickerPackCollectionInfoFlags(rawValue: 1 << 0)
|
||||
public static let isOfficial = StickerPackCollectionInfoFlags(rawValue: 1 << 1)
|
||||
public static let isAnimated = StickerPackCollectionInfoFlags(rawValue: 1 << 2)
|
||||
public static let isVideo = StickerPackCollectionInfoFlags(rawValue: 1 << 3)
|
||||
public static let isEmoji = StickerPackCollectionInfoFlags(rawValue: 1 << 4)
|
||||
public static let isAvailableAsChannelStatus = StickerPackCollectionInfoFlags(rawValue: 1 << 5)
|
||||
public static let isCustomTemplateEmoji = StickerPackCollectionInfoFlags(rawValue: 1 << 6)
|
||||
|
@ -360,20 +360,36 @@ public final class TelegramMediaImage: Media, Equatable, Codable {
|
||||
}
|
||||
|
||||
public final class TelegramMediaImageRepresentation: PostboxCoding, Equatable, CustomStringConvertible {
|
||||
public enum TypeHint: Int32 {
|
||||
case generic
|
||||
case animated
|
||||
case video
|
||||
}
|
||||
|
||||
public let dimensions: PixelDimensions
|
||||
public let resource: TelegramMediaResource
|
||||
public let progressiveSizes: [Int32]
|
||||
public let immediateThumbnailData: Data?
|
||||
public let hasVideo: Bool
|
||||
public let isPersonal: Bool
|
||||
public let typeHint: TypeHint
|
||||
|
||||
public init(dimensions: PixelDimensions, resource: TelegramMediaResource, progressiveSizes: [Int32], immediateThumbnailData: Data?, hasVideo: Bool, isPersonal: Bool) {
|
||||
public init(
|
||||
dimensions: PixelDimensions,
|
||||
resource: TelegramMediaResource,
|
||||
progressiveSizes: [Int32],
|
||||
immediateThumbnailData: Data?,
|
||||
hasVideo: Bool = false,
|
||||
isPersonal: Bool = false,
|
||||
typeHint: TypeHint = .generic
|
||||
) {
|
||||
self.dimensions = dimensions
|
||||
self.resource = resource
|
||||
self.progressiveSizes = progressiveSizes
|
||||
self.immediateThumbnailData = immediateThumbnailData
|
||||
self.hasVideo = hasVideo
|
||||
self.isPersonal = isPersonal
|
||||
self.typeHint = typeHint
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
@ -383,6 +399,7 @@ public final class TelegramMediaImageRepresentation: PostboxCoding, Equatable, C
|
||||
self.immediateThumbnailData = decoder.decodeDataForKey("th")
|
||||
self.hasVideo = decoder.decodeBoolForKey("hv", orElse: false)
|
||||
self.isPersonal = decoder.decodeBoolForKey("ip", orElse: false)
|
||||
self.typeHint = TypeHint(rawValue: decoder.decodeInt32ForKey("th", orElse: 0)) ?? .generic
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
@ -397,6 +414,7 @@ public final class TelegramMediaImageRepresentation: PostboxCoding, Equatable, C
|
||||
}
|
||||
encoder.encodeBool(self.hasVideo, forKey: "hv")
|
||||
encoder.encodeBool(self.isPersonal, forKey: "ip")
|
||||
encoder.encodeInt32(self.typeHint.rawValue, forKey: "th")
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
@ -422,6 +440,9 @@ public final class TelegramMediaImageRepresentation: PostboxCoding, Equatable, C
|
||||
if self.isPersonal != other.isPersonal {
|
||||
return false
|
||||
}
|
||||
if self.typeHint != other.typeHint {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ func _internal_createStickerSet(account: Account, title: String, shortName: Stri
|
||||
flags |= (1 << 1)
|
||||
}
|
||||
|
||||
inputStickers.append(.inputStickerSetItem(flags: flags, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), emoji: sticker.emojis.first ?? "", maskCoords: nil, keywords: sticker.keywords))
|
||||
inputStickers.append(.inputStickerSetItem(flags: flags, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), emoji: sticker.emojis.joined(), maskCoords: nil, keywords: sticker.keywords))
|
||||
}
|
||||
var thumbnailDocument: Api.InputDocument?
|
||||
if thumbnail != nil, let resource = resources.last {
|
||||
@ -307,7 +307,7 @@ func _internal_addStickerToStickerSet(account: Account, packReference: StickerPa
|
||||
if sticker.keywords.count > 0 {
|
||||
flags |= (1 << 1)
|
||||
}
|
||||
let inputSticker: Api.InputStickerSetItem = .inputStickerSetItem(flags: flags, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), emoji: sticker.emojis.first ?? "", maskCoords: nil, keywords: sticker.keywords)
|
||||
let inputSticker: Api.InputStickerSetItem = .inputStickerSetItem(flags: flags, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), emoji: sticker.emojis.joined(), maskCoords: nil, keywords: sticker.keywords)
|
||||
|
||||
return account.network.request(Api.functions.stickers.addStickerToSet(stickerset: packReference.apiInputStickerSet, sticker: inputSticker))
|
||||
|> mapError { error -> AddStickerToSetError in
|
||||
@ -416,7 +416,7 @@ func _internal_replaceSticker(account: Account, previousSticker: FileMediaRefere
|
||||
if sticker.keywords.count > 0 {
|
||||
flags |= (1 << 1)
|
||||
}
|
||||
let inputSticker: Api.InputStickerSetItem = .inputStickerSetItem(flags: flags, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), emoji: sticker.emojis.first ?? "", maskCoords: nil, keywords: sticker.keywords)
|
||||
let inputSticker: Api.InputStickerSetItem = .inputStickerSetItem(flags: flags, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), emoji: sticker.emojis.joined(), maskCoords: nil, keywords: sticker.keywords)
|
||||
|
||||
return account.network.request(Api.functions.stickers.replaceSticker(sticker: .inputDocument(id: previousResource.fileId, accessHash: previousResource.accessHash, fileReference: Buffer(data: previousResource.fileReference ?? Data())), newSticker: inputSticker))
|
||||
|> mapError { error -> ReplaceStickerError in
|
||||
|
@ -5,19 +5,31 @@ import SwiftSignalKit
|
||||
import MtProtoKit
|
||||
|
||||
func telegramStickerPackThumbnailRepresentationFromApiSizes(datacenterId: Int32, thumbVersion: Int32?, sizes: [Api.PhotoSize]) -> (immediateThumbnail: Data?, representations: [TelegramMediaImageRepresentation]) {
|
||||
func stickerTypeHint(for type: String) -> TelegramMediaImageRepresentation.TypeHint {
|
||||
switch type {
|
||||
case "s":
|
||||
return .generic
|
||||
case "a":
|
||||
return .animated
|
||||
case "v":
|
||||
return .video
|
||||
default:
|
||||
return .generic
|
||||
}
|
||||
}
|
||||
var immediateThumbnailData: Data?
|
||||
var representations: [TelegramMediaImageRepresentation] = []
|
||||
for size in sizes {
|
||||
switch size {
|
||||
case let .photoCachedSize(_, w, h, _):
|
||||
case let .photoCachedSize(type, w, h, _):
|
||||
let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, thumbVersion: thumbVersion, volumeId: nil, localId: nil)
|
||||
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
||||
case let .photoSize(_, w, h, _):
|
||||
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, typeHint: stickerTypeHint(for: type)))
|
||||
case let .photoSize(type, w, h, _):
|
||||
let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, thumbVersion: thumbVersion, volumeId: nil, localId: nil)
|
||||
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
||||
case let .photoSizeProgressive(_, w, h, sizes):
|
||||
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, typeHint: stickerTypeHint(for: type)))
|
||||
case let .photoSizeProgressive(type, w, h, sizes):
|
||||
let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, thumbVersion: thumbVersion, volumeId: nil, localId: nil)
|
||||
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: sizes, immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
||||
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: sizes, immediateThumbnailData: nil, typeHint: stickerTypeHint(for: type)))
|
||||
case let .photoPathSize(_, data):
|
||||
immediateThumbnailData = data.makeData()
|
||||
case .photoStrippedSize:
|
||||
@ -40,12 +52,6 @@ extension StickerPackCollectionInfo {
|
||||
if (flags & (1 << 3)) != 0 {
|
||||
setFlags.insert(.isMasks)
|
||||
}
|
||||
if (flags & (1 << 5)) != 0 {
|
||||
setFlags.insert(.isAnimated)
|
||||
}
|
||||
if (flags & (1 << 6)) != 0 {
|
||||
setFlags.insert(.isVideo)
|
||||
}
|
||||
if (flags & (1 << 7)) != 0 {
|
||||
setFlags.insert(.isEmoji)
|
||||
}
|
||||
|
@ -1162,11 +1162,6 @@ public extension EmojiPagerContentComponent {
|
||||
}
|
||||
} else if case .stickerAlt = subject {
|
||||
for reactionItem in topReactionItems {
|
||||
// if existingIds.contains(reactionItem.reaction) {
|
||||
// continue
|
||||
// }
|
||||
// existingIds.insert(reactionItem.reaction)
|
||||
|
||||
let icon: EmojiPagerContentComponent.Item.Icon
|
||||
if case .reaction(onlyTop: true) = subject {
|
||||
icon = .none
|
||||
|
@ -5676,11 +5676,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
if let navigationController = self.navigationController as? NavigationController {
|
||||
navigationController.updateRootContainerTransitionOffset(0.0, transition: .immediate)
|
||||
}
|
||||
|
||||
// mediaEditor.stop()
|
||||
// mediaEditor.invalidate()
|
||||
// self.node.entitiesView.invalidate()
|
||||
|
||||
|
||||
let entities = self.node.entitiesView.entities.filter { !($0 is DrawingMediaEntity) }
|
||||
let codableEntities = DrawingEntitiesView.encodeEntities(entities, entitiesView: self.node.entitiesView)
|
||||
mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities)
|
||||
@ -5772,36 +5768,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
}
|
||||
})))
|
||||
|
||||
let thumbSize = CGSize(width: 24.0, height: 24.0)
|
||||
for (pack, firstItem) in self.myStickerPacks {
|
||||
let thumbnailResource = pack.thumbnail?.resource ?? firstItem?.file.resource
|
||||
let thumbnailIconSource: ContextMenuActionItemIconSource?
|
||||
if let thumbnailResource {
|
||||
var resourceId: Int64 = 0
|
||||
if let resource = thumbnailResource as? CloudDocumentMediaResource {
|
||||
resourceId = resource.fileId
|
||||
}
|
||||
let thumbnailFile = firstItem?.file ?? TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: resourceId), partialReference: nil, resource: thumbnailResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "image/webp", size: thumbnailResource.size ?? 0, attributes: [])
|
||||
|
||||
let _ = freeMediaFileInteractiveFetched(account: self.context.account, userLocation: .other, fileReference: .stickerPack(stickerPack: .id(id: pack.id.id, accessHash: pack.accessHash), media: thumbnailFile)).start()
|
||||
thumbnailIconSource = ContextMenuActionItemIconSource(
|
||||
size: thumbSize,
|
||||
signal: chatMessageStickerPackThumbnail(postbox: self.context.account.postbox, resource: thumbnailResource)
|
||||
|> map { generator -> UIImage? in
|
||||
return generator(TransformImageArguments(corners: ImageCorners(), imageSize: thumbSize, boundingSize: thumbSize, intrinsicInsets: .zero))?.generateImage()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
thumbnailIconSource = nil
|
||||
contextItems.append(.custom(StickerPackListContextItem(context: self.context, packs: self.myStickerPacks, packSelected: { [weak self] pack in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
contextItems.append(.action(ContextMenuActionItem(text: pack.title, icon: { _ in return nil }, iconSource: thumbnailIconSource, iconPosition: .left, action: { [weak self] _, f in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
f(.default)
|
||||
self.uploadSticker(file, action: .addToStickerPack(pack: .id(id: pack.id.id, accessHash: pack.accessHash), title: pack.title))
|
||||
})))
|
||||
}
|
||||
self.uploadSticker(file, action: .addToStickerPack(pack: .id(id: pack.id.id, accessHash: pack.accessHash), title: pack.title))
|
||||
}), false))
|
||||
|
||||
let items = ContextController.Items(
|
||||
id: 1,
|
||||
@ -5877,7 +5849,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||
|
||||
var dismissImpl: (() -> Void)?
|
||||
let controller = stickerPackEditTitleController(context: self.context, forceDark: true, title: "New Sticker Set", text: "Choose a name for your sticker set.", placeholder: presentationData.strings.ImportStickerPack_NamePlaceholder, actionTitle: presentationData.strings.Common_Done, value: nil, maxLength: 128, apply: { [weak self] title in
|
||||
let controller = stickerPackEditTitleController(context: self.context, forceDark: true, title: "New Sticker Set", text: "Choose a name for your sticker set.", placeholder: presentationData.strings.ImportStickerPack_NamePlaceholder, actionTitle: presentationData.strings.Common_Done, value: nil, maxLength: 64, apply: { [weak self] title in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@ -5972,7 +5944,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
case let .createStickerPack(title):
|
||||
let sticker = ImportSticker(
|
||||
resource: resource,
|
||||
emojis: ["😀"],
|
||||
emojis: ["😀😂"],
|
||||
dimensions: dimensions,
|
||||
mimeType: "image/webp",
|
||||
keywords: ""
|
||||
@ -5991,7 +5963,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
case let .addToStickerPack(pack, _):
|
||||
let sticker = ImportSticker(
|
||||
resource: resource,
|
||||
emojis: ["😀"],
|
||||
emojis: ["😀😂"],
|
||||
dimensions: dimensions,
|
||||
mimeType: "image/webp",
|
||||
keywords: ""
|
||||
|
@ -0,0 +1,191 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import AccountContext
|
||||
import TelegramPresentationData
|
||||
import StickerResources
|
||||
import ContextUI
|
||||
|
||||
final class StickerPackListContextItem: ContextMenuCustomItem {
|
||||
let context: AccountContext
|
||||
let packs: [(StickerPackCollectionInfo, StickerPackItem?)]
|
||||
let packSelected: (StickerPackCollectionInfo) -> Void
|
||||
|
||||
init(context: AccountContext, packs: [(StickerPackCollectionInfo, StickerPackItem?)], packSelected: @escaping (StickerPackCollectionInfo) -> Void) {
|
||||
self.context = context
|
||||
self.packs = packs
|
||||
self.packSelected = packSelected
|
||||
}
|
||||
|
||||
func node(presentationData: PresentationData, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) -> ContextMenuCustomNode {
|
||||
return StickerPackListContextItemNode(presentationData: presentationData, item: self, getController: getController, actionSelected: actionSelected)
|
||||
}
|
||||
}
|
||||
|
||||
private final class StickerPackListContextItemNode: ASDisplayNode, ContextMenuCustomNode, ContextActionNodeProtocol, UIScrollViewDelegate {
|
||||
private let item: StickerPackListContextItem
|
||||
private let presentationData: PresentationData
|
||||
private let getController: () -> ContextControllerProtocol?
|
||||
private let actionSelected: (ContextMenuActionResult) -> Void
|
||||
|
||||
private let scrollNode: ASScrollNode
|
||||
private let actionNodes: [ContextControllerActionsListActionItemNode]
|
||||
private let separatorNodes: [ASDisplayNode]
|
||||
|
||||
init(presentationData: PresentationData, item: StickerPackListContextItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) {
|
||||
self.item = item
|
||||
self.presentationData = presentationData
|
||||
self.getController = getController
|
||||
self.actionSelected = actionSelected
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
|
||||
var actionNodes: [ContextControllerActionsListActionItemNode] = []
|
||||
var separatorNodes: [ASDisplayNode] = []
|
||||
|
||||
var i = 0
|
||||
for (pack, topItem) in item.packs {
|
||||
let thumbSize = CGSize(width: 24.0, height: 24.0)
|
||||
let thumbnailResource = pack.thumbnail?.resource ?? topItem?.file.resource
|
||||
let thumbnailIconSource: ContextMenuActionItemIconSource?
|
||||
if let thumbnailResource {
|
||||
var resourceId: Int64 = 0
|
||||
if let resource = thumbnailResource as? CloudDocumentMediaResource {
|
||||
resourceId = resource.fileId
|
||||
}
|
||||
let thumbnailFile = topItem?.file ?? TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: resourceId), partialReference: nil, resource: thumbnailResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "image/webp", size: thumbnailResource.size ?? 0, attributes: [])
|
||||
|
||||
let _ = freeMediaFileInteractiveFetched(account: item.context.account, userLocation: .other, fileReference: .stickerPack(stickerPack: .id(id: pack.id.id, accessHash: pack.accessHash), media: thumbnailFile)).start()
|
||||
thumbnailIconSource = ContextMenuActionItemIconSource(
|
||||
size: thumbSize,
|
||||
signal: chatMessageStickerPackThumbnail(postbox: item.context.account.postbox, resource: thumbnailResource)
|
||||
|> map { generator -> UIImage? in
|
||||
return generator(TransformImageArguments(corners: ImageCorners(), imageSize: thumbSize, boundingSize: thumbSize, intrinsicInsets: .zero))?.generateImage()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
thumbnailIconSource = nil
|
||||
}
|
||||
|
||||
let action = ContextMenuActionItem(text: pack.title, textLayout: .singleLine, icon: { _ in nil }, iconSource: thumbnailIconSource, iconPosition: .left, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
item.packSelected(pack)
|
||||
})
|
||||
let actionNode = ContextControllerActionsListActionItemNode(getController: getController, requestDismiss: actionSelected, requestUpdateAction: { _, _ in }, item: action)
|
||||
actionNodes.append(actionNode)
|
||||
if actionNodes.count != item.packs.count {
|
||||
let separatorNode = ASDisplayNode()
|
||||
separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor
|
||||
separatorNodes.append(separatorNode)
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
self.actionNodes = actionNodes
|
||||
self.separatorNodes = separatorNodes
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.scrollNode)
|
||||
for separatorNode in self.separatorNodes {
|
||||
self.scrollNode.addSubnode(separatorNode)
|
||||
}
|
||||
for actionNode in self.actionNodes {
|
||||
self.scrollNode.addSubnode(actionNode)
|
||||
}
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.scrollNode.view.delegate = self
|
||||
self.scrollNode.view.alwaysBounceVertical = false
|
||||
self.scrollNode.view.showsHorizontalScrollIndicator = false
|
||||
self.scrollNode.view.scrollIndicatorInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 5.0, right: 0.0)
|
||||
}
|
||||
|
||||
func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
|
||||
let minActionsWidth: CGFloat = 250.0
|
||||
let maxActionsWidth: CGFloat = 300.0
|
||||
let constrainedWidth = min(constrainedWidth, maxActionsWidth)
|
||||
var maxWidth: CGFloat = 0.0
|
||||
var contentHeight: CGFloat = 0.0
|
||||
var heightsAndCompletions: [(CGFloat, (CGSize, ContainedViewLayoutTransition) -> Void)?] = []
|
||||
for i in 0 ..< self.actionNodes.count {
|
||||
let itemNode = self.actionNodes[i]
|
||||
let (minSize, complete) = itemNode.update(presentationData: self.presentationData, constrainedSize: CGSize(width: constrainedWidth, height: constrainedHeight))
|
||||
maxWidth = max(maxWidth, minSize.width)
|
||||
heightsAndCompletions.append((minSize.height, complete))
|
||||
contentHeight += minSize.height
|
||||
}
|
||||
|
||||
maxWidth = max(maxWidth, minActionsWidth)
|
||||
|
||||
let maxHeight: CGFloat = min(155.0, constrainedHeight - 108.0)
|
||||
|
||||
return (CGSize(width: maxWidth, height: min(maxHeight, contentHeight)), { size, transition in
|
||||
var verticalOffset: CGFloat = 0.0
|
||||
for i in 0 ..< heightsAndCompletions.count {
|
||||
let itemNode = self.actionNodes[i]
|
||||
if let (itemHeight, itemCompletion) = heightsAndCompletions[i] {
|
||||
let itemSize = CGSize(width: maxWidth, height: itemHeight)
|
||||
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: verticalOffset), size: itemSize))
|
||||
itemCompletion(itemSize, transition)
|
||||
verticalOffset += itemHeight
|
||||
}
|
||||
|
||||
if i < self.actionNodes.count - 1 {
|
||||
let separatorNode = self.separatorNodes[i]
|
||||
separatorNode.frame = CGRect(x: 0, y: verticalOffset, width: size.width, height: UIScreenPixel)
|
||||
}
|
||||
}
|
||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.scrollNode.view.contentSize = CGSize(width: size.width, height: contentHeight)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func updateTheme(presentationData: PresentationData) {
|
||||
// for actionNode in self.actionNodes {
|
||||
// actionNode.updateTheme(presentationData: presentationData)
|
||||
// }
|
||||
}
|
||||
|
||||
var isActionEnabled: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func performAction() {
|
||||
}
|
||||
|
||||
func setIsHighlighted(_ value: Bool) {
|
||||
}
|
||||
|
||||
func canBeHighlighted() -> Bool {
|
||||
return self.isActionEnabled
|
||||
}
|
||||
|
||||
func updateIsHighlighted(isHighlighted: Bool) {
|
||||
self.setIsHighlighted(isHighlighted)
|
||||
}
|
||||
|
||||
func actionNode(at point: CGPoint) -> ContextActionNodeProtocol {
|
||||
// for actionNode in self.actionNodes {
|
||||
// let frame = actionNode.convert(actionNode.bounds, to: self)
|
||||
// if frame.contains(point) {
|
||||
// return actionNode
|
||||
// }
|
||||
// }
|
||||
return self
|
||||
}
|
||||
|
||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
for actionNode in self.actionNodes {
|
||||
actionNode.updateIsHighlighted(isHighlighted: false)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
//
|
||||
// PeerInfoScreenBirthdatePickerItem.swift
|
||||
// MediaEditorScreen
|
||||
//
|
||||
// Created by Ilya Laktyushin on 15.03.2024.
|
||||
//
|
||||
|
||||
import Foundation
|
@ -38,9 +38,10 @@ final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem {
|
||||
let text: String
|
||||
let icon: UIImage?
|
||||
let iconSignal: Signal<UIImage?, NoError>?
|
||||
let hasArrow: Bool
|
||||
let action: (() -> Void)?
|
||||
|
||||
init(id: AnyHashable, label: Label = .none, additionalBadgeLabel: String? = nil, additionalBadgeIcon: UIImage? = nil, text: String, icon: UIImage? = nil, iconSignal: Signal<UIImage?, NoError>? = nil, action: (() -> Void)?) {
|
||||
init(id: AnyHashable, label: Label = .none, additionalBadgeLabel: String? = nil, additionalBadgeIcon: UIImage? = nil, text: String, icon: UIImage? = nil, iconSignal: Signal<UIImage?, NoError>? = nil, hasArrow: Bool = true, action: (() -> Void)?) {
|
||||
self.id = id
|
||||
self.label = label
|
||||
self.additionalBadgeLabel = additionalBadgeLabel
|
||||
@ -48,6 +49,7 @@ final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem {
|
||||
self.text = text
|
||||
self.icon = icon
|
||||
self.iconSignal = iconSignal
|
||||
self.hasArrow = hasArrow
|
||||
self.action = action
|
||||
}
|
||||
|
||||
@ -139,7 +141,7 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
let leftInset = (item.icon == nil && item.iconSignal == nil ? sideInset : sideInset + 29.0 + 16.0)
|
||||
let rightInset = sideInset + 18.0
|
||||
let rightInset = sideInset + (item.hasArrow ? 18.0 : 0.0)
|
||||
let separatorInset = item.icon == nil && item.iconSignal == nil ? sideInset : leftInset - 1.0
|
||||
let titleFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize)
|
||||
|
||||
@ -206,7 +208,7 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
self.iconNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if let arrowImage = PresentationResourcesItemList.disclosureArrowImage(presentationData.theme) {
|
||||
if item.hasArrow, let arrowImage = PresentationResourcesItemList.disclosureArrowImage(presentationData.theme) {
|
||||
self.arrowNode.image = arrowImage
|
||||
let arrowFrame = CGRect(origin: CGPoint(x: width - 7.0 - arrowImage.size.width - safeInsets.right, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
|
||||
transition.updateFrame(node: self.arrowNode, frame: arrowFrame)
|
||||
|
@ -1354,7 +1354,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
self.controller?.containerLayoutUpdated(layout, transition: .immediate)
|
||||
}
|
||||
} else {
|
||||
let contactListNode = ContactListNode(context: self.context, updatedPresentationData: self.updatedPresentationData, presentation: .single(.natural(options: [], includeChatList: false, topPeers: false)), onlyWriteable: self.filter.contains(.onlyWriteable))
|
||||
let contactListNode = ContactListNode(context: self.context, updatedPresentationData: self.updatedPresentationData, presentation: .single(.natural(options: [], includeChatList: false, topPeers: .none)), onlyWriteable: self.filter.contains(.onlyWriteable))
|
||||
self.contactListNode = contactListNode
|
||||
contactListNode.enableUpdates = true
|
||||
contactListNode.selectionStateUpdated = { [weak self] selectionState in
|
||||
|
@ -255,7 +255,7 @@ private final class ImportStickerPackTitleInputFieldNode: ASDisplayNode, UITextF
|
||||
|
||||
private let maxLength: Int
|
||||
|
||||
init(theme: PresentationTheme, placeholder: String, maxLength: Int, keyboardType: UIKeyboardType = .default, returnKeyType: UIReturnKeyType = .done) {
|
||||
init(theme: PresentationTheme, placeholder: String, maxLength: Int, keyboardType: UIKeyboardType = .default, returnKeyType: UIReturnKeyType = .done, hasClearButton: Bool = false) {
|
||||
self.theme = theme
|
||||
self.maxLength = maxLength
|
||||
|
||||
@ -370,6 +370,10 @@ private final class ImportStickerPackTitleInputFieldNode: ASDisplayNode, UITextF
|
||||
return false
|
||||
}
|
||||
|
||||
if string == " " && updatedText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
return false
|
||||
}
|
||||
|
||||
if self.textInputNode.keyboardType == .asciiCapable {
|
||||
var cleanString = string.folding(options: .diacriticInsensitive, locale: .current).replacingOccurrences(of: " ", with: "_")
|
||||
|
||||
@ -506,7 +510,7 @@ private final class ImportStickerPackTitleAlertContentNode: AlertContentNode {
|
||||
return self.isUserInteractionEnabled
|
||||
}
|
||||
|
||||
init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], title: String, text: String, placeholder: String, value: String?, maxLength: Int, asciiOnly: Bool = false) {
|
||||
init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], title: String, text: String, placeholder: String, value: String?, maxLength: Int, asciiOnly: Bool = false, hasClearButton: Bool) {
|
||||
self.strings = strings
|
||||
self.alertTheme = theme
|
||||
self.theme = ptheme
|
||||
@ -524,7 +528,7 @@ private final class ImportStickerPackTitleAlertContentNode: AlertContentNode {
|
||||
self.activityIndicator = ActivityIndicator(type: .custom(ptheme.rootController.navigationBar.secondaryTextColor, 20.0, 1.5, false), speed: .slow)
|
||||
self.activityIndicator.isHidden = true
|
||||
|
||||
self.inputFieldNode = ImportStickerPackTitleInputFieldNode(theme: ptheme, placeholder: placeholder, maxLength: maxLength, keyboardType: asciiOnly ? .asciiCapable : .default, returnKeyType: asciiOnly ? .done : .next)
|
||||
self.inputFieldNode = ImportStickerPackTitleInputFieldNode(theme: ptheme, placeholder: placeholder, maxLength: maxLength, keyboardType: asciiOnly ? .asciiCapable : .default, returnKeyType: asciiOnly ? .done : .next, hasClearButton: hasClearButton)
|
||||
if asciiOnly {
|
||||
self.inputFieldNode.prefix = "t.me/addstickers/"
|
||||
}
|
||||
@ -743,7 +747,7 @@ public func stickerPackEditTitleController(context: AccountContext, forceDark: B
|
||||
applyImpl?()
|
||||
})]
|
||||
|
||||
let contentNode = ImportStickerPackTitleAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, text: text, placeholder: placeholder, value: value, maxLength: maxLength)
|
||||
let contentNode = ImportStickerPackTitleAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, text: text, placeholder: placeholder, value: value, maxLength: maxLength, hasClearButton: false)
|
||||
contentNode.complete = {
|
||||
applyImpl?()
|
||||
}
|
||||
@ -805,7 +809,7 @@ public func importStickerPackShortNameController(context: AccountContext, title:
|
||||
applyImpl?()
|
||||
})]
|
||||
|
||||
let contentNode = ImportStickerPackTitleAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, text: text, placeholder: placeholder, value: value, maxLength: maxLength, asciiOnly: true)
|
||||
let contentNode = ImportStickerPackTitleAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, text: text, placeholder: placeholder, value: value, maxLength: maxLength, asciiOnly: true, hasClearButton: true)
|
||||
contentNode.complete = {
|
||||
applyImpl?()
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ final class ComposeControllerNode: ASDisplayNode {
|
||||
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewChannel, icon: .generic(UIImage(bundleImageName: "Contact List/CreateChannelActionIcon")!), action: {
|
||||
openCreateNewChannelImpl?()
|
||||
})
|
||||
], includeChatList: false, topPeers: false)), onlyWriteable: false, displayPermissionPlaceholder: false)
|
||||
], includeChatList: false, topPeers: .none)), onlyWriteable: false, displayPermissionPlaceholder: false)
|
||||
|
||||
super.init()
|
||||
|
||||
|
@ -169,11 +169,17 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
||||
}
|
||||
self.contentNode = .chats(chatListNode)
|
||||
} else {
|
||||
var displayTopPeers = false
|
||||
if case .premiumGifting = mode {
|
||||
displayTopPeers = true
|
||||
let displayTopPeers: ContactListPresentation.TopPeers
|
||||
if case let .premiumGifting(topSectionTitle, topSectionPeers) = mode {
|
||||
if let topSectionTitle {
|
||||
displayTopPeers = .custom(title: topSectionTitle, peerIds: topSectionPeers)
|
||||
} else {
|
||||
displayTopPeers = .recent
|
||||
}
|
||||
} else if case .requestedUsersSelection = mode {
|
||||
displayTopPeers = true
|
||||
displayTopPeers = .recent
|
||||
} else {
|
||||
displayTopPeers = .none
|
||||
}
|
||||
let contactListNode = ContactListNode(context: context, presentation: .single(.natural(options: options, includeChatList: includeChatList, topPeers: displayTopPeers)), filters: filters, onlyWriteable: onlyWriteable, selectionState: ContactListNodeGroupSelectionState())
|
||||
self.contentNode = .contacts(contactListNode)
|
||||
|
@ -68,7 +68,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
self.filters = filters
|
||||
|
||||
var contextActionImpl: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false, topPeers: false)), filters: filters, onlyWriteable: false, displayCallIcons: displayCallIcons, contextAction: multipleSelection ? { peer, node, gesture, _, _ in
|
||||
self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false, topPeers: .none)), filters: filters, onlyWriteable: false, displayCallIcons: displayCallIcons, contextAction: multipleSelection ? { peer, node, gesture, _, _ in
|
||||
contextActionImpl?(peer, node, gesture, nil)
|
||||
} : nil, multipleSelection: multipleSelection)
|
||||
|
||||
|
@ -2110,7 +2110,15 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
|
||||
let limit: Int32 = 10
|
||||
var reachedLimitImpl: ((Int32) -> Void)?
|
||||
let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .premiumGifting, options: [], isPeerEnabled: { peer in
|
||||
|
||||
let mode: ContactMultiselectionControllerMode
|
||||
if case let .chatList(peerIds) = source {
|
||||
mode = .premiumGifting(topSectionTitle: "🎂 BIRTHDAY TODAY", topSectionPeers: peerIds)
|
||||
} else {
|
||||
mode = .premiumGifting(topSectionTitle: nil, topSectionPeers: [])
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -2309,7 +2317,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, loadedStickerPacks: loadedStickerPacks, isEditing: isEditing, parentNavigationController: parentNavigationController, sendSticker: sendSticker)
|
||||
}
|
||||
|
||||
public func makeStickerEditorScreen(context: AccountContext, source: Any, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile) -> Void) -> ViewController {
|
||||
public func makeStickerEditorScreen(context: AccountContext, source: Any, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile, @escaping () -> Void) -> Void) -> ViewController {
|
||||
let subject: MediaEditorScreen.Subject
|
||||
let mode: MediaEditorScreen.Mode.StickerEditorMode
|
||||
if let file = source as? TelegramMediaFile {
|
||||
@ -2342,9 +2350,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}
|
||||
return nil
|
||||
}, completion: { result, commit in
|
||||
commit({})
|
||||
if case let .sticker(file) = result.media {
|
||||
completion(file)
|
||||
completion(file, {
|
||||
commit({})
|
||||
})
|
||||
}
|
||||
} as (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void
|
||||
)
|
||||
|
@ -441,8 +441,8 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
var resourceReference: MediaResourceReference?
|
||||
|
||||
if let thumbnail = info.thumbnail {
|
||||
if info.flags.contains(.isAnimated) || info.flags.contains(.isVideo) {
|
||||
thumbnailItem = .animated(EngineMediaResource(thumbnail.resource), thumbnail.dimensions, info.flags.contains(.isVideo))
|
||||
if thumbnail.typeHint != .generic {
|
||||
thumbnailItem = .animated(EngineMediaResource(thumbnail.resource), thumbnail.dimensions, thumbnail.typeHint == .video)
|
||||
} else {
|
||||
thumbnailItem = .still(thumbnail)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user