mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit '85ac817816cb8ed5afdd3314aea99ee19cc325f5'
This commit is contained in:
commit
dec8a63809
@ -8134,5 +8134,11 @@ Sorry for the inconvenience.";
|
|||||||
"Channel.EditAdmin.PermissionCreateTopics" = "Create Topics";
|
"Channel.EditAdmin.PermissionCreateTopics" = "Create Topics";
|
||||||
|
|
||||||
"ChatList.Search.FilterTopics" = "Topics";
|
"ChatList.Search.FilterTopics" = "Topics";
|
||||||
|
|
||||||
"DialogList.SearchSectionTopics" = "Topics";
|
"DialogList.SearchSectionTopics" = "Topics";
|
||||||
|
|
||||||
|
"ChatListFolderSettings.SubscribeToMoveAll" = "Subscribe to **Telegram Premium** to move the \"All Chats\" folder.";
|
||||||
|
"ChatListFolderSettings.SubscribeToMoveAllAction" = "More";
|
||||||
|
|
||||||
|
"Channel.AdminLog.MessageChangedGroupUsernames" = "%@ changed group links:";
|
||||||
|
"Channel.AdminLog.MessageChangedChannelUsernames" = "%@ changed channel links:";
|
||||||
|
"Channel.AdminLog.MessagePreviousLinks" = "Previous links";
|
||||||
|
@ -1792,6 +1792,16 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
self?.askForFilterRemoval(id: id)
|
self?.askForFilterRemoval(id: id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.tabContainerNode.presentPremiumTip = { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
let context = strongSelf.context
|
||||||
|
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_reorder", scale: 0.05, colors: [:], title: nil, text: strongSelf.presentationData.strings.ChatListFolderSettings_SubscribeToMoveAll, customUndoText: strongSelf.presentationData.strings.ChatListFolderSettings_SubscribeToMoveAllAction), elevatedLayout: true, animateInAsReplacement: false, action: { action in
|
||||||
|
if case .undo = action {
|
||||||
|
strongSelf.push(PremiumIntroScreen(context: context, source: .folders))
|
||||||
|
}
|
||||||
|
return false }), in: .window(.root))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let tabContextGesture: (Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool, Bool) -> Void = { [weak self] id, sourceNode, gesture, keepInPlace, isDisabled in
|
let tabContextGesture: (Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool, Bool) -> Void = { [weak self] id, sourceNode, gesture, keepInPlace, isDisabled in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
|
@ -11,6 +11,7 @@ import AccountContext
|
|||||||
import ItemListPeerActionItem
|
import ItemListPeerActionItem
|
||||||
import ChatListFilterSettingsHeaderItem
|
import ChatListFilterSettingsHeaderItem
|
||||||
import PremiumUI
|
import PremiumUI
|
||||||
|
import UndoUI
|
||||||
|
|
||||||
private final class ChatListFilterPresetListControllerArguments {
|
private final class ChatListFilterPresetListControllerArguments {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
@ -223,7 +224,7 @@ private func chatListFilterPresetListControllerEntries(presentationData: Present
|
|||||||
|
|
||||||
var folderCount = 0
|
var folderCount = 0
|
||||||
for (filter, chatCount) in filtersWithAppliedOrder(filters: filters, order: updatedFilterOrder) {
|
for (filter, chatCount) in filtersWithAppliedOrder(filters: filters, order: updatedFilterOrder) {
|
||||||
if isPremium, case .allChats = filter {
|
if case .allChats = filter {
|
||||||
entries.append(.preset(index: PresetIndex(value: entries.count), title: "", label: "", preset: filter, canBeReordered: filters.count > 1, canBeDeleted: false, isEditing: state.isEditing, isAllChats: true, isDisabled: false))
|
entries.append(.preset(index: PresetIndex(value: entries.count), title: "", label: "", preset: filter, canBeReordered: filters.count > 1, canBeDeleted: false, isEditing: state.isEditing, isAllChats: true, isDisabled: false))
|
||||||
}
|
}
|
||||||
if case let .filter(_, title, _, _) = filter {
|
if case let .filter(_, title, _, _) = filter {
|
||||||
@ -512,6 +513,8 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch
|
|||||||
|> afterDisposed {
|
|> afterDisposed {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var previousOrder: [Int32]?
|
||||||
|
|
||||||
let controller = ItemListController(context: context, state: signal)
|
let controller = ItemListController(context: context, state: signal)
|
||||||
controller.isOpaqueWhenInOverlay = true
|
controller.isOpaqueWhenInOverlay = true
|
||||||
controller.blocksBackgroundWhenInOverlay = true
|
controller.blocksBackgroundWhenInOverlay = true
|
||||||
@ -598,6 +601,28 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
controller.setReorderCompleted({ (entries: [ChatListFilterPresetListEntry]) -> Void in
|
||||||
|
let _ = (combineLatest(
|
||||||
|
updatedFilterOrder.get() |> take(1),
|
||||||
|
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||||
|
)
|
||||||
|
|> deliverOnMainQueue).start(next: { order, peer in
|
||||||
|
let isPremium = peer?.isPremium ?? false
|
||||||
|
if !isPremium, let order = order, order.first != 0 {
|
||||||
|
updatedFilterOrder.set(.single(previousOrder))
|
||||||
|
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_reorder", scale: 0.05, colors: [:], title: nil, text: presentationData.strings.ChatListFolderSettings_SubscribeToMoveAll, customUndoText: presentationData.strings.ChatListFolderSettings_SubscribeToMoveAllAction), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||||
|
if case .undo = action {
|
||||||
|
pushControllerImpl?(PremiumIntroScreen(context: context, source: .folders))
|
||||||
|
}
|
||||||
|
return false })
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
previousOrder = order
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
@ -224,9 +224,7 @@ private final class ItemNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.selectionFraction = selectionFraction
|
self.selectionFraction = selectionFraction
|
||||||
self.unreadCount = unreadCount
|
self.unreadCount = unreadCount
|
||||||
|
|
||||||
transition.updateAlpha(node: self.containerNode, alpha: (isReordering && isNoFilter && !canReorderAllChats) ? 0.5 : 1.0)
|
|
||||||
|
|
||||||
if isReordering && !isNoFilter {
|
if isReordering && !isNoFilter {
|
||||||
if self.deleteButtonNode == nil {
|
if self.deleteButtonNode == nil {
|
||||||
let deleteButtonNode = ItemNodeDeleteButtonNode(pressed: { [weak self] in
|
let deleteButtonNode = ItemNodeDeleteButtonNode(pressed: { [weak self] in
|
||||||
@ -275,7 +273,7 @@ private final class ItemNode: ASDisplayNode {
|
|||||||
|
|
||||||
if self.isReordering != isReordering {
|
if self.isReordering != isReordering {
|
||||||
self.isReordering = isReordering
|
self.isReordering = isReordering
|
||||||
if self.isReordering && (!isNoFilter || canReorderAllChats) {
|
if self.isReordering {
|
||||||
self.startShaking()
|
self.startShaking()
|
||||||
} else {
|
} else {
|
||||||
self.layer.removeAnimation(forKey: "shaking_position")
|
self.layer.removeAnimation(forKey: "shaking_position")
|
||||||
@ -476,11 +474,13 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
|||||||
var tabRequestedDeletion: ((ChatListFilterTabEntryId) -> Void)?
|
var tabRequestedDeletion: ((ChatListFilterTabEntryId) -> Void)?
|
||||||
var addFilter: (() -> Void)?
|
var addFilter: (() -> Void)?
|
||||||
var contextGesture: ((Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool) -> Void)?
|
var contextGesture: ((Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool) -> Void)?
|
||||||
|
var presentPremiumTip: (() -> Void)?
|
||||||
|
|
||||||
private var reorderingGesture: ReorderingGestureRecognizer?
|
private var reorderingGesture: ReorderingGestureRecognizer?
|
||||||
private var reorderingItem: ChatListFilterTabEntryId?
|
private var reorderingItem: ChatListFilterTabEntryId?
|
||||||
private var reorderingItemPosition: (initial: CGFloat, offset: CGFloat)?
|
private var reorderingItemPosition: (initial: CGFloat, offset: CGFloat)?
|
||||||
private var reorderingAutoScrollAnimator: ConstantDisplayLinkAnimator?
|
private var reorderingAutoScrollAnimator: ConstantDisplayLinkAnimator?
|
||||||
|
private var initialReorderedItemIds: [ChatListFilterTabEntryId]?
|
||||||
private var reorderedItemIds: [ChatListFilterTabEntryId]?
|
private var reorderedItemIds: [ChatListFilterTabEntryId]?
|
||||||
private lazy var hapticFeedback = { HapticFeedback() }()
|
private lazy var hapticFeedback = { HapticFeedback() }()
|
||||||
|
|
||||||
@ -539,11 +539,8 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for (id, itemNode) in strongSelf.itemNodes {
|
for (_, itemNode) in strongSelf.itemNodes {
|
||||||
if itemNode.view.convert(itemNode.bounds, to: strongSelf.view).contains(point) {
|
if itemNode.view.convert(itemNode.bounds, to: strongSelf.view).contains(point) {
|
||||||
if case .all = id, !(strongSelf.currentParams?.canReorderAllChats ?? false) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -552,6 +549,7 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
|||||||
guard let strongSelf = self, let _ = strongSelf.currentParams else {
|
guard let strongSelf = self, let _ = strongSelf.currentParams else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
strongSelf.initialReorderedItemIds = strongSelf.reorderedItemIds
|
||||||
for (id, itemNode) in strongSelf.itemNodes {
|
for (id, itemNode) in strongSelf.itemNodes {
|
||||||
let itemFrame = itemNode.view.convert(itemNode.bounds, to: strongSelf.view)
|
let itemFrame = itemNode.view.convert(itemNode.bounds, to: strongSelf.view)
|
||||||
if itemFrame.contains(point) {
|
if itemFrame.contains(point) {
|
||||||
@ -594,6 +592,11 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
|||||||
strongSelf.scrollNode.addSubnode(itemNode)
|
strongSelf.scrollNode.addSubnode(itemNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strongSelf.currentParams?.canReorderAllChats == false, let firstItem = strongSelf.reorderedItemIds?.first, case .filter = firstItem {
|
||||||
|
strongSelf.reorderedItemIds = strongSelf.initialReorderedItemIds
|
||||||
|
strongSelf.presentPremiumTip?()
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.reorderingItem = nil
|
strongSelf.reorderingItem = nil
|
||||||
strongSelf.reorderingItemPosition = nil
|
strongSelf.reorderingItemPosition = nil
|
||||||
strongSelf.reorderingAutoScrollAnimator?.invalidate()
|
strongSelf.reorderingAutoScrollAnimator?.invalidate()
|
||||||
@ -606,7 +609,7 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let minIndex = (strongSelf.currentParams?.canReorderAllChats ?? false) ? 0 : 1
|
let minIndex = 0
|
||||||
if let reorderingItemNode = strongSelf.itemNodes[reorderingItem], let (initial, _) = strongSelf.reorderingItemPosition, let reorderedItemIds = strongSelf.reorderedItemIds, let currentItemIndex = reorderedItemIds.firstIndex(of: reorderingItem) {
|
if let reorderingItemNode = strongSelf.itemNodes[reorderingItem], let (initial, _) = strongSelf.reorderingItemPosition, let reorderedItemIds = strongSelf.reorderedItemIds, let currentItemIndex = reorderedItemIds.firstIndex(of: reorderingItem) {
|
||||||
|
|
||||||
for (id, itemNode) in strongSelf.itemNodes {
|
for (id, itemNode) in strongSelf.itemNodes {
|
||||||
@ -927,13 +930,6 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
|||||||
} else {
|
} else {
|
||||||
transition.updateFrame(node: self.selectedLineNode, frame: lineFrame)
|
transition.updateFrame(node: self.selectedLineNode, frame: lineFrame)
|
||||||
}
|
}
|
||||||
let lineAlpha: CGFloat
|
|
||||||
if isReordering && canReorderAllChats {
|
|
||||||
lineAlpha = 0.0
|
|
||||||
} else {
|
|
||||||
lineAlpha = isReordering && selectedFilter == .all ? 0.5 : 1.0
|
|
||||||
}
|
|
||||||
transition.updateAlpha(node: self.selectedLineNode, alpha: lineAlpha)
|
|
||||||
|
|
||||||
if let previousSelectedFrame = self.previousSelectedFrame {
|
if let previousSelectedFrame = self.previousSelectedFrame {
|
||||||
let previousContentOffsetX = max(0.0, min(previousContentWidth - previousScrollBounds.width, floor(previousSelectedFrame.midX - previousScrollBounds.width / 2.0)))
|
let previousContentOffsetX = max(0.0, min(previousContentWidth - previousScrollBounds.width, floor(previousSelectedFrame.midX - previousScrollBounds.width / 2.0)))
|
||||||
|
@ -1285,7 +1285,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
|||||||
|> deliverOnMainQueue).start(next: { result in
|
|> deliverOnMainQueue).start(next: { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .generic:
|
case .generic:
|
||||||
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), nil)
|
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved, customUndoText: nil), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
@ -1294,7 +1294,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
|||||||
} else {
|
} else {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
||||||
}
|
}
|
||||||
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text), elevatedLayout: true, animateInAsReplacement: false, action: { action in
|
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text, customUndoText: nil), elevatedLayout: true, animateInAsReplacement: false, action: { action in
|
||||||
if case .info = action {
|
if case .info = action {
|
||||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs)
|
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs)
|
||||||
controllerInteraction?.pushController(controller)
|
controllerInteraction?.pushController(controller)
|
||||||
@ -1489,7 +1489,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
|||||||
|> deliverOnMainQueue).start(next: { result in
|
|> deliverOnMainQueue).start(next: { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .generic:
|
case .generic:
|
||||||
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), nil)
|
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved, customUndoText: nil), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
@ -1498,7 +1498,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
|||||||
} else {
|
} else {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
||||||
}
|
}
|
||||||
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text), elevatedLayout: true, animateInAsReplacement: false, action: { action in
|
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text, customUndoText: nil), elevatedLayout: true, animateInAsReplacement: false, action: { action in
|
||||||
if case .info = action {
|
if case .info = action {
|
||||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs)
|
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs)
|
||||||
controllerInteraction?.pushController(controller)
|
controllerInteraction?.pushController(controller)
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1855,7 +1855,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
}
|
}
|
||||||
switch result {
|
switch result {
|
||||||
case .generic:
|
case .generic:
|
||||||
strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
@ -1864,7 +1864,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
} else {
|
} else {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
||||||
}
|
}
|
||||||
strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -1391,7 +1391,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
Queue.mainQueue().after(0.2) {
|
Queue.mainQueue().after(0.2) {
|
||||||
switch result {
|
switch result {
|
||||||
case .generic:
|
case .generic:
|
||||||
controllerInteraction.presentControllerInCurrent(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
controllerInteraction.presentControllerInCurrent(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
@ -1400,7 +1400,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
} else {
|
} else {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
||||||
}
|
}
|
||||||
controllerInteraction.presentControllerInCurrent(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
controllerInteraction.presentControllerInCurrent(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||||
if case .info = action {
|
if case .info = action {
|
||||||
let controller = PremiumIntroScreen(context: context, source: .savedGifs)
|
let controller = PremiumIntroScreen(context: context, source: .savedGifs)
|
||||||
controllerInteraction.navigationController()?.pushViewController(controller)
|
controllerInteraction.navigationController()?.pushViewController(controller)
|
||||||
|
@ -1535,7 +1535,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
|||||||
|> deliverOnMainQueue).start(next: { result in
|
|> deliverOnMainQueue).start(next: { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .generic:
|
case .generic:
|
||||||
controllerInteraction.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
controllerInteraction.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
@ -1544,7 +1544,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
|||||||
} else {
|
} else {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
||||||
}
|
}
|
||||||
controllerInteraction.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
controllerInteraction.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||||
if case .info = action {
|
if case .info = action {
|
||||||
let controller = PremiumIntroScreen(context: context, source: .savedGifs)
|
let controller = PremiumIntroScreen(context: context, source: .savedGifs)
|
||||||
controllerInteraction.navigationController()?.pushViewController(controller)
|
controllerInteraction.navigationController()?.pushViewController(controller)
|
||||||
|
@ -2270,84 +2270,91 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) {
|
private var playedSwipeToReplyHaptic = false
|
||||||
|
@objc func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) {
|
||||||
|
var offset: CGFloat = 0.0
|
||||||
|
var swipeOffset: CGFloat = 45.0
|
||||||
|
if let item = self.item, item.content.effectivelyIncoming(item.context.account.peerId, associatedData: item.associatedData) {
|
||||||
|
offset = -24.0
|
||||||
|
} else {
|
||||||
|
offset = 10.0
|
||||||
|
swipeOffset = 60.0
|
||||||
|
}
|
||||||
|
|
||||||
switch recognizer.state {
|
switch recognizer.state {
|
||||||
case .began:
|
case .began:
|
||||||
self.currentSwipeToReplyTranslation = 0.0
|
self.playedSwipeToReplyHaptic = false
|
||||||
if self.swipeToReplyFeedback == nil {
|
self.currentSwipeToReplyTranslation = 0.0
|
||||||
self.swipeToReplyFeedback = HapticFeedback()
|
if self.swipeToReplyFeedback == nil {
|
||||||
self.swipeToReplyFeedback?.prepareImpact()
|
self.swipeToReplyFeedback = HapticFeedback()
|
||||||
}
|
self.swipeToReplyFeedback?.prepareImpact()
|
||||||
(self.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures()
|
}
|
||||||
case .changed:
|
self.item?.controllerInteraction.cancelInteractiveKeyboardGestures()
|
||||||
var translation = recognizer.translation(in: self.view)
|
case .changed:
|
||||||
translation.x = max(-80.0, min(0.0, translation.x))
|
var translation = recognizer.translation(in: self.view)
|
||||||
var animateReplyNodeIn = false
|
translation.x = max(-80.0, min(0.0, translation.x))
|
||||||
if (translation.x < -45.0) != (self.currentSwipeToReplyTranslation < -45.0) {
|
|
||||||
if translation.x < -45.0, self.swipeToReplyNode == nil, let item = self.item {
|
if let item = self.item, self.swipeToReplyNode == nil {
|
||||||
self.swipeToReplyFeedback?.impact(.heavy)
|
|
||||||
|
|
||||||
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper), backgroundNode: item.controllerInteraction.presentationContext.backgroundNode, action: ChatMessageSwipeToReplyNode.Action(self.currentSwipeAction))
|
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper), backgroundNode: item.controllerInteraction.presentationContext.backgroundNode, action: ChatMessageSwipeToReplyNode.Action(self.currentSwipeAction))
|
||||||
self.swipeToReplyNode = swipeToReplyNode
|
self.swipeToReplyNode = swipeToReplyNode
|
||||||
self.addSubnode(swipeToReplyNode)
|
self.insertSubnode(swipeToReplyNode, at: 0)
|
||||||
animateReplyNodeIn = true
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
self.currentSwipeToReplyTranslation = translation.x
|
self.currentSwipeToReplyTranslation = translation.x
|
||||||
var bounds = self.bounds
|
var bounds = self.bounds
|
||||||
bounds.origin.x = -translation.x
|
bounds.origin.x = -translation.x
|
||||||
self.bounds = bounds
|
self.bounds = bounds
|
||||||
|
|
||||||
self.updateAttachedAvatarNodeOffset(offset: translation.x, transition: .immediate)
|
self.updateAttachedAvatarNodeOffset(offset: translation.x, transition: .immediate)
|
||||||
|
|
||||||
if let swipeToReplyNode = self.swipeToReplyNode {
|
if let swipeToReplyNode = self.swipeToReplyNode {
|
||||||
swipeToReplyNode.frame = CGRect(origin: CGPoint(x: bounds.size.width, y: floor((self.contentSize.height - 33.0) / 2.0)), size: CGSize(width: 33.0, height: 33.0))
|
swipeToReplyNode.frame = CGRect(origin: CGPoint(x: bounds.size.width + offset, y: floor((self.contentSize.height - 33.0) / 2.0)), size: CGSize(width: 33.0, height: 33.0))
|
||||||
|
|
||||||
if let (rect, containerSize) = self.absoluteRect {
|
if let (rect, containerSize) = self.absoluteRect {
|
||||||
let mappedRect = CGRect(origin: CGPoint(x: rect.minX + swipeToReplyNode.frame.minX, y: rect.minY + swipeToReplyNode.frame.minY), size: swipeToReplyNode.frame.size)
|
let mappedRect = CGRect(origin: CGPoint(x: rect.minX + swipeToReplyNode.frame.minX, y: rect.minY + swipeToReplyNode.frame.minY), size: swipeToReplyNode.frame.size)
|
||||||
swipeToReplyNode.updateAbsoluteRect(mappedRect, within: containerSize)
|
swipeToReplyNode.updateAbsoluteRect(mappedRect, within: containerSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
let progress = abs(translation.x) / swipeOffset
|
||||||
|
swipeToReplyNode.updateProgress(progress)
|
||||||
|
|
||||||
|
if progress == 1.0 && !self.playedSwipeToReplyHaptic {
|
||||||
|
self.swipeToReplyFeedback?.impact(.heavy)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
case .cancelled, .ended:
|
||||||
|
self.swipeToReplyFeedback = nil
|
||||||
|
|
||||||
if animateReplyNodeIn {
|
let translation = recognizer.translation(in: self.view)
|
||||||
swipeToReplyNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.12)
|
if case .ended = recognizer.state, translation.x < -swipeOffset {
|
||||||
swipeToReplyNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4)
|
if let item = self.item {
|
||||||
} else {
|
if let currentSwipeAction = currentSwipeAction {
|
||||||
swipeToReplyNode.alpha = min(1.0, abs(translation.x / 45.0))
|
switch currentSwipeAction {
|
||||||
}
|
case .none:
|
||||||
}
|
break
|
||||||
case .cancelled, .ended:
|
case .reply:
|
||||||
self.swipeToReplyFeedback = nil
|
item.controllerInteraction.setupReply(item.message.id)
|
||||||
|
}
|
||||||
let translation = recognizer.translation(in: self.view)
|
|
||||||
if case .ended = recognizer.state, translation.x < -45.0 {
|
|
||||||
if let item = self.item {
|
|
||||||
if let currentSwipeAction = currentSwipeAction {
|
|
||||||
switch currentSwipeAction {
|
|
||||||
case .none:
|
|
||||||
break
|
|
||||||
case .reply:
|
|
||||||
item.controllerInteraction.setupReply(item.message.id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
var bounds = self.bounds
|
||||||
var bounds = self.bounds
|
let previousBounds = bounds
|
||||||
let previousBounds = bounds
|
bounds.origin.x = 0.0
|
||||||
bounds.origin.x = 0.0
|
self.bounds = bounds
|
||||||
self.bounds = bounds
|
self.layer.animateBounds(from: previousBounds, to: bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
self.layer.animateBounds(from: previousBounds, to: bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
|
||||||
|
|
||||||
self.updateAttachedAvatarNodeOffset(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring))
|
self.updateAttachedAvatarNodeOffset(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring))
|
||||||
|
|
||||||
if let swipeToReplyNode = self.swipeToReplyNode {
|
if let swipeToReplyNode = self.swipeToReplyNode {
|
||||||
self.swipeToReplyNode = nil
|
self.swipeToReplyNode = nil
|
||||||
swipeToReplyNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak swipeToReplyNode] _ in
|
swipeToReplyNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak swipeToReplyNode] _ in
|
||||||
swipeToReplyNode?.removeFromSupernode()
|
swipeToReplyNode?.removeFromSupernode()
|
||||||
})
|
})
|
||||||
swipeToReplyNode.layer.animateScale(from: 1.0, to: 0.2, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
swipeToReplyNode.layer.animateScale(from: 1.0, to: 0.2, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3903,6 +3903,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var playedSwipeToReplyHaptic = false
|
||||||
@objc func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) {
|
@objc func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) {
|
||||||
var offset: CGFloat = 0.0
|
var offset: CGFloat = 0.0
|
||||||
var swipeOffset: CGFloat = 45.0
|
var swipeOffset: CGFloat = 45.0
|
||||||
@ -3915,6 +3916,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
|
|
||||||
switch recognizer.state {
|
switch recognizer.state {
|
||||||
case .began:
|
case .began:
|
||||||
|
self.playedSwipeToReplyHaptic = false
|
||||||
self.currentSwipeToReplyTranslation = 0.0
|
self.currentSwipeToReplyTranslation = 0.0
|
||||||
if self.swipeToReplyFeedback == nil {
|
if self.swipeToReplyFeedback == nil {
|
||||||
self.swipeToReplyFeedback = HapticFeedback()
|
self.swipeToReplyFeedback = HapticFeedback()
|
||||||
@ -3925,7 +3927,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
var translation = recognizer.translation(in: self.view)
|
var translation = recognizer.translation(in: self.view)
|
||||||
translation.x = max(-80.0, min(0.0, translation.x))
|
translation.x = max(-80.0, min(0.0, translation.x))
|
||||||
|
|
||||||
|
|
||||||
if let item = self.item, self.swipeToReplyNode == nil {
|
if let item = self.item, self.swipeToReplyNode == nil {
|
||||||
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper), backgroundNode: item.controllerInteraction.presentationContext.backgroundNode, action: ChatMessageSwipeToReplyNode.Action(self.currentSwipeAction))
|
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper), backgroundNode: item.controllerInteraction.presentationContext.backgroundNode, action: ChatMessageSwipeToReplyNode.Action(self.currentSwipeAction))
|
||||||
self.swipeToReplyNode = swipeToReplyNode
|
self.swipeToReplyNode = swipeToReplyNode
|
||||||
@ -3950,7 +3951,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
swipeToReplyNode.updateAbsoluteRect(mappedRect, within: containerSize)
|
swipeToReplyNode.updateAbsoluteRect(mappedRect, within: containerSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
swipeToReplyNode.updateProgress(abs(translation.x) / swipeOffset)
|
let progress = abs(translation.x) / swipeOffset
|
||||||
|
swipeToReplyNode.updateProgress(progress)
|
||||||
|
|
||||||
|
if progress == 1.0 && !self.playedSwipeToReplyHaptic {
|
||||||
|
self.swipeToReplyFeedback?.impact(.heavy)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case .cancelled, .ended:
|
case .cancelled, .ended:
|
||||||
self.swipeToReplyFeedback = nil
|
self.swipeToReplyFeedback = nil
|
||||||
|
@ -40,7 +40,7 @@ final class ChatMessageEventLogPreviousLinkContentNode: ChatMessageBubbleContent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let title: String = item.presentationData.strings.Channel_AdminLog_MessagePreviousLink
|
let title: String = item.message.text.contains("\n") ? item.presentationData.strings.Channel_AdminLog_MessagePreviousLinks : item.presentationData.strings.Channel_AdminLog_MessagePreviousLink
|
||||||
let text: String = item.message.text
|
let text: String = item.message.text
|
||||||
let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil
|
let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil
|
||||||
|
|
||||||
|
@ -983,84 +983,91 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var playedSwipeToReplyHaptic = false
|
||||||
@objc func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) {
|
@objc func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) {
|
||||||
|
var offset: CGFloat = 0.0
|
||||||
|
var swipeOffset: CGFloat = 45.0
|
||||||
|
if let item = self.item, item.content.effectivelyIncoming(item.context.account.peerId, associatedData: item.associatedData) {
|
||||||
|
offset = -24.0
|
||||||
|
} else {
|
||||||
|
offset = 10.0
|
||||||
|
swipeOffset = 60.0
|
||||||
|
}
|
||||||
|
|
||||||
switch recognizer.state {
|
switch recognizer.state {
|
||||||
case .began:
|
case .began:
|
||||||
self.currentSwipeToReplyTranslation = 0.0
|
self.playedSwipeToReplyHaptic = false
|
||||||
if self.swipeToReplyFeedback == nil {
|
self.currentSwipeToReplyTranslation = 0.0
|
||||||
self.swipeToReplyFeedback = HapticFeedback()
|
if self.swipeToReplyFeedback == nil {
|
||||||
self.swipeToReplyFeedback?.prepareImpact()
|
self.swipeToReplyFeedback = HapticFeedback()
|
||||||
}
|
self.swipeToReplyFeedback?.prepareImpact()
|
||||||
(self.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures()
|
}
|
||||||
case .changed:
|
self.item?.controllerInteraction.cancelInteractiveKeyboardGestures()
|
||||||
var translation = recognizer.translation(in: self.view)
|
case .changed:
|
||||||
translation.x = max(-80.0, min(0.0, translation.x))
|
var translation = recognizer.translation(in: self.view)
|
||||||
var animateReplyNodeIn = false
|
translation.x = max(-80.0, min(0.0, translation.x))
|
||||||
if (translation.x < -45.0) != (self.currentSwipeToReplyTranslation < -45.0) {
|
|
||||||
if translation.x < -45.0, self.swipeToReplyNode == nil, let item = self.item {
|
if let item = self.item, self.swipeToReplyNode == nil {
|
||||||
self.swipeToReplyFeedback?.impact(.heavy)
|
|
||||||
|
|
||||||
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper), backgroundNode: item.controllerInteraction.presentationContext.backgroundNode, action: ChatMessageSwipeToReplyNode.Action(self.currentSwipeAction))
|
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper), backgroundNode: item.controllerInteraction.presentationContext.backgroundNode, action: ChatMessageSwipeToReplyNode.Action(self.currentSwipeAction))
|
||||||
self.swipeToReplyNode = swipeToReplyNode
|
self.swipeToReplyNode = swipeToReplyNode
|
||||||
self.addSubnode(swipeToReplyNode)
|
self.insertSubnode(swipeToReplyNode, at: 0)
|
||||||
animateReplyNodeIn = true
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
self.currentSwipeToReplyTranslation = translation.x
|
self.currentSwipeToReplyTranslation = translation.x
|
||||||
var bounds = self.bounds
|
var bounds = self.bounds
|
||||||
bounds.origin.x = -translation.x
|
bounds.origin.x = -translation.x
|
||||||
self.bounds = bounds
|
self.bounds = bounds
|
||||||
|
|
||||||
self.updateAttachedAvatarNodeOffset(offset: self.avatarOffset ?? translation.x, transition: .immediate)
|
self.updateAttachedAvatarNodeOffset(offset: translation.x, transition: .immediate)
|
||||||
|
|
||||||
if let swipeToReplyNode = self.swipeToReplyNode {
|
if let swipeToReplyNode = self.swipeToReplyNode {
|
||||||
swipeToReplyNode.frame = CGRect(origin: CGPoint(x: bounds.size.width, y: floor((self.contentSize.height - 33.0) / 2.0)), size: CGSize(width: 33.0, height: 33.0))
|
swipeToReplyNode.frame = CGRect(origin: CGPoint(x: bounds.size.width + offset, y: floor((self.contentSize.height - 33.0) / 2.0)), size: CGSize(width: 33.0, height: 33.0))
|
||||||
|
|
||||||
if let (rect, containerSize) = self.absoluteRect {
|
if let (rect, containerSize) = self.absoluteRect {
|
||||||
let mappedRect = CGRect(origin: CGPoint(x: rect.minX + swipeToReplyNode.frame.minX, y: rect.minY + swipeToReplyNode.frame.minY), size: swipeToReplyNode.frame.size)
|
let mappedRect = CGRect(origin: CGPoint(x: rect.minX + swipeToReplyNode.frame.minX, y: rect.minY + swipeToReplyNode.frame.minY), size: swipeToReplyNode.frame.size)
|
||||||
swipeToReplyNode.updateAbsoluteRect(mappedRect, within: containerSize)
|
swipeToReplyNode.updateAbsoluteRect(mappedRect, within: containerSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
let progress = abs(translation.x) / swipeOffset
|
||||||
|
swipeToReplyNode.updateProgress(progress)
|
||||||
|
|
||||||
|
if progress == 1.0 && !self.playedSwipeToReplyHaptic {
|
||||||
|
self.swipeToReplyFeedback?.impact(.heavy)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
case .cancelled, .ended:
|
||||||
|
self.swipeToReplyFeedback = nil
|
||||||
|
|
||||||
if animateReplyNodeIn {
|
let translation = recognizer.translation(in: self.view)
|
||||||
swipeToReplyNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.12)
|
if case .ended = recognizer.state, translation.x < -swipeOffset {
|
||||||
swipeToReplyNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4)
|
if let item = self.item {
|
||||||
} else {
|
if let currentSwipeAction = currentSwipeAction {
|
||||||
swipeToReplyNode.alpha = min(1.0, abs(translation.x / 45.0))
|
switch currentSwipeAction {
|
||||||
}
|
case .none:
|
||||||
}
|
break
|
||||||
case .cancelled, .ended:
|
case .reply:
|
||||||
self.swipeToReplyFeedback = nil
|
item.controllerInteraction.setupReply(item.message.id)
|
||||||
|
}
|
||||||
let translation = recognizer.translation(in: self.view)
|
|
||||||
if case .ended = recognizer.state, translation.x < -45.0 {
|
|
||||||
if let item = self.item {
|
|
||||||
if let currentSwipeAction = currentSwipeAction {
|
|
||||||
switch currentSwipeAction {
|
|
||||||
case .none:
|
|
||||||
break
|
|
||||||
case .reply:
|
|
||||||
item.controllerInteraction.setupReply(item.message.id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
var bounds = self.bounds
|
||||||
var bounds = self.bounds
|
let previousBounds = bounds
|
||||||
let previousBounds = bounds
|
bounds.origin.x = 0.0
|
||||||
bounds.origin.x = 0.0
|
self.bounds = bounds
|
||||||
self.bounds = bounds
|
self.layer.animateBounds(from: previousBounds, to: bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
self.layer.animateBounds(from: previousBounds, to: bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
|
||||||
|
|
||||||
self.updateAttachedAvatarNodeOffset(offset: self.avatarOffset ?? 0.0, transition: .animated(duration: 0.3, curve: .spring))
|
self.updateAttachedAvatarNodeOffset(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring))
|
||||||
|
|
||||||
if let swipeToReplyNode = self.swipeToReplyNode {
|
if let swipeToReplyNode = self.swipeToReplyNode {
|
||||||
self.swipeToReplyNode = nil
|
self.swipeToReplyNode = nil
|
||||||
swipeToReplyNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak swipeToReplyNode] _ in
|
swipeToReplyNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak swipeToReplyNode] _ in
|
||||||
swipeToReplyNode?.removeFromSupernode()
|
swipeToReplyNode?.removeFromSupernode()
|
||||||
})
|
})
|
||||||
swipeToReplyNode.layer.animateScale(from: 1.0, to: 0.2, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
swipeToReplyNode.layer.animateScale(from: 1.0, to: 0.2, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1235,56 +1235,63 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var playedSwipeToReplyHaptic = false
|
||||||
@objc func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) {
|
@objc func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) {
|
||||||
|
var offset: CGFloat = 0.0
|
||||||
|
var swipeOffset: CGFloat = 45.0
|
||||||
|
if let item = self.item, item.content.effectivelyIncoming(item.context.account.peerId, associatedData: item.associatedData) {
|
||||||
|
offset = -24.0
|
||||||
|
} else {
|
||||||
|
offset = 10.0
|
||||||
|
swipeOffset = 60.0
|
||||||
|
}
|
||||||
|
|
||||||
switch recognizer.state {
|
switch recognizer.state {
|
||||||
case .began:
|
case .began:
|
||||||
|
self.playedSwipeToReplyHaptic = false
|
||||||
self.currentSwipeToReplyTranslation = 0.0
|
self.currentSwipeToReplyTranslation = 0.0
|
||||||
if self.swipeToReplyFeedback == nil {
|
if self.swipeToReplyFeedback == nil {
|
||||||
self.swipeToReplyFeedback = HapticFeedback()
|
self.swipeToReplyFeedback = HapticFeedback()
|
||||||
self.swipeToReplyFeedback?.prepareImpact()
|
self.swipeToReplyFeedback?.prepareImpact()
|
||||||
}
|
}
|
||||||
(self.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures()
|
self.item?.controllerInteraction.cancelInteractiveKeyboardGestures()
|
||||||
case .changed:
|
case .changed:
|
||||||
var translation = recognizer.translation(in: self.view)
|
var translation = recognizer.translation(in: self.view)
|
||||||
translation.x = max(-80.0, min(0.0, translation.x))
|
translation.x = max(-80.0, min(0.0, translation.x))
|
||||||
var animateReplyNodeIn = false
|
|
||||||
if (translation.x < -45.0) != (self.currentSwipeToReplyTranslation < -45.0) {
|
if let item = self.item, self.swipeToReplyNode == nil {
|
||||||
if translation.x < -45.0, self.swipeToReplyNode == nil, let item = self.item {
|
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper), backgroundNode: item.controllerInteraction.presentationContext.backgroundNode, action: ChatMessageSwipeToReplyNode.Action(self.currentSwipeAction))
|
||||||
self.swipeToReplyFeedback?.impact(.heavy)
|
self.swipeToReplyNode = swipeToReplyNode
|
||||||
|
self.insertSubnode(swipeToReplyNode, at: 0)
|
||||||
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper), backgroundNode: item.controllerInteraction.presentationContext.backgroundNode, action: ChatMessageSwipeToReplyNode.Action(self.currentSwipeAction))
|
|
||||||
self.swipeToReplyNode = swipeToReplyNode
|
|
||||||
self.addSubnode(swipeToReplyNode)
|
|
||||||
animateReplyNodeIn = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.currentSwipeToReplyTranslation = translation.x
|
self.currentSwipeToReplyTranslation = translation.x
|
||||||
var bounds = self.bounds
|
var bounds = self.bounds
|
||||||
bounds.origin.x = -translation.x
|
bounds.origin.x = -translation.x
|
||||||
self.bounds = bounds
|
self.bounds = bounds
|
||||||
|
|
||||||
self.updateAttachedAvatarNodeOffset(offset: translation.x, transition: .immediate)
|
self.updateAttachedAvatarNodeOffset(offset: translation.x, transition: .immediate)
|
||||||
|
|
||||||
if let swipeToReplyNode = self.swipeToReplyNode {
|
if let swipeToReplyNode = self.swipeToReplyNode {
|
||||||
swipeToReplyNode.frame = CGRect(origin: CGPoint(x: bounds.size.width, y: floor((self.contentSize.height - 33.0) / 2.0)), size: CGSize(width: 33.0, height: 33.0))
|
swipeToReplyNode.frame = CGRect(origin: CGPoint(x: bounds.size.width + offset, y: floor((self.contentSize.height - 33.0) / 2.0)), size: CGSize(width: 33.0, height: 33.0))
|
||||||
|
|
||||||
if let (rect, containerSize) = self.absoluteRect {
|
if let (rect, containerSize) = self.absoluteRect {
|
||||||
let mappedRect = CGRect(origin: CGPoint(x: rect.minX + swipeToReplyNode.frame.minX, y: rect.minY + swipeToReplyNode.frame.minY), size: swipeToReplyNode.frame.size)
|
let mappedRect = CGRect(origin: CGPoint(x: rect.minX + swipeToReplyNode.frame.minX, y: rect.minY + swipeToReplyNode.frame.minY), size: swipeToReplyNode.frame.size)
|
||||||
swipeToReplyNode.updateAbsoluteRect(mappedRect, within: containerSize)
|
swipeToReplyNode.updateAbsoluteRect(mappedRect, within: containerSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
if animateReplyNodeIn {
|
let progress = abs(translation.x) / swipeOffset
|
||||||
swipeToReplyNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.12)
|
swipeToReplyNode.updateProgress(progress)
|
||||||
swipeToReplyNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4)
|
|
||||||
} else {
|
if progress == 1.0 && !self.playedSwipeToReplyHaptic {
|
||||||
swipeToReplyNode.alpha = min(1.0, abs(translation.x / 45.0))
|
self.swipeToReplyFeedback?.impact(.heavy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .cancelled, .ended:
|
case .cancelled, .ended:
|
||||||
self.swipeToReplyFeedback = nil
|
self.swipeToReplyFeedback = nil
|
||||||
|
|
||||||
let translation = recognizer.translation(in: self.view)
|
let translation = recognizer.translation(in: self.view)
|
||||||
if case .ended = recognizer.state, translation.x < -45.0 {
|
if case .ended = recognizer.state, translation.x < -swipeOffset {
|
||||||
if let item = self.item {
|
if let item = self.item {
|
||||||
if let currentSwipeAction = currentSwipeAction {
|
if let currentSwipeAction = currentSwipeAction {
|
||||||
switch currentSwipeAction {
|
switch currentSwipeAction {
|
||||||
|
@ -31,7 +31,7 @@ struct ChatRecentActionsEntryId: Hashable, Comparable {
|
|||||||
|
|
||||||
private func eventNeedsHeader(_ event: AdminLogEvent) -> Bool {
|
private func eventNeedsHeader(_ event: AdminLogEvent) -> Bool {
|
||||||
switch event.action {
|
switch event.action {
|
||||||
case .changeAbout, .changeUsername, .editMessage, .deleteMessage, .pollStopped, .sendMessage:
|
case .changeAbout, .changeUsername, .changeUsernames, .editMessage, .deleteMessage, .pollStopped, .sendMessage:
|
||||||
return true
|
return true
|
||||||
case let .updatePinned(message):
|
case let .updatePinned(message):
|
||||||
if message != nil {
|
if message != nil {
|
||||||
@ -217,14 +217,14 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
|||||||
var text: String = ""
|
var text: String = ""
|
||||||
var entities: [MessageTextEntity] = []
|
var entities: [MessageTextEntity] = []
|
||||||
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||||
appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedChannelUsername(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""), generateEntities: { index in
|
appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedChannelUsernames(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""), generateEntities: { index in
|
||||||
if index == 0, let author = author {
|
if index == 0, let author = author {
|
||||||
return [.TextMention(peerId: author.id)]
|
return [.TextMention(peerId: author.id)]
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
}, to: &text, entities: &entities)
|
}, to: &text, entities: &entities)
|
||||||
} else {
|
} else {
|
||||||
appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedGroupUsername(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""), generateEntities: { index in
|
appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedGroupUsernames(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""), generateEntities: { index in
|
||||||
if index == 0, let author = author {
|
if index == 0, let author = author {
|
||||||
return [.TextMention(peerId: author.id)]
|
return [.TextMention(peerId: author.id)]
|
||||||
}
|
}
|
||||||
@ -237,14 +237,33 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
|||||||
case .content:
|
case .content:
|
||||||
var previousAttributes: [MessageAttribute] = []
|
var previousAttributes: [MessageAttribute] = []
|
||||||
var attributes: [MessageAttribute] = []
|
var attributes: [MessageAttribute] = []
|
||||||
|
|
||||||
let prevText = "https://t.me/\(prev.first ?? "")"
|
var prevTextEntities: [MessageTextEntity] = []
|
||||||
previousAttributes.append(TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< prevText.count, type: .Url)]))
|
var textEntities: [MessageTextEntity] = []
|
||||||
|
|
||||||
let text: String
|
var prevText: String = ""
|
||||||
|
for username in prev {
|
||||||
|
let link = "https://t.me/\(username)"
|
||||||
|
prevTextEntities.append(MessageTextEntity(range: prevText.count ..< prevText.count + link.count, type: .Url))
|
||||||
|
prevText.append(link)
|
||||||
|
prevText.append("\n")
|
||||||
|
}
|
||||||
|
prevText.removeLast()
|
||||||
|
if !prevTextEntities.isEmpty {
|
||||||
|
previousAttributes.append(TextEntitiesMessageAttribute(entities: prevTextEntities))
|
||||||
|
}
|
||||||
|
var text: String = ""
|
||||||
if !new.isEmpty {
|
if !new.isEmpty {
|
||||||
text = "https://t.me/\(new.first ?? "")"
|
for username in new {
|
||||||
attributes.append(TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< text.count, type: .Url)]))
|
let link = "https://t.me/\(username)"
|
||||||
|
textEntities.append(MessageTextEntity(range: text.count ..< text.count + link.count, type: .Url))
|
||||||
|
text.append(link)
|
||||||
|
text.append("\n")
|
||||||
|
}
|
||||||
|
text.removeLast()
|
||||||
|
if !textEntities.isEmpty {
|
||||||
|
attributes.append(TextEntitiesMessageAttribute(entities: prevTextEntities))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
text = self.presentationData.strings.Channel_AdminLog_EmptyMessageText
|
text = self.presentationData.strings.Channel_AdminLog_EmptyMessageText
|
||||||
attributes.append(TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< text.count, type: .Italic)]))
|
attributes.append(TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< text.count, type: .Italic)]))
|
||||||
|
@ -202,7 +202,7 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
|
|||||||
|> deliverOnMainQueue).start(next: { result in
|
|> deliverOnMainQueue).start(next: { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .generic:
|
case .generic:
|
||||||
interfaceInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
interfaceInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
case let .limitExceeded(limit, premiumLimit):
|
case let .limitExceeded(limit, premiumLimit):
|
||||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
let text: String
|
let text: String
|
||||||
@ -211,7 +211,7 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
|
|||||||
} else {
|
} else {
|
||||||
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
|
||||||
}
|
}
|
||||||
interfaceInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
interfaceInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||||
if case .info = action {
|
if case .info = action {
|
||||||
let controller = PremiumIntroScreen(context: context, source: .savedGifs)
|
let controller = PremiumIntroScreen(context: context, source: .savedGifs)
|
||||||
interfaceInteraction?.getNavigationController()?.pushViewController(controller)
|
interfaceInteraction?.getNavigationController()?.pushViewController(controller)
|
||||||
|
@ -3948,7 +3948,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
"Bottom.Group 1.Fill 1": iconColor,
|
"Bottom.Group 1.Fill 1": iconColor,
|
||||||
"EXAMPLE.Group 1.Fill 1": iconColor,
|
"EXAMPLE.Group 1.Fill 1": iconColor,
|
||||||
"Line.Group 1.Stroke 1": iconColor
|
"Line.Group 1.Stroke 1": iconColor
|
||||||
], title: nil, text: self.presentationData.strings.PeerInfo_TooltipUnmuted), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
], title: nil, text: self.presentationData.strings.PeerInfo_TooltipUnmuted, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||||
} else {
|
} else {
|
||||||
self.state = self.state.withHighlightedButton(.mute)
|
self.state = self.state.withHighlightedButton(.mute)
|
||||||
if let (layout, navigationHeight) = self.validLayout {
|
if let (layout, navigationHeight) = self.validLayout {
|
||||||
@ -3990,7 +3990,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, muteInterval: value).start()
|
let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, muteInterval: value).start()
|
||||||
|
|
||||||
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_mute_for", scale: 0.066, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedFor(mutedForTimeIntervalString(strings: strongSelf.presentationData.strings, value: value)).string), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_mute_for", scale: 0.066, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedFor(mutedForTimeIntervalString(strings: strongSelf.presentationData.strings, value: value)).string, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4028,7 +4028,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, sound: .default).start()
|
let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, sound: .default).start()
|
||||||
|
|
||||||
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_on", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundEnabled), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_on", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundEnabled, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||||
})))
|
})))
|
||||||
} else {
|
} else {
|
||||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_DisableSound, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_DisableSound, icon: { theme in
|
||||||
@ -4041,7 +4041,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, sound: .none).start()
|
let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, sound: .none).start()
|
||||||
|
|
||||||
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_off", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundDisabled), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_off", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundDisabled, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4103,7 +4103,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
"Bottom.Group 1.Fill 1": iconColor,
|
"Bottom.Group 1.Fill 1": iconColor,
|
||||||
"EXAMPLE.Group 1.Fill 1": iconColor,
|
"EXAMPLE.Group 1.Fill 1": iconColor,
|
||||||
"Line.Group 1.Stroke 1": iconColor
|
"Line.Group 1.Stroke 1": iconColor
|
||||||
], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, updatePeerDisplayPreviews: { peerId, displayPreviews in
|
}, updatePeerDisplayPreviews: { peerId, displayPreviews in
|
||||||
@ -4137,7 +4137,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
"Bottom.Group 1.Fill 1": iconColor,
|
"Bottom.Group 1.Fill 1": iconColor,
|
||||||
"EXAMPLE.Group 1.Fill 1": iconColor,
|
"EXAMPLE.Group 1.Fill 1": iconColor,
|
||||||
"Line.Group 1.Stroke 1": iconColor
|
"Line.Group 1.Stroke 1": iconColor
|
||||||
], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||||
})))
|
})))
|
||||||
|
|
||||||
var tip: ContextController.Tip?
|
var tip: ContextController.Tip?
|
||||||
@ -4869,7 +4869,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
|
|
||||||
let timeString = stringForPreciseRelativeTimestamp(strings: strongSelf.presentationData.strings, relativeTimestamp: Int32(Date().timeIntervalSince1970) + value, relativeTo: Int32(Date().timeIntervalSince1970), dateTimeFormat: strongSelf.presentationData.dateTimeFormat)
|
let timeString = stringForPreciseRelativeTimestamp(strings: strongSelf.presentationData.strings, relativeTimestamp: Int32(Date().timeIntervalSince1970) + value, relativeTo: Int32(Date().timeIntervalSince1970), dateTimeFormat: strongSelf.presentationData.dateTimeFormat)
|
||||||
|
|
||||||
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_mute_for", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedUntil(timeString).string), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_mute_for", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedUntil(timeString).string, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
self.controller?.view.endEditing(true)
|
self.controller?.view.endEditing(true)
|
||||||
|
@ -41,7 +41,7 @@ public enum UndoOverlayContent {
|
|||||||
case inviteRequestSent(title: String, text: String)
|
case inviteRequestSent(title: String, text: String)
|
||||||
case image(image: UIImage, text: String)
|
case image(image: UIImage, text: String)
|
||||||
case notificationSoundAdded(title: String, text: String, action: (() -> Void)?)
|
case notificationSoundAdded(title: String, text: String, action: (() -> Void)?)
|
||||||
case universal(animation: String, scale: CGFloat, colors: [String: UIColor], title: String?, text: String)
|
case universal(animation: String, scale: CGFloat, colors: [String: UIColor], title: String?, text: String, customUndoText: String?)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum UndoOverlayAction {
|
public enum UndoOverlayAction {
|
||||||
|
@ -824,7 +824,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .universal(animation, scale, colors, title, text):
|
case let .universal(animation, scale, colors, title, text, customUndoText):
|
||||||
self.avatarNode = nil
|
self.avatarNode = nil
|
||||||
self.iconNode = nil
|
self.iconNode = nil
|
||||||
self.iconCheckNode = nil
|
self.iconCheckNode = nil
|
||||||
@ -851,7 +851,13 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
self.originalRemainingSeconds = isUserInteractionEnabled ? 5 : 3
|
self.originalRemainingSeconds = isUserInteractionEnabled ? 5 : 3
|
||||||
|
|
||||||
self.textNode.maximumNumberOfLines = 5
|
self.textNode.maximumNumberOfLines = 5
|
||||||
displayUndo = false
|
|
||||||
|
if let customUndoText = customUndoText {
|
||||||
|
undoText = customUndoText
|
||||||
|
displayUndo = true
|
||||||
|
} else {
|
||||||
|
displayUndo = false
|
||||||
|
}
|
||||||
case let .image(image, text):
|
case let .image(image, text):
|
||||||
self.avatarNode = nil
|
self.avatarNode = nil
|
||||||
self.iconNode = ASImageNode()
|
self.iconNode = ASImageNode()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user