mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Notification sounds update
This commit is contained in:
parent
c278a5af7d
commit
f45de2d44f
@ -544,52 +544,55 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
private func initializeContent() {
|
private func initializeContent() {
|
||||||
switch self.source {
|
switch self.source {
|
||||||
case let .reference(source):
|
case let .reference(source):
|
||||||
/*let transitionInfo = source.transitionInfo()
|
if let controller = self.getController() as? ContextController, controller.workaroundUseLegacyImplementation {
|
||||||
if let transitionInfo = transitionInfo {
|
let transitionInfo = source.transitionInfo()
|
||||||
let referenceView = transitionInfo.referenceView
|
if let transitionInfo = transitionInfo {
|
||||||
self.contentContainerNode.contentNode = .reference(view: referenceView)
|
let referenceView = transitionInfo.referenceView
|
||||||
self.contentAreaInScreenSpace = transitionInfo.contentAreaInScreenSpace
|
self.contentContainerNode.contentNode = .reference(view: referenceView)
|
||||||
self.customPosition = transitionInfo.customPosition
|
self.contentAreaInScreenSpace = transitionInfo.contentAreaInScreenSpace
|
||||||
var projectedFrame = convertFrame(referenceView.bounds, from: referenceView, to: self.view)
|
self.customPosition = transitionInfo.customPosition
|
||||||
projectedFrame.origin.x += transitionInfo.insets.left
|
var projectedFrame = convertFrame(referenceView.bounds, from: referenceView, to: self.view)
|
||||||
projectedFrame.size.width -= transitionInfo.insets.left + transitionInfo.insets.right
|
projectedFrame.origin.x += transitionInfo.insets.left
|
||||||
projectedFrame.origin.y += transitionInfo.insets.top
|
projectedFrame.size.width -= transitionInfo.insets.left + transitionInfo.insets.right
|
||||||
projectedFrame.size.width -= transitionInfo.insets.top + transitionInfo.insets.bottom
|
projectedFrame.origin.y += transitionInfo.insets.top
|
||||||
self.originalProjectedContentViewFrame = (projectedFrame, projectedFrame)
|
projectedFrame.size.width -= transitionInfo.insets.top + transitionInfo.insets.bottom
|
||||||
}*/
|
self.originalProjectedContentViewFrame = (projectedFrame, projectedFrame)
|
||||||
let presentationNode = ContextControllerExtractedPresentationNode(
|
}
|
||||||
getController: { [weak self] in
|
} else {
|
||||||
return self?.getController()
|
let presentationNode = ContextControllerExtractedPresentationNode(
|
||||||
},
|
getController: { [weak self] in
|
||||||
requestUpdate: { [weak self] transition in
|
return self?.getController()
|
||||||
guard let strongSelf = self else {
|
},
|
||||||
return
|
requestUpdate: { [weak self] transition in
|
||||||
}
|
guard let strongSelf = self else {
|
||||||
if let validLayout = strongSelf.validLayout {
|
return
|
||||||
strongSelf.updateLayout(
|
}
|
||||||
layout: validLayout,
|
if let validLayout = strongSelf.validLayout {
|
||||||
transition: transition,
|
strongSelf.updateLayout(
|
||||||
previousActionsContainerNode: nil
|
layout: validLayout,
|
||||||
)
|
transition: transition,
|
||||||
}
|
previousActionsContainerNode: nil
|
||||||
},
|
)
|
||||||
requestDismiss: { [weak self] result in
|
}
|
||||||
guard let strongSelf = self else {
|
},
|
||||||
return
|
requestDismiss: { [weak self] result in
|
||||||
}
|
guard let strongSelf = self else {
|
||||||
strongSelf.dismissedForCancel?()
|
return
|
||||||
strongSelf.beginDismiss(result)
|
}
|
||||||
},
|
strongSelf.dismissedForCancel?()
|
||||||
requestAnimateOut: { [weak self] result, completion in
|
strongSelf.beginDismiss(result)
|
||||||
guard let strongSelf = self else {
|
},
|
||||||
return
|
requestAnimateOut: { [weak self] result, completion in
|
||||||
}
|
guard let strongSelf = self else {
|
||||||
strongSelf.animateOut(result: result, completion: completion)
|
return
|
||||||
},
|
}
|
||||||
source: .reference(source)
|
strongSelf.animateOut(result: result, completion: completion)
|
||||||
)
|
},
|
||||||
self.presentationNode = presentationNode
|
source: .reference(source)
|
||||||
self.addSubnode(presentationNode)
|
)
|
||||||
|
self.presentationNode = presentationNode
|
||||||
|
self.addSubnode(presentationNode)
|
||||||
|
}
|
||||||
case let .extracted(source):
|
case let .extracted(source):
|
||||||
let presentationNode = ContextControllerExtractedPresentationNode(
|
let presentationNode = ContextControllerExtractedPresentationNode(
|
||||||
getController: { [weak self] in
|
getController: { [weak self] in
|
||||||
@ -2251,6 +2254,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
|||||||
|
|
||||||
public var useComplexItemsTransitionAnimation = false
|
public var useComplexItemsTransitionAnimation = false
|
||||||
public var immediateItemsTransitionAnimation = false
|
public var immediateItemsTransitionAnimation = false
|
||||||
|
public var workaroundUseLegacyImplementation = false
|
||||||
|
|
||||||
public enum HandledTouchEvent {
|
public enum HandledTouchEvent {
|
||||||
case ignore
|
case ignore
|
||||||
|
@ -32,8 +32,9 @@ public class ItemListCheckboxItem: ListViewItem, ItemListItem {
|
|||||||
let zeroSeparatorInsets: Bool
|
let zeroSeparatorInsets: Bool
|
||||||
public let sectionId: ItemListSectionId
|
public let sectionId: ItemListSectionId
|
||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
|
let deleteAction: (() -> Void)?
|
||||||
|
|
||||||
public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, iconSize: CGSize? = nil, iconPlacement: IconPlacement = .default, title: String, style: ItemListCheckboxItemStyle, color: ItemListCheckboxItemColor = .accent, checked: Bool, zeroSeparatorInsets: Bool, sectionId: ItemListSectionId, action: @escaping () -> Void) {
|
public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, iconSize: CGSize? = nil, iconPlacement: IconPlacement = .default, title: String, style: ItemListCheckboxItemStyle, color: ItemListCheckboxItemColor = .accent, checked: Bool, zeroSeparatorInsets: Bool, sectionId: ItemListSectionId, action: @escaping () -> Void, deleteAction: (() -> Void)? = nil) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.icon = icon
|
self.icon = icon
|
||||||
self.iconSize = iconSize
|
self.iconSize = iconSize
|
||||||
@ -45,6 +46,7 @@ public class ItemListCheckboxItem: ListViewItem, ItemListItem {
|
|||||||
self.zeroSeparatorInsets = zeroSeparatorInsets
|
self.zeroSeparatorInsets = zeroSeparatorInsets
|
||||||
self.sectionId = sectionId
|
self.sectionId = sectionId
|
||||||
self.action = action
|
self.action = action
|
||||||
|
self.deleteAction = deleteAction
|
||||||
}
|
}
|
||||||
|
|
||||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||||
@ -88,7 +90,7 @@ public class ItemListCheckboxItem: ListViewItem, ItemListItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ItemListCheckboxItemNode: ListViewItemNode {
|
public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode {
|
||||||
private let backgroundNode: ASDisplayNode
|
private let backgroundNode: ASDisplayNode
|
||||||
private let topStripeNode: ASDisplayNode
|
private let topStripeNode: ASDisplayNode
|
||||||
private let bottomStripeNode: ASDisplayNode
|
private let bottomStripeNode: ASDisplayNode
|
||||||
@ -97,6 +99,7 @@ public class ItemListCheckboxItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
private let activateArea: AccessibilityAreaNode
|
private let activateArea: AccessibilityAreaNode
|
||||||
|
|
||||||
|
private let contentContainerNode: ASDisplayNode
|
||||||
private let imageNode: ASImageNode
|
private let imageNode: ASImageNode
|
||||||
private let iconNode: ASImageNode
|
private let iconNode: ASImageNode
|
||||||
private let titleNode: TextNode
|
private let titleNode: TextNode
|
||||||
@ -115,6 +118,8 @@ public class ItemListCheckboxItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
self.maskNode = ASImageNode()
|
self.maskNode = ASImageNode()
|
||||||
|
|
||||||
|
self.contentContainerNode = ASDisplayNode()
|
||||||
|
|
||||||
self.imageNode = ASImageNode()
|
self.imageNode = ASImageNode()
|
||||||
self.imageNode.isLayerBacked = true
|
self.imageNode.isLayerBacked = true
|
||||||
self.imageNode.displayWithoutProcessing = true
|
self.imageNode.displayWithoutProcessing = true
|
||||||
@ -135,11 +140,12 @@ public class ItemListCheckboxItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
self.activateArea = AccessibilityAreaNode()
|
self.activateArea = AccessibilityAreaNode()
|
||||||
|
|
||||||
super.init(layerBacked: false, dynamicBounce: false)
|
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||||
|
|
||||||
self.addSubnode(self.imageNode)
|
self.addSubnode(self.contentContainerNode)
|
||||||
self.addSubnode(self.iconNode)
|
self.contentContainerNode.addSubnode(self.imageNode)
|
||||||
self.addSubnode(self.titleNode)
|
self.contentContainerNode.addSubnode(self.iconNode)
|
||||||
|
self.contentContainerNode.addSubnode(self.titleNode)
|
||||||
self.addSubnode(self.activateArea)
|
self.addSubnode(self.activateArea)
|
||||||
|
|
||||||
self.activateArea.activate = { [weak self] in
|
self.activateArea.activate = { [weak self] in
|
||||||
@ -204,6 +210,8 @@ public class ItemListCheckboxItemNode: ListViewItemNode {
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.item = item
|
strongSelf.item = item
|
||||||
|
|
||||||
|
strongSelf.contentContainerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: params.width, height: layout.contentSize.height))
|
||||||
|
|
||||||
strongSelf.activateArea.accessibilityLabel = item.title
|
strongSelf.activateArea.accessibilityLabel = item.title
|
||||||
if item.checked {
|
if item.checked {
|
||||||
strongSelf.activateArea.accessibilityValue = "Selected"
|
strongSelf.activateArea.accessibilityValue = "Selected"
|
||||||
@ -297,6 +305,14 @@ public class ItemListCheckboxItemNode: ListViewItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: strongSelf.backgroundNode.frame.height + UIScreenPixel + UIScreenPixel))
|
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: strongSelf.backgroundNode.frame.height + UIScreenPixel + UIScreenPixel))
|
||||||
|
|
||||||
|
strongSelf.updateLayout(size: layout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset)
|
||||||
|
|
||||||
|
if item.deleteAction != nil {
|
||||||
|
strongSelf.setRevealOptions((left: [], right: [ItemListRevealOption(key: 0, title: item.presentationData.strings.Common_Delete, icon: .none, color: item.presentationData.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.presentationData.theme.list.itemDisclosureActions.destructive.foregroundColor)]))
|
||||||
|
} else {
|
||||||
|
strongSelf.setRevealOptions((left: [], right: []))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -347,4 +363,61 @@ public class ItemListCheckboxItemNode: ListViewItemNode {
|
|||||||
override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override public func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
|
super.updateRevealOffset(offset: offset, transition: transition)
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.contentContainerNode, frame: CGRect(origin: CGPoint(x: offset, y: self.contentContainerNode.frame.minY), size: self.contentContainerNode.bounds.size))
|
||||||
|
|
||||||
|
/*if let (item, params, _, _, _) = self.layoutParams {
|
||||||
|
let revealOffset = offset
|
||||||
|
|
||||||
|
let editingOffset: CGFloat
|
||||||
|
if let editableControlNode = self.editableControlNode {
|
||||||
|
editingOffset = editableControlNode.bounds.size.width
|
||||||
|
var editableControlFrame = editableControlNode.frame
|
||||||
|
editableControlFrame.origin.x = params.leftInset + offset
|
||||||
|
transition.updateFrame(node: editableControlNode, frame: editableControlFrame)
|
||||||
|
} else {
|
||||||
|
editingOffset = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
let leftInset: CGFloat = 86.0 + params.leftInset + editingOffset
|
||||||
|
let rightInset: CGFloat = 13.0 + params.rightInset
|
||||||
|
var infoIconRightInset: CGFloat = rightInset - 1.0
|
||||||
|
|
||||||
|
var dateRightInset: CGFloat = 46.0 + params.rightInset
|
||||||
|
if item.editing {
|
||||||
|
dateRightInset += 5.0
|
||||||
|
infoIconRightInset -= 36.0
|
||||||
|
}
|
||||||
|
|
||||||
|
var avatarFrame = self.avatarNode.frame
|
||||||
|
avatarFrame.origin.x = revealOffset + leftInset - 52.0
|
||||||
|
transition.updateFrameAdditive(node: self.avatarNode, frame: avatarFrame)
|
||||||
|
|
||||||
|
transition.updateFrameAdditive(node: self.titleNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset, y: self.titleNode.frame.minY), size: self.titleNode.bounds.size))
|
||||||
|
|
||||||
|
transition.updateFrameAdditive(node: self.statusNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset, y: self.statusNode.frame.minY), size: self.statusNode.bounds.size))
|
||||||
|
|
||||||
|
transition.updateFrameAdditive(node: self.dateNode, frame: CGRect(origin: CGPoint(x: editingOffset + revealOffset + self.bounds.size.width - dateRightInset - self.dateNode.bounds.size.width, y: self.dateNode.frame.minY), size: self.dateNode.bounds.size))
|
||||||
|
|
||||||
|
transition.updateFrameAdditive(node: self.typeIconNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset - 81.0, y: self.typeIconNode.frame.minY), size: self.typeIconNode.bounds.size))
|
||||||
|
|
||||||
|
transition.updateFrameAdditive(node: self.infoButtonNode, frame: CGRect(origin: CGPoint(x: revealOffset + self.bounds.size.width - infoIconRightInset - self.infoButtonNode.bounds.width, y: self.infoButtonNode.frame.minY), size: self.infoButtonNode.bounds.size))
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func revealOptionSelected(_ option: ItemListRevealOption, animated: Bool) {
|
||||||
|
self.setRevealOptionsOpened(false, animated: true)
|
||||||
|
self.revealOptionsInteractivelyClosed()
|
||||||
|
|
||||||
|
if let item = self.item {
|
||||||
|
item.deleteAction?()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*if let item = self.layoutParams?.0 {
|
||||||
|
item.interaction.delete(item.messages.map { $0.id })
|
||||||
|
}*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ private struct NotificationSoundSelectionArguments {
|
|||||||
let complete: () -> Void
|
let complete: () -> Void
|
||||||
let cancel: () -> Void
|
let cancel: () -> Void
|
||||||
let upload: () -> Void
|
let upload: () -> Void
|
||||||
|
let deleteSound: (PeerMessageSound) -> Void
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum NotificationSoundSelectionSection: Int32 {
|
private enum NotificationSoundSelectionSection: Int32 {
|
||||||
@ -31,9 +32,15 @@ private enum NotificationSoundSelectionSection: Int32 {
|
|||||||
|
|
||||||
private struct NotificationSoundSelectionState: Equatable {
|
private struct NotificationSoundSelectionState: Equatable {
|
||||||
var selectedSound: PeerMessageSound
|
var selectedSound: PeerMessageSound
|
||||||
|
var removedSounds: [PeerMessageSound]
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum NotificationSoundSelectionEntry: ItemListNodeEntry {
|
private enum NotificationSoundSelectionEntry: ItemListNodeEntry {
|
||||||
|
enum StableId: Hashable {
|
||||||
|
case index(Int32)
|
||||||
|
case sound(PeerMessageSound.Id)
|
||||||
|
}
|
||||||
|
|
||||||
case cloudHeader(String)
|
case cloudHeader(String)
|
||||||
case uploadSound(String)
|
case uploadSound(String)
|
||||||
case cloudInfo(String)
|
case cloudInfo(String)
|
||||||
@ -42,7 +49,7 @@ private enum NotificationSoundSelectionEntry: ItemListNodeEntry {
|
|||||||
case classicHeader(PresentationTheme, String)
|
case classicHeader(PresentationTheme, String)
|
||||||
case none(section: NotificationSoundSelectionSection, theme: PresentationTheme, text: String, selected: Bool)
|
case none(section: NotificationSoundSelectionSection, theme: PresentationTheme, text: String, selected: Bool)
|
||||||
case `default`(section: NotificationSoundSelectionSection, theme: PresentationTheme, text: String, selected: Bool)
|
case `default`(section: NotificationSoundSelectionSection, theme: PresentationTheme, text: String, selected: Bool)
|
||||||
case sound(section: NotificationSoundSelectionSection, index: Int32, theme: PresentationTheme, text: String, sound: PeerMessageSound, selected: Bool)
|
case sound(section: NotificationSoundSelectionSection, index: Int32, theme: PresentationTheme, text: String, sound: PeerMessageSound, selected: Bool, canBeDeleted: Bool)
|
||||||
|
|
||||||
var section: ItemListSectionId {
|
var section: ItemListSectionId {
|
||||||
switch self {
|
switch self {
|
||||||
@ -56,7 +63,7 @@ private enum NotificationSoundSelectionEntry: ItemListNodeEntry {
|
|||||||
return section.rawValue
|
return section.rawValue
|
||||||
case let .default(section, _, _, _):
|
case let .default(section, _, _, _):
|
||||||
return section.rawValue
|
return section.rawValue
|
||||||
case let .sound(section, _, _, _, _, _):
|
case let .sound(section, _, _, _, _, _, _):
|
||||||
return section.rawValue
|
return section.rawValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +98,7 @@ private enum NotificationSoundSelectionEntry: ItemListNodeEntry {
|
|||||||
case .classic:
|
case .classic:
|
||||||
return 2002
|
return 2002
|
||||||
}
|
}
|
||||||
case let .sound(section, index, _, _, _, _):
|
case let .sound(section, index, _, _, _, _, _):
|
||||||
switch section {
|
switch section {
|
||||||
case .cloud:
|
case .cloud:
|
||||||
return 3 + index
|
return 3 + index
|
||||||
@ -103,8 +110,13 @@ private enum NotificationSoundSelectionEntry: ItemListNodeEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var stableId: Int32 {
|
var stableId: StableId {
|
||||||
return self.sortId
|
switch self {
|
||||||
|
case let .sound(_ , _, _, _, sound, _, _):
|
||||||
|
return .sound(sound.id)
|
||||||
|
default:
|
||||||
|
return .index(self.sortId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: NotificationSoundSelectionEntry, rhs: NotificationSoundSelectionEntry) -> Bool {
|
static func ==(lhs: NotificationSoundSelectionEntry, rhs: NotificationSoundSelectionEntry) -> Bool {
|
||||||
@ -151,8 +163,8 @@ private enum NotificationSoundSelectionEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .sound(lhsSection, lhsIndex, lhsTheme, lhsText, lhsSound, lhsSelected):
|
case let .sound(lhsSection, lhsIndex, lhsTheme, lhsText, lhsSound, lhsSelected, lhsCanBeDeleted):
|
||||||
if case let .sound(rhsSection, rhsIndex, rhsTheme, rhsText, rhsSound, rhsSelected) = rhs, lhsSection == rhsSection, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsText == rhsText, lhsSound == rhsSound, lhsSelected == rhsSelected {
|
if case let .sound(rhsSection, rhsIndex, rhsTheme, rhsText, rhsSound, rhsSelected, rhsCanBeDeleted) = rhs, lhsSection == rhsSection, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsText == rhsText, lhsSound == rhsSound, lhsSelected == rhsSelected, lhsCanBeDeleted == rhsCanBeDeleted {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -161,7 +173,7 @@ private enum NotificationSoundSelectionEntry: ItemListNodeEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func <(lhs: NotificationSoundSelectionEntry, rhs: NotificationSoundSelectionEntry) -> Bool {
|
static func <(lhs: NotificationSoundSelectionEntry, rhs: NotificationSoundSelectionEntry) -> Bool {
|
||||||
return lhs.stableId < rhs.stableId
|
return lhs.sortId < rhs.sortId
|
||||||
}
|
}
|
||||||
|
|
||||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||||
@ -188,23 +200,30 @@ private enum NotificationSoundSelectionEntry: ItemListNodeEntry {
|
|||||||
return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||||
arguments.selectSound(.default)
|
arguments.selectSound(.default)
|
||||||
})
|
})
|
||||||
case let .sound(_, _, _, text, sound, selected):
|
case let .sound(_, _, _, text, sound, selected, canBeDeleted):
|
||||||
return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||||
arguments.selectSound(sound)
|
arguments.selectSound(sound)
|
||||||
})
|
}, deleteAction: canBeDeleted ? {
|
||||||
|
arguments.deleteSound(sound)
|
||||||
|
} : nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func notificationsAndSoundsEntries(presentationData: PresentationData, defaultSound: PeerMessageSound?, state: NotificationSoundSelectionState, notificationSoundList: NotificationSoundList?) -> [NotificationSoundSelectionEntry] {
|
private func notificationsAndSoundsEntries(presentationData: PresentationData, defaultSound: PeerMessageSound?, state: NotificationSoundSelectionState, notificationSoundList: NotificationSoundList?) -> [NotificationSoundSelectionEntry] {
|
||||||
|
let selectedSound = resolvedNotificationSound(sound: state.selectedSound, notificationSoundList: notificationSoundList)
|
||||||
|
|
||||||
var entries: [NotificationSoundSelectionEntry] = []
|
var entries: [NotificationSoundSelectionEntry] = []
|
||||||
|
|
||||||
entries.append(.cloudHeader(presentationData.strings.Notifications_TelegramTones))
|
entries.append(.cloudHeader(presentationData.strings.Notifications_TelegramTones))
|
||||||
//entries.append(.none(section: .cloud, theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: .none), selected: state.selectedSound == .none))
|
//entries.append(.none(section: .cloud, theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: .none), selected: selectedSound == .none))
|
||||||
if let notificationSoundList = notificationSoundList {
|
if let notificationSoundList = notificationSoundList {
|
||||||
for listSound in notificationSoundList.sounds {
|
for listSound in notificationSoundList.sounds {
|
||||||
let sound: PeerMessageSound = .cloud(fileId: listSound.file.fileId.id)
|
let sound: PeerMessageSound = .cloud(fileId: listSound.file.fileId.id)
|
||||||
entries.append(.sound(section: .cloud, index: Int32(entries.count), theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: sound), sound: sound, selected: state.selectedSound.id == sound.id))
|
if state.removedSounds.contains(where: { $0.id == sound.id }) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
entries.append(.sound(section: .cloud, index: Int32(entries.count), theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: sound), sound: sound, selected: selectedSound.id == sound.id, canBeDeleted: true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entries.append(.uploadSound(presentationData.strings.Notifications_UploadSound))
|
entries.append(.uploadSound(presentationData.strings.Notifications_UploadSound))
|
||||||
@ -212,18 +231,18 @@ private func notificationsAndSoundsEntries(presentationData: PresentationData, d
|
|||||||
|
|
||||||
entries.append(.modernHeader(presentationData.theme, presentationData.strings.Notifications_AlertTones))
|
entries.append(.modernHeader(presentationData.theme, presentationData.strings.Notifications_AlertTones))
|
||||||
if let defaultSound = defaultSound {
|
if let defaultSound = defaultSound {
|
||||||
entries.append(.default(section: .modern, theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: .default, default: defaultSound), selected: state.selectedSound.id == .default))
|
entries.append(.default(section: .modern, theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: .default, default: defaultSound), selected: selectedSound.id == .default))
|
||||||
}
|
}
|
||||||
entries.append(.none(section: .modern, theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: .none), selected: state.selectedSound.id == .none))
|
entries.append(.none(section: .modern, theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: .none), selected: selectedSound.id == .none))
|
||||||
for i in 0 ..< 12 {
|
for i in 0 ..< 12 {
|
||||||
let sound: PeerMessageSound = .bundledModern(id: Int32(i))
|
let sound: PeerMessageSound = .bundledModern(id: Int32(i))
|
||||||
entries.append(.sound(section: .modern, index: Int32(i), theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: sound), sound: sound, selected: sound.id == state.selectedSound.id))
|
entries.append(.sound(section: .modern, index: Int32(i), theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: sound), sound: sound, selected: sound.id == selectedSound.id, canBeDeleted: false))
|
||||||
}
|
}
|
||||||
|
|
||||||
entries.append(.classicHeader(presentationData.theme, presentationData.strings.Notifications_ClassicTones))
|
entries.append(.classicHeader(presentationData.theme, presentationData.strings.Notifications_ClassicTones))
|
||||||
for i in 0 ..< 8 {
|
for i in 0 ..< 8 {
|
||||||
let sound: PeerMessageSound = .bundledClassic(id: Int32(i))
|
let sound: PeerMessageSound = .bundledClassic(id: Int32(i))
|
||||||
entries.append(.sound(section: .classic, index: Int32(i), theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: sound), sound: sound, selected: sound.id == state.selectedSound.id))
|
entries.append(.sound(section: .classic, index: Int32(i), theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: sound), sound: sound, selected: sound.id == selectedSound.id, canBeDeleted: false))
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
@ -340,8 +359,8 @@ public func playSound(context: AccountContext, notificationSoundList: Notificati
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func notificationSoundSelectionController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, isModal: Bool, currentSound: PeerMessageSound, defaultSound: PeerMessageSound?, completion: @escaping (PeerMessageSound) -> Void) -> ViewController {
|
public func notificationSoundSelectionController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, isModal: Bool, currentSound: PeerMessageSound, defaultSound: PeerMessageSound?, completion: @escaping (PeerMessageSound) -> Void) -> ViewController {
|
||||||
let statePromise = ValuePromise(NotificationSoundSelectionState(selectedSound: currentSound), ignoreRepeated: true)
|
let statePromise = ValuePromise(NotificationSoundSelectionState(selectedSound: currentSound, removedSounds: []), ignoreRepeated: true)
|
||||||
let stateValue = Atomic(value: NotificationSoundSelectionState(selectedSound: currentSound))
|
let stateValue = Atomic(value: NotificationSoundSelectionState(selectedSound: currentSound, removedSounds: []))
|
||||||
let updateState: ((NotificationSoundSelectionState) -> NotificationSoundSelectionState) -> Void = { f in
|
let updateState: ((NotificationSoundSelectionState) -> NotificationSoundSelectionState) -> Void = { f in
|
||||||
statePromise.set(stateValue.modify { f($0) })
|
statePromise.set(stateValue.modify { f($0) })
|
||||||
}
|
}
|
||||||
@ -355,7 +374,11 @@ public func notificationSoundSelectionController(context: AccountContext, update
|
|||||||
|
|
||||||
let arguments = NotificationSoundSelectionArguments(account: context.account, selectSound: { sound in
|
let arguments = NotificationSoundSelectionArguments(account: context.account, selectSound: { sound in
|
||||||
updateState { state in
|
updateState { state in
|
||||||
return NotificationSoundSelectionState(selectedSound: sound)
|
var state = state
|
||||||
|
|
||||||
|
state.selectedSound = sound
|
||||||
|
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = (context.engine.peers.notificationSoundList()
|
let _ = (context.engine.peers.notificationSoundList()
|
||||||
@ -369,6 +392,25 @@ public func notificationSoundSelectionController(context: AccountContext, update
|
|||||||
cancelImpl?()
|
cancelImpl?()
|
||||||
}, upload: {
|
}, upload: {
|
||||||
presentFilePicker?()
|
presentFilePicker?()
|
||||||
|
}, deleteSound: { sound in
|
||||||
|
updateState { state in
|
||||||
|
var state = state
|
||||||
|
|
||||||
|
state.removedSounds.append(sound)
|
||||||
|
if state.selectedSound.id == sound.id {
|
||||||
|
state.selectedSound = .bundledModern(id: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
switch sound {
|
||||||
|
case let .cloud(id):
|
||||||
|
soundActionDisposable.set((context.engine.peers.deleteNotificationSound(fileId: id)
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
}))
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
|
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
|
||||||
@ -432,20 +474,32 @@ public func notificationSoundSelectionController(context: AccountContext, update
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if data.count > 200 * 1024 {
|
if data.count > 200 * 1024 {
|
||||||
presentUndo?(.info(title: "Audio is too large", text: "The file is larger than 200 KB."))
|
//TODO:localize
|
||||||
|
presentUndo?(.info(title: "Audio is too large", text: "The file is over 200 KB."))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
asset.loadValuesAsynchronously(forKeys: ["duration"], completionHandler: {
|
asset.loadValuesAsynchronously(forKeys: ["tracks", "duration"], completionHandler: {
|
||||||
let duration = asset.duration.seconds
|
if asset.statusOfValue(forKey: "duration", error: nil) != .loaded {
|
||||||
if duration > 5.0 {
|
return
|
||||||
//TODO:localize
|
}
|
||||||
presentUndo?(.info(title: "Audio is too long", text: "The duration is longer than 5 seconds."))
|
guard let track = asset.tracks(withMediaType: .audio).first else {
|
||||||
} else {
|
return
|
||||||
soundActionDisposable.set((context.engine.peers.uploadNotificationSound(title: url.lastPathComponent, data: data)
|
}
|
||||||
|> deliverOnMainQueue).start(next: { _ in
|
let duration = track.timeRange.duration.seconds
|
||||||
}, error: { _ in
|
|
||||||
}))
|
Queue.mainQueue().async {
|
||||||
|
if duration > 5.0 {
|
||||||
|
//TODO:localize
|
||||||
|
presentUndo?(.info(title: "\(url.lastPathComponent) is too long", text: "The duration is longer than 5 seconds."))
|
||||||
|
} else {
|
||||||
|
soundActionDisposable.set((context.engine.peers.uploadNotificationSound(title: url.lastPathComponent, data: data)
|
||||||
|
|> deliverOnMainQueue).start(next: { _ in
|
||||||
|
//TODO:localize
|
||||||
|
presentUndo?(.notificationSoundAdded(title: "Sound Added", text: "The sound **\(url.deletingPathExtension().lastPathComponent)** was added to your Telegram tones.**", action: nil))
|
||||||
|
}, error: { _ in
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -279,3 +279,59 @@ func _internal_uploadNotificationSound(account: Account, title: String, data: Da
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum DeleteNotificationSoundError {
|
||||||
|
case generic
|
||||||
|
}
|
||||||
|
|
||||||
|
func _internal_deleteNotificationSound(account: Account, fileId: Int64) -> Signal<Never, DeleteNotificationSoundError> {
|
||||||
|
return account.postbox.transaction { transaction -> NotificationSoundList.NotificationSound? in
|
||||||
|
return _internal_cachedNotificationSoundList(transaction: transaction).flatMap { list -> NotificationSoundList.NotificationSound? in
|
||||||
|
return list.sounds.first(where: { $0.file.fileId.id == fileId })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> castError(DeleteNotificationSoundError.self)
|
||||||
|
|> mapToSignal { sound -> Signal<Never, DeleteNotificationSoundError> in
|
||||||
|
guard let sound = sound else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
guard let resource = sound.file.resource as? CloudDocumentMediaResource else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
|
||||||
|
return account.network.request(Api.functions.account.saveRingtone(id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)), unsave: .boolTrue))
|
||||||
|
|> mapError { _ -> DeleteNotificationSoundError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
|> mapToSignal { _ -> Signal<Never, DeleteNotificationSoundError> in
|
||||||
|
return account.postbox.transaction { transaction -> Void in
|
||||||
|
if let notificationSoundList = _internal_cachedNotificationSoundList(transaction: transaction) {
|
||||||
|
let updatedNotificationSoundList = NotificationSoundList(hash: notificationSoundList.hash, sounds: notificationSoundList.sounds.filter { item in
|
||||||
|
return item.file.fileId.id != fileId
|
||||||
|
})
|
||||||
|
_internal_setCachedNotificationSoundList(transaction: transaction, notificationSoundList: updatedNotificationSoundList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> castError(DeleteNotificationSoundError.self)
|
||||||
|
|> ignoreValues
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func resolvedNotificationSound(sound: PeerMessageSound, notificationSoundList: NotificationSoundList?) -> PeerMessageSound {
|
||||||
|
switch sound {
|
||||||
|
case let .cloud(fileId):
|
||||||
|
if let notificationSoundList = notificationSoundList {
|
||||||
|
for listSound in notificationSoundList.sounds {
|
||||||
|
if listSound.file.fileId.id == fileId {
|
||||||
|
return sound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .bundledModern(id: 0)
|
||||||
|
} else {
|
||||||
|
return .default
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return sound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -703,6 +703,10 @@ public extension TelegramEngine {
|
|||||||
public func uploadNotificationSound(title: String, data: Data) -> Signal<NotificationSoundList.NotificationSound, UploadNotificationSoundError> {
|
public func uploadNotificationSound(title: String, data: Data) -> Signal<NotificationSoundList.NotificationSound, UploadNotificationSoundError> {
|
||||||
return _internal_uploadNotificationSound(account: self.account, title: title, data: data)
|
return _internal_uploadNotificationSound(account: self.account, title: title, data: data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func deleteNotificationSound(fileId: Int64) -> Signal<Never, DeleteNotificationSoundError> {
|
||||||
|
return _internal_deleteNotificationSound(account: self.account, fileId: fileId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,30 @@ private func soundName(strings: PresentationStrings, sound: PeerMessageSound, no
|
|||||||
}
|
}
|
||||||
for sound in notificationSoundList.sounds {
|
for sound in notificationSoundList.sounds {
|
||||||
if sound.file.fileId.id == fileId {
|
if sound.file.fileId.id == fileId {
|
||||||
return sound.file.fileName ?? "Cloud Tone"
|
for attribute in sound.file.attributes {
|
||||||
|
switch attribute {
|
||||||
|
case let .Audio(_, _, title, performer, _):
|
||||||
|
if let title = title, !title.isEmpty, let performer = performer, !performer.isEmpty {
|
||||||
|
return "\(title) - \(performer)"
|
||||||
|
} else if let title = title, !title.isEmpty {
|
||||||
|
return title
|
||||||
|
} else if let performer = performer, !performer.isEmpty {
|
||||||
|
return performer
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let fileName = sound.file.fileName, !fileName.isEmpty {
|
||||||
|
if let range = fileName.range(of: ".", options: .backwards) {
|
||||||
|
return String(fileName[fileName.startIndex ..< range.lowerBound])
|
||||||
|
} else {
|
||||||
|
return fileName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Cloud Tone"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
@ -71,9 +94,16 @@ public func localizedPeerNotificationSoundString(strings: PresentationStrings, n
|
|||||||
}
|
}
|
||||||
return strings.UserInfo_NotificationsDefaultSound(actualName).string
|
return strings.UserInfo_NotificationsDefaultSound(actualName).string
|
||||||
} else {
|
} else {
|
||||||
return strings.UserInfo_NotificationsDefault
|
let name = soundName(strings: strings, sound: .bundledModern(id: 0), notificationSoundList: notificationSoundList)
|
||||||
|
return name
|
||||||
|
//return strings.UserInfo_NotificationsDefault
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return soundName(strings: strings, sound: sound, notificationSoundList: notificationSoundList)
|
let name = soundName(strings: strings, sound: sound, notificationSoundList: notificationSoundList)
|
||||||
|
if name.isEmpty {
|
||||||
|
return localizedPeerNotificationSoundString(strings: strings, notificationSoundList: notificationSoundList, sound: .default, default: `default`)
|
||||||
|
} else {
|
||||||
|
return name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8222,6 +8222,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||||
|
|
||||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceNode: node, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomInset, right: 0.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceNode: node, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomInset, right: 0.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||||
|
contextController.workaroundUseLegacyImplementation = true
|
||||||
contextController.dismissed = { [weak self] in
|
contextController.dismissed = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.updateChatPresentationInterfaceState(interactive: true, {
|
strongSelf.updateChatPresentationInterfaceState(interactive: true, {
|
||||||
|
@ -875,7 +875,8 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
actions.append(.action(ContextMenuActionItem(text: "Save for Notifications", icon: { theme in
|
actions.append(.action(ContextMenuActionItem(text: "Save for Notifications", icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/DownloadTone"), color: theme.actionSheet.primaryTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/DownloadTone"), color: theme.actionSheet.primaryTextColor)
|
||||||
}, action: { _, f in
|
}, action: { _, f in
|
||||||
let _ = context.engine.peers.saveNotificationSound(file: .message(message: MessageReference(message), media: file)).start(completed: {
|
let _ = (context.engine.peers.saveNotificationSound(file: .message(message: MessageReference(message), media: file))
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
controllerInteraction.displayUndo(.notificationSoundAdded(title: "Sound added", text: "You can now use this sound as a notification tone in your [custom notification settings]().", action: {
|
controllerInteraction.displayUndo(.notificationSoundAdded(title: "Sound added", text: "You can now use this sound as a notification tone in your [custom notification settings]().", action: {
|
||||||
controllerInteraction.navigationController()?.pushViewController(notificationsAndSoundsController(context: context, exceptionsList: nil))
|
controllerInteraction.navigationController()?.pushViewController(notificationsAndSoundsController(context: context, exceptionsList: nil))
|
||||||
|
@ -766,7 +766,9 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
|
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
|
||||||
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
|
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
|
||||||
let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor)
|
let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor)
|
||||||
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural)
|
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { contents in
|
||||||
|
return ("URL", contents)
|
||||||
|
}), textAlignment: .natural)
|
||||||
self.textNode.attributedText = attributedText
|
self.textNode.attributedText = attributedText
|
||||||
|
|
||||||
self.textNode.maximumNumberOfLines = 5
|
self.textNode.maximumNumberOfLines = 5
|
||||||
@ -774,6 +776,13 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
self.originalRemainingSeconds = 5
|
self.originalRemainingSeconds = 5
|
||||||
|
|
||||||
if let action = action {
|
if let action = action {
|
||||||
|
self.textNode.highlightAttributeAction = { attributes in
|
||||||
|
if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] {
|
||||||
|
return NSAttributedString.Key(rawValue: "URL")
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
self.textNode.tapAttributeAction = { attributes, _ in
|
self.textNode.tapAttributeAction = { attributes, _ in
|
||||||
if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] {
|
if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] {
|
||||||
action()
|
action()
|
||||||
@ -820,15 +829,33 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
switch content {
|
switch content {
|
||||||
case .removedChat:
|
case .removedChat:
|
||||||
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
||||||
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak, .sticker, .copy, .mediaSaved, .paymentSent, .image, .inviteRequestSent, .notificationSoundAdded:
|
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak, .sticker, .copy, .mediaSaved, .paymentSent, .image, .inviteRequestSent, .notificationSoundAdded:
|
||||||
break
|
if self.textNode.tapAttributeAction != nil {
|
||||||
case .dice:
|
self.isUserInteractionEnabled = true
|
||||||
self.panelWrapperNode.clipsToBounds = true
|
} else {
|
||||||
case .info:
|
|
||||||
self.isUserInteractionEnabled = false
|
self.isUserInteractionEnabled = false
|
||||||
|
}
|
||||||
|
case .dice:
|
||||||
|
self.panelWrapperNode.clipsToBounds = true
|
||||||
|
case .info:
|
||||||
|
if self.textNode.tapAttributeAction != nil {
|
||||||
|
self.isUserInteractionEnabled = true
|
||||||
|
} else {
|
||||||
|
self.isUserInteractionEnabled = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.titleNode.isUserInteractionEnabled = false
|
||||||
|
self.textNode.isUserInteractionEnabled = self.textNode.tapAttributeAction != nil
|
||||||
|
self.iconNode?.isUserInteractionEnabled = false
|
||||||
|
self.animationNode?.isUserInteractionEnabled = false
|
||||||
|
self.iconCheckNode?.isUserInteractionEnabled = false
|
||||||
|
self.avatarNode?.isUserInteractionEnabled = false
|
||||||
|
self.slotMachineNode?.isUserInteractionEnabled = false
|
||||||
|
self.animatedStickerNode?.isUserInteractionEnabled = false
|
||||||
|
|
||||||
self.statusNode.flatMap(self.panelWrapperNode.addSubnode)
|
self.statusNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||||
self.iconNode.flatMap(self.panelWrapperNode.addSubnode)
|
self.iconNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||||
self.iconCheckNode.flatMap(self.panelWrapperNode.addSubnode)
|
self.iconCheckNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||||
@ -837,9 +864,9 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode)
|
self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||||
self.slotMachineNode.flatMap(self.panelWrapperNode.addSubnode)
|
self.slotMachineNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||||
self.avatarNode.flatMap(self.panelWrapperNode.addSubnode)
|
self.avatarNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||||
|
self.panelWrapperNode.addSubnode(self.buttonNode)
|
||||||
self.panelWrapperNode.addSubnode(self.titleNode)
|
self.panelWrapperNode.addSubnode(self.titleNode)
|
||||||
self.panelWrapperNode.addSubnode(self.textNode)
|
self.panelWrapperNode.addSubnode(self.textNode)
|
||||||
self.panelWrapperNode.addSubnode(self.buttonNode)
|
|
||||||
if displayUndo {
|
if displayUndo {
|
||||||
self.panelWrapperNode.addSubnode(self.undoButtonTextNode)
|
self.panelWrapperNode.addSubnode(self.undoButtonTextNode)
|
||||||
self.panelWrapperNode.addSubnode(self.undoButtonNode)
|
self.panelWrapperNode.addSubnode(self.undoButtonNode)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user