Merge commit '8ce13713b9fbcdd196efa4a715eaddd084e472b6'

This commit is contained in:
Ali 2022-03-12 16:16:01 +04:00
commit b82b1e41e0
31 changed files with 874 additions and 556 deletions

View File

@ -7284,6 +7284,7 @@ Sorry for the inconvenience.";
"Attachment.Ungrouped" = "Ungrouped";
"Attachment.MessagePreview" = "Message Preview";
"Attachment.MessagesPreview" = "Messages Preview";
"Attachment.DragToReorder" = "Drag media to reorder";
"Attachment.SearchWeb" = "Search Web";
@ -7352,6 +7353,10 @@ Sorry for the inconvenience.";
"Attachment.LocationAccessTitle" = "Access Your Location";
"Attachment.LocationAccessText" = "Share places or your live location.";
"Attachment.CancelSelectionAlertText" = "Cancel selection?";
"Attachment.CancelSelectionAlertYes" = "Yes";
"Attachment.CancelSelectionAlertNo" = "No";
"ChannelInfo.CreateExternalStream" = "Stream With...";
"CreateExternalStream.Title" = "Stream With...";

View File

@ -207,18 +207,24 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate {
if currentOffset > 0.0, let scrollView = scrollView {
scrollView.panGestureRecognizer.setTranslation(CGPoint(), in: scrollView)
}
var bounds = self.bounds
bounds.origin.y = -translation
bounds.origin.y = min(0.0, bounds.origin.y)
self.bounds = bounds
}
var bounds = self.bounds
if self.isExpanded {
bounds.origin.y = -max(0.0, translation - edgeTopInset)
} else {
bounds.origin.y = -translation
}
bounds.origin.y = min(0.0, bounds.origin.y)
self.bounds = bounds
self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .immediate)
case .ended:
guard let (currentTopInset, panOffset, scrollView, listNode) = self.panGestureArguments else {
return
}
self.panGestureArguments = nil
let visibleContentOffset = listNode?.visibleContentOffset()
let contentOffset = scrollView?.contentOffset.y ?? 0.0
@ -236,15 +242,23 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate {
}
var bounds = self.bounds
bounds.origin.y = -translation
if self.isExpanded {
bounds.origin.y = -max(0.0, translation - edgeTopInset)
} else {
bounds.origin.y = -translation
}
bounds.origin.y = min(0.0, bounds.origin.y)
scrollView?.bounces = true
let offset = currentTopInset + panOffset
let topInset: CGFloat = edgeTopInset
if self.isExpanded {
self.panGestureArguments = nil
var dismissing = false
if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) || (self.isExpanded && bounds.minY.isZero && velocity.y > 600.0) {
self.interactivelyDismissed?()
dismissing = true
} else if self.isExpanded {
if velocity.y > 300.0 || offset > topInset / 2.0 {
self.isExpanded = false
if let listNode = listNode {
@ -262,42 +276,34 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate {
self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .animated(duration: 0.3, curve: .easeInOut))
}
} else if (velocity.y < -300.0 || offset < topInset / 2.0) {
if velocity.y > -2200.0 && velocity.y < -300.0, let listNode = listNode {
DispatchQueue.main.async {
listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
}
}
let initialVelocity: CGFloat = offset.isZero ? 0.0 : abs(velocity.y / offset)
let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity))
self.isExpanded = true
self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: transition)
} else {
self.panGestureArguments = nil
var dismissing = false
if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) {
self.interactivelyDismissed?()
dismissing = true
} else if (velocity.y < -300.0 || offset < topInset / 2.0) {
if velocity.y > -2200.0 && velocity.y < -300.0, let listNode = listNode {
DispatchQueue.main.async {
listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
}
}
let initialVelocity: CGFloat = offset.isZero ? 0.0 : abs(velocity.y / offset)
let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity))
self.isExpanded = true
self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: transition)
} else {
if let listNode = listNode {
listNode.scroller.setContentOffset(CGPoint(), animated: false)
} else if let scrollView = scrollView {
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false)
}
self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .animated(duration: 0.3, curve: .easeInOut))
if let listNode = listNode {
listNode.scroller.setContentOffset(CGPoint(), animated: false)
} else if let scrollView = scrollView {
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false)
}
if !dismissing {
var bounds = self.bounds
let previousBounds = bounds
bounds.origin.y = 0.0
self.bounds = bounds
self.layer.animateBounds(from: previousBounds, to: self.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
}
self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .animated(duration: 0.3, curve: .easeInOut))
}
if !dismissing {
var bounds = self.bounds
let previousBounds = bounds
bounds.origin.y = 0.0
self.bounds = bounds
self.layer.animateBounds(from: previousBounds, to: self.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
}
case .cancelled:
self.panGestureArguments = nil

View File

@ -28,6 +28,8 @@ public protocol AttachmentContainable: ViewController {
func resetForReuse()
func prepareForReuse()
func requestDismiss(completion: @escaping () -> Void)
}
public extension AttachmentContainable {
@ -38,6 +40,10 @@ public extension AttachmentContainable {
func prepareForReuse() {
}
func requestDismiss(completion: @escaping () -> Void) {
completion()
}
}
public enum AttachmentMediaPickerSendMode {
@ -274,7 +280,13 @@ public class AttachmentController: ViewController {
@objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.controller?.dismiss(animated: true)
if let controller = self.currentControllers.last {
controller.requestDismiss(completion: { [weak self] in
self?.controller?.dismiss(animated: true)
})
} else {
self.controller?.dismiss(animated: true)
}
}
}

View File

@ -184,7 +184,7 @@ public final class AvatarNode: ASDisplayNode {
public var font: UIFont {
didSet {
if oldValue !== font {
if oldValue.pointSize != font.pointSize {
if let parameters = self.parameters {
self.parameters = AvatarNodeParameters(theme: parameters.theme, accountPeerId: parameters.accountPeerId, peerId: parameters.peerId, letters: parameters.letters, font: self.font, icon: parameters.icon, explicitColorIndex: parameters.explicitColorIndex, hasImage: parameters.hasImage, clipStyle: parameters.clipStyle)
}

View File

@ -519,7 +519,7 @@ public final class ComposedPoll {
private class CreatePollControllerImpl: ItemListController, AttachmentContainable {
public var requestAttachmentMenuExpansion: () -> Void = {}
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
public var cancelPanGesture: () -> Void = { }
}

View File

@ -1499,10 +1499,14 @@ public final class ContactListNode: ASDisplayNode {
self.indexNode.update(size: indexNodeFrame.size, color: self.presentationData.theme.list.itemAccentColor, sections: indexSections, transition: transition)
}
let permissionSize = CGSize(width: layout.size.width, height: layout.size.height - 160.0)
var permissionInsets = insets
permissionInsets.bottom += 100.0
self.authorizationNode.updateLayout(size: permissionSize, insets: permissionInsets, transition: transition)
if self.multipleSelection {
let permissionSize = CGSize(width: layout.size.width, height: layout.size.height - 160.0)
var permissionInsets = insets
permissionInsets.bottom += 100.0
self.authorizationNode.updateLayout(size: permissionSize, insets: permissionInsets, transition: transition)
} else {
self.authorizationNode.updateLayout(size: layout.size, insets: insets, transition: transition)
}
transition.updateFrame(node: self.authorizationNode, frame: self.bounds)
if !hadValidLayout {

View File

@ -439,7 +439,6 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
}
}
private let avatarFont = avatarPlaceholderFont(size: floor(40.0 * 16.0 / 37.0))
private let badgeFont = Font.regular(15.0)
public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNode {
@ -503,7 +502,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
self.containerNode = ContextControllerSourceNode()
self.avatarNode = AvatarNode(font: avatarFont)
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: floor(40.0 * 16.0 / 37.0)))
//self.avatarNode.isLayerBacked = !smartInvertColorsEnabled()
self.titleNode = TextNode()
@ -763,6 +762,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
let verticalInset: CGFloat
let verticalOffset: CGFloat
let avatarSize: CGFloat
let avatarFontSize: CGFloat
switch item.height {
case .generic:
if case .none = item.text {
@ -773,6 +773,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
verticalOffset = 0.0
avatarSize = 31.0
leftInset = 59.0 + params.leftInset
avatarFontSize = floor(31.0 * 16.0 / 37.0)
case .peerList:
if case .none = item.text {
verticalInset = 14.0
@ -782,6 +783,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
verticalOffset = 0.0
avatarSize = 40.0
leftInset = 65.0 + params.leftInset
avatarFontSize = floor(40.0 * 16.0 / 37.0)
}
var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)?
@ -887,6 +889,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
strongSelf.containerNode.isGestureEnabled = item.contextAction != nil
strongSelf.avatarNode.font = avatarPlaceholderFont(size: avatarFontSize)
strongSelf.accessibilityLabel = titleAttributedString?.string
var combinedValueString = ""
if let statusString = statusAttributedString?.string, !statusString.isEmpty {

View File

@ -41,6 +41,7 @@
- (SSignal *)selectionChangedSignal;
- (void)enumerateSelectedItems:(void (^)(id<TGMediaSelectableItem>))enumerationBlock;
- (void)enumerateDeselectedItems:(void (^)(id<TGMediaSelectableItem>))enumerationBlock;
- (NSOrderedSet *)selectedItemsIdentifiers;
- (NSArray *)selectedItems;

View File

@ -140,6 +140,10 @@
if (strongSelf == nil)
return;
strongSelf->_ignoreSelectionUpdates = true;
[strongSelf->_selectionChangedDisposable dispose];
[strongSelf->_itemSelectedDisposable dispose];
[strongSelf.window endEditing:true];
strongSelf->_portraitToolbarView.doneButton.userInteractionEnabled = false;
strongSelf->_landscapeToolbarView.doneButton.userInteractionEnabled = false;

View File

@ -188,7 +188,7 @@
if (enumerationBlock == nil)
return;
for (NSArray *identifier in _selectedIdentifiers)
for (NSString *identifier in _selectedIdentifiers)
{
NSObject<TGMediaSelectableItem> *item = _selectionMap[identifier];
if (item != nil) {
@ -197,6 +197,22 @@
}
}
- (void)enumerateDeselectedItems:(void (^)(id<TGMediaSelectableItem>))enumerationBlock
{
if (enumerationBlock == nil || _savedSelectedIdentifiers == nil)
return;
for (NSString *identifier in _savedSelectedIdentifiers)
{
if (![_selectedIdentifiers containsObject:identifier]) {
NSObject<TGMediaSelectableItem> *item = _selectionMap[identifier];
if (item != nil) {
enumerationBlock(item);
}
}
}
}
- (NSOrderedSet *)selectedItemsIdentifiers
{
return [[NSOrderedSet alloc] initWithArray:_selectedIdentifiers];

View File

@ -191,7 +191,6 @@
UIView *contentView = [itemView transitionContentView];
UIView *snapshotView = [contentView snapshotViewAfterScreenUpdates:true];
snapshotView.frame = [contentView convertRect:contentView.bounds toView:nil];
// snapshotView.frame = CGRectOffset([contentView convertRect:contentView.bounds toView:nil], 0.0, -self.view.frame.size.height);
return snapshotView;
}
}

View File

@ -379,12 +379,14 @@ static const CGFloat swipeDistanceThreshold = 128.0f;
completion();
}];
[UIView animateWithDuration:ABS(distance / velocity) animations:^
{
[UIView animateWithDuration:0.15 animations:^{
_interfaceView.alpha = 0.0f;
_overlayContainerView.alpha = 0.0f;
}];
[UIView animateWithDuration:0.35 animations:^{
self.backgroundColor = UIColorRGBA(0x000000, 0.0f);
} completion:nil];
}];
}
- (void)transitionInWithDuration:(NSTimeInterval)duration

View File

@ -88,8 +88,7 @@ public final class LocationPickerController: ViewController, AttachmentContainab
self.title = self.presentationData.strings.Map_ChooseLocationTitle
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.searchPressed))
self.navigationItem.rightBarButtonItem?.accessibilityLabel = self.presentationData.strings.Common_Search
self.updateBarButtons()
self.presentationDataDisposable = ((updatedPresentationData?.signal ?? context.sharedContext.presentationData)
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
@ -100,7 +99,8 @@ public final class LocationPickerController: ViewController, AttachmentContainab
strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: strongSelf.presentationData.theme).withUpdatedSeparatorColor(.clear), strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings)))
strongSelf.searchNavigationContentNode?.updatePresentationData(strongSelf.presentationData)
strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(strongSelf.presentationData.theme), style: .plain, target: strongSelf, action: #selector(strongSelf.searchPressed))
strongSelf.updateBarButtons()
if strongSelf.isNodeLoaded {
strongSelf.controllerNode.updatePresentationData(presentationData)
@ -288,6 +288,16 @@ public final class LocationPickerController: ViewController, AttachmentContainab
self.isSearchingDisposable.dispose()
}
private var locationAccessDenied = false
private func updateBarButtons() {
if self.locationAccessDenied {
self.navigationItem.rightBarButtonItem = nil
} else {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.searchPressed))
self.navigationItem.rightBarButtonItem?.accessibilityLabel = self.presentationData.strings.Common_Search
}
}
override public func loadDisplayNode() {
super.loadDisplayNode()
guard let interaction = self.interaction else {
@ -299,6 +309,10 @@ public final class LocationPickerController: ViewController, AttachmentContainab
self.controllerNode.beganInteractiveDragging = { [weak self] in
self?.requestAttachmentMenuExpansion()
}
self.controllerNode.locationAccessDeniedUpdated = { [weak self] denied in
self?.locationAccessDenied = denied
self?.updateBarButtons()
}
self.permissionDisposable = (DeviceAccess.authorizationStatus(subject: .location(.send))
|> deliverOnMainQueue).start(next: { [weak self] next in

View File

@ -313,6 +313,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
private var listOffset: CGFloat?
var beganInteractiveDragging: () -> Void = {}
var locationAccessDeniedUpdated: (Bool) -> Void = { _ in }
init(controller: LocationPickerController, context: AccountContext, presentationData: PresentationData, mode: LocationPickerMode, interaction: LocationPickerInteraction, locationManager: LocationManager) {
self.controller = controller
@ -639,11 +640,13 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
if [.denied, .restricted].contains(access) {
if !strongSelf.locationAccessDenied {
strongSelf.locationAccessDenied = true
strongSelf.locationAccessDeniedUpdated(true)
updateLayout = true
}
} else {
if strongSelf.locationAccessDenied {
strongSelf.locationAccessDenied = false
strongSelf.locationAccessDeniedUpdated(false)
updateLayout = true
}
}
@ -998,6 +1001,8 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
self.placeholderBackgroundNode = nil
placeholderBackgroundNode.removeFromSupernode()
}
self.controller?.navigationBar?.updateBackgroundAlpha(1.0, transition: .immediate)
self.controller?.updateTabBarAlpha(1.0, .immediate)
}

View File

@ -19,6 +19,7 @@ import ContextUI
import WebSearchUI
import SparseItemGrid
import UndoUI
import PresentationDataUtils
let overflowInset: CGFloat = 0.0
@ -687,7 +688,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
return
}
self.controller?.legacyCompletion(signals, silently, scheduleTime, { [weak self] identifier in
return self?.getItemSnapshot(identifier)
return !asFile ? self?.getItemSnapshot(identifier) : nil
}, { [weak self] in
completion()
self?.controller?.dismiss(animated: animated)
@ -1155,7 +1156,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
selectionState.setItem(item, selected: value)
if showUndo {
strongSelf.showSelectionUndo(item: item, count: Int32(selectionState.savedStateDifference()))
strongSelf.showSelectionUndo(item: item)
}
}
}, sendSelected: { [weak self] currentItem, silently, scheduleTime, animated, completion in
@ -1198,7 +1199,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
private weak var undoOverlayController: UndoOverlayController?
private func showSelectionUndo(item: TGMediaSelectableItem, count: Int32) {
private func showSelectionUndo(item: TGMediaSelectableItem) {
var asset: PHAsset?
if let item = item as? TGMediaAsset {
asset = item.backingAsset
@ -1218,12 +1219,36 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
return
}
var photosCount = 0
var videosCount = 0
strongSelf.interaction?.selectionState?.enumerateDeselectedItems({ item in
if let item = item as? TGMediaAsset {
if item.isVideo {
videosCount += 1
} else {
photosCount += 1
}
} else if let _ = item as? TGCameraCapturedVideo {
videosCount += 1
}
})
let totalCount = Int32(photosCount + videosCount)
let presentationData = strongSelf.presentationData
let text: String
if photosCount > 0 && videosCount > 0 {
text = presentationData.strings.Attachment_DeselectedItems(totalCount)
} else if photosCount > 0 {
text = presentationData.strings.Attachment_DeselectedPhotos(totalCount)
} else if videosCount > 0 {
text = presentationData.strings.Attachment_DeselectedVideos(totalCount)
} else {
text = presentationData.strings.Attachment_DeselectedItems(totalCount)
}
if let undoOverlayController = strongSelf.undoOverlayController {
let text = presentationData.strings.Attachment_DeselectedItems(count)
undoOverlayController.content = .image(image: image ?? UIImage(), text: text)
} else {
let text = presentationData.strings.Attachment_DeselectedItems(count)
let undoOverlayController = UndoOverlayController(presentationData: presentationData, content: .image(image: image ?? UIImage(), text: text), elevatedLayout: true, action: { [weak self] action in
guard let strongSelf = self else {
return true
@ -1282,8 +1307,23 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.undoOverlayController?.dismissWithCommitAction()
}
public func requestDismiss(completion: @escaping () -> Void) {
if let selectionState = self.interaction?.selectionState, selectionState.count() > 0 {
let controller = textAlertController(context: self.context, title: nil, text: self.presentationData.strings.Attachment_CancelSelectionAlertText, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertNo, action: {
}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertYes, action: { [weak self] in
self?.dismissAllTooltips()
completion()
})])
self.present(controller, in: .window(.root))
} else {
completion()
}
}
@objc private func cancelPressed() {
self.dismissAllTooltips()
self.dismiss()
}

View File

@ -6,6 +6,7 @@ import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramPresentationData
import TelegramStringFormatting
import LegacyComponents
import CheckNode
import MosaicLayout
@ -13,7 +14,6 @@ import WallpaperBackgroundNode
import AccountContext
import ChatMessageBackground
private class MediaPickerSelectedItemNode: ASDisplayNode {
let asset: TGMediaAsset
private let interaction: MediaPickerInteraction?
@ -23,6 +23,8 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
private var durationBackgroundNode: ASDisplayNode?
private var durationTextNode: ImmediateTextNode?
private var adjustmentsDisposable: Disposable?
private var theme: PresentationTheme?
private var validLayout: CGSize?
@ -49,6 +51,8 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
return self.readyPromise.get()
}
private var videoDuration: Double?
init(asset: TGMediaAsset, interaction: MediaPickerInteraction?) {
self.imageNode = ImageNode()
self.imageNode.contentMode = .scaleAspectFill
@ -63,6 +67,44 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
self.clipsToBounds = true
self.addSubnode(self.imageNode)
if asset.isVideo, let editingState = interaction?.editingState {
func adjustmentsChangedSignal(editingState: TGMediaEditingContext) -> Signal<TGMediaEditAdjustments?, NoError> {
return Signal { subscriber in
let disposable = editingState.adjustmentsSignal(for: asset).start(next: { next in
if let next = next as? TGMediaEditAdjustments {
subscriber.putNext(next)
} else if next == nil {
subscriber.putNext(nil)
}
}, error: nil, completed: {})
return ActionDisposable {
disposable?.dispose()
}
}
}
self.adjustmentsDisposable = (adjustmentsChangedSignal(editingState: editingState)
|> deliverOnMainQueue).start(next: { [weak self] adjustments in
if let strongSelf = self {
let duration: Double
if let adjustments = adjustments as? TGVideoEditAdjustments, adjustments.trimApplied() {
duration = adjustments.trimEndValue - adjustments.trimStartValue
} else {
duration = asset.videoDuration
}
strongSelf.videoDuration = duration
if let size = strongSelf.validLayout {
strongSelf.updateLayout(size: size, transition: .immediate)
}
}
})
}
}
deinit {
self.adjustmentsDisposable?.dispose()
}
override func didLoad() {
@ -158,6 +200,13 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
if let checkNode = self.checkNode, checkNode.alpha > 0.0 {
checkNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
if let durationTextNode = self.durationTextNode, durationTextNode.alpha > 0.0 {
durationTextNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
if let durationBackgroundNode = self.durationBackgroundNode, durationBackgroundNode.alpha > 0.0 {
durationBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
}
@ -182,6 +231,42 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
if let checkNode = self.checkNode {
transition.updateFrame(node: checkNode, frame: CGRect(origin: CGPoint(x: size.width - checkSize.width - 3.0, y: 3.0), size: checkSize))
}
if let duration = self.videoDuration {
let textNode: ImmediateTextNode
let backgroundNode: ASDisplayNode
if let currentTextNode = self.durationTextNode, let currentBackgroundNode = self.durationBackgroundNode {
textNode = currentTextNode
backgroundNode = currentBackgroundNode
} else {
backgroundNode = ASDisplayNode()
backgroundNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.5)
backgroundNode.cornerRadius = 9.0
self.addSubnode(backgroundNode)
self.durationBackgroundNode = backgroundNode
textNode = ImmediateTextNode()
textNode.displaysAsynchronously = false
self.addSubnode(textNode)
self.durationTextNode = textNode
}
textNode.attributedText = NSAttributedString(string: stringForDuration(Int32(duration)), font: Font.with(size: 11.0, design: .regular, weight: .regular, traits: .monospacedNumbers), textColor: .white)
let textSize = textNode.updateLayout(size)
let backgroundFrame = CGRect(x: 6.0, y: 6.0, width: ceil(textSize.width) + 14.0, height: 18.0)
backgroundNode.frame = backgroundFrame
textNode.frame = CGRect(origin: CGPoint(x: backgroundFrame.minX + floorToScreenPixels((backgroundFrame.size.width - textSize.width) / 2.0), y: backgroundFrame.minY + floorToScreenPixels((backgroundFrame.size.height - textSize.height) / 2.0)), size: textSize)
} else {
if let durationTextNode = self.durationTextNode {
self.durationTextNode = nil
durationTextNode.removeFromSupernode()
}
if let durationBackgroundNode = self.durationBackgroundNode {
self.durationBackgroundNode = nil
durationBackgroundNode.removeFromSupernode()
}
}
}
func transitionView() -> UIView {
@ -203,6 +288,9 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
let frame = view.convert(view.bounds, to: self.supernode?.view)
let targetFrame = self.frame
self.durationTextNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.durationBackgroundNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.updateLayout(size: frame.size, transition: .immediate)
self.updateLayout(size: targetFrame.size, transition: .animated(duration: 0.25, curve: .spring))
self.layer.animateFrame(from: frame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak view] _ in
@ -218,11 +306,17 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
let corners = self.corners
self.durationTextNode?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.durationBackgroundNode?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.corners = []
self.updateLayout(size: targetFrame.size, transition: .animated(duration: 0.25, curve: .spring))
self.layer.animateFrame(from: frame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { [weak view, weak self] _ in
view?.alpha = 1.0
self?.durationTextNode?.layer.removeAllAnimations()
self?.durationBackgroundNode?.layer.removeAllAnimations()
var animateCheckNode = false
if let strongSelf = self, let checkNode = strongSelf.checkNode, checkNode.alpha.isZero {
animateCheckNode = true
@ -541,53 +635,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1))
var peers = SimpleDictionary<PeerId, Peer>()
peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let previewMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: presentationData.strings.Attachment_MessagePreview, entities: []))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
let previewItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [previewMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, isCentered: true)
let dragMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: presentationData.strings.Attachment_DragToReorder, entities: []))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
let dragItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [dragMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, isCentered: true)
let headerItems: [ListViewItem] = [previewItem, dragItem]
let params = ListViewItemLayoutParams(width: size.width, leftInset: insets.left, rightInset: insets.right, availableHeight: size.height)
if let messageNodes = self.messageNodes {
for i in 0 ..< headerItems.count {
let itemNode = messageNodes[i]
headerItems[i].updateNode(async: { $0() }, node: {
return itemNode
}, params: params, previousItem: nil, nextItem: nil, animation: .None, completion: { (layout, apply) in
let nodeFrame = CGRect(origin: itemNode.frame.origin, size: CGSize(width: size.width, height: layout.size.height))
itemNode.contentSize = layout.contentSize
itemNode.insets = layout.insets
itemNode.frame = nodeFrame
itemNode.isUserInteractionEnabled = false
apply(ListViewItemApply(isOnScreen: true))
})
}
} else {
var messageNodes: [ListViewItemNode] = []
for i in 0 ..< headerItems.count {
var itemNode: ListViewItemNode?
headerItems[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: false, previousItem: nil, nextItem: nil, completion: { node, apply in
itemNode = node
apply().1(ListViewItemApply(isOnScreen: true))
})
itemNode!.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
itemNode!.isUserInteractionEnabled = false
messageNodes.append(itemNode!)
self.scrollNode.addSubnode(itemNode!)
}
self.messageNodes = messageNodes
}
var itemSizes: [CGSize] = []
let sideInset: CGFloat = 34.0
let boundingWidth = min(320.0, size.width - insets.left - insets.right - sideInset * 2.0)
@ -672,6 +720,53 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
}
}
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1))
var peers = SimpleDictionary<PeerId, Peer>()
peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let previewText = groupLayouts.count > 1 ? presentationData.strings.Attachment_MessagesPreview : presentationData.strings.Attachment_MessagePreview
let previewMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: previewText, entities: []))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
let previewItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [previewMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, isCentered: true)
let dragMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: presentationData.strings.Attachment_DragToReorder, entities: []))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
let dragItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [dragMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, isCentered: true)
let headerItems: [ListViewItem] = [previewItem, dragItem]
let params = ListViewItemLayoutParams(width: size.width, leftInset: insets.left, rightInset: insets.right, availableHeight: size.height)
if let messageNodes = self.messageNodes {
for i in 0 ..< headerItems.count {
let itemNode = messageNodes[i]
headerItems[i].updateNode(async: { $0() }, node: {
return itemNode
}, params: params, previousItem: nil, nextItem: nil, animation: .None, completion: { (layout, apply) in
let nodeFrame = CGRect(origin: itemNode.frame.origin, size: CGSize(width: size.width, height: layout.size.height))
itemNode.contentSize = layout.contentSize
itemNode.insets = layout.insets
itemNode.frame = nodeFrame
itemNode.isUserInteractionEnabled = false
apply(ListViewItemApply(isOnScreen: true))
})
}
} else {
var messageNodes: [ListViewItemNode] = []
for i in 0 ..< headerItems.count {
var itemNode: ListViewItemNode?
headerItems[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: false, previousItem: nil, nextItem: nil, completion: { node, apply in
itemNode = node
apply().1(ListViewItemApply(isOnScreen: true))
})
itemNode!.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
itemNode!.isUserInteractionEnabled = false
messageNodes.append(itemNode!)
self.scrollNode.addSubnode(itemNode!)
}
self.messageNodes = messageNodes
}
let spacing: CGFloat = 8.0
var contentHeight: CGFloat = 60.0

View File

@ -13,6 +13,7 @@ swift_library(
"//submodules/Display:Display",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/LegacyComponents:LegacyComponents",
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
],
visibility = [
"//visibility:public",

View File

@ -3,6 +3,7 @@ import UIKit
import Display
import AsyncDisplayKit
import LegacyComponents
import SwiftSignalKit
private final class RadialProgressContentCancelNodeParameters: NSObject {
let color: UIColor
@ -159,23 +160,28 @@ private final class RadialProgressContentSpinnerNode: ASDisplayNode {
}
}
private var hierarchyVersion: Int = 0
override func willEnterHierarchy() {
super.willEnterHierarchy()
if self.animateRotation {
let basicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
basicAnimation.duration = 1.5
var fromValue = Float.pi + 0.58
if let presentation = self.layer.presentation(), let value = (presentation.value(forKeyPath: "transform.rotation.z") as? NSNumber)?.floatValue {
fromValue = value
}
basicAnimation.fromValue = NSNumber(value: fromValue)
basicAnimation.toValue = NSNumber(value: fromValue + Float.pi * 2.0)
basicAnimation.repeatCount = Float.infinity
basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
basicAnimation.beginTime = 0.0
self.hierarchyVersion += 1
self.layer.add(basicAnimation, forKey: "progressRotation")
if self.layer.animation(forKey: "progressRotation") == nil {
let basicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
basicAnimation.duration = 1.5
var fromValue = Float.pi + 0.58
if let presentation = self.layer.presentation(), let value = (presentation.value(forKeyPath: "transform.rotation.z") as? NSNumber)?.floatValue {
fromValue = value
}
basicAnimation.fromValue = NSNumber(value: fromValue)
basicAnimation.toValue = NSNumber(value: fromValue + Float.pi * 2.0)
basicAnimation.repeatCount = Float.infinity
basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
basicAnimation.beginTime = 0.0
self.layer.add(basicAnimation, forKey: "progressRotation")
}
}
}
@ -183,7 +189,12 @@ private final class RadialProgressContentSpinnerNode: ASDisplayNode {
super.didExitHierarchy()
if self.animateRotation {
self.layer.removeAnimation(forKey: "progressRotation")
let version = self.hierarchyVersion
Queue.mainQueue().after(0.1, {
if self.hierarchyVersion == version {
self.layer.removeAnimation(forKey: "progressRotation")
}
})
}
}
}

View File

@ -181,7 +181,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
var authorNameCenter: CGFloat?
let forwardedString = item.strings.Message_ForwardedMessage("").string
let forwardedString = item.strings.Message_ForwardedMessageShort("").string
var fromString: String?
if let newlineRange = forwardedString.range(of: "\n") {
let from = forwardedString[newlineRange.upperBound...]

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "attach_hidden.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,105 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 3.315430 8.007324 cm
0.000000 0.000000 0.000000 scn
23.041992 0.435059 m
23.364258 0.102051 23.879883 0.080566 24.212891 0.435059 c
24.567383 0.789551 24.535156 1.272949 24.212891 1.605957 c
6.251953 19.556152 l
5.929688 19.878418 5.403320 19.878418 5.070312 19.556152 c
4.758789 19.244629 4.758789 18.696777 5.070312 18.385254 c
23.041992 0.435059 l
h
14.684570 18.943848 m
12.815430 18.943848 11.118164 18.621582 9.539062 18.095215 c
10.957031 16.687988 l
12.138672 17.042480 13.352539 17.246582 14.684570 17.246582 c
21.752930 17.246582 27.478516 11.016113 27.478516 9.737793 c
27.478516 8.867676 25.598633 6.525879 22.623047 4.699707 c
23.944336 3.378418 l
27.338867 5.580566 29.358398 8.470215 29.358398 9.737793 c
29.358398 11.939941 23.364258 18.943848 14.684570 18.943848 c
h
14.684570 0.531738 m
16.682617 0.531738 18.508789 0.886230 20.173828 1.455566 c
18.766602 2.862793 l
17.488281 2.465332 16.134766 2.229004 14.684570 2.229004 c
7.605469 2.229004 1.879883 8.223145 1.879883 9.737793 c
1.879883 10.489746 3.867188 12.992676 7.036133 14.894043 c
5.704102 16.226074 l
2.126953 14.013184 0.000000 11.037598 0.000000 9.737793 c
0.000000 7.546387 6.112305 0.531738 14.684570 0.531738 c
h
20.195312 7.385254 m
20.528320 8.104980 20.710938 8.910645 20.710938 9.737793 c
20.710938 13.100098 18.025391 15.753418 14.684570 15.753418 c
13.835938 15.753418 13.041016 15.570801 12.321289 15.259277 c
20.195312 7.385254 l
h
14.684570 3.722168 m
15.629883 3.722168 16.521484 3.958496 17.316406 4.345215 c
9.259766 12.401855 l
8.862305 11.606934 8.636719 10.704590 8.636719 9.737793 c
8.647461 6.461426 11.322266 3.722168 14.684570 3.722168 c
h
f
n
Q
endstream
endobj
3 0 obj
1716
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 36.000000 36.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001806 00000 n
0000001829 00000 n
0000002002 00000 n
0000002076 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
2135
%%EOF

View File

@ -10269,7 +10269,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let storeEditedPhotos = settings.storeEditedPhotos
let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText
presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: nil, parentController: strongSelf, attachmentController: self?.attachmentController, editingMedia: false, saveCapturedPhotos: storeEditedPhotos, mediaGrouping: true, initialCaption: inputText.string, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, photoOnly: photoOnly, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: nil, parentController: strongSelf, attachmentController: self?.attachmentController, editingMedia: false, saveCapturedPhotos: storeEditedPhotos, mediaGrouping: true, initialCaption: inputText, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, photoOnly: photoOnly, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
if let strongSelf = self {
strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil)
if !inputText.string.isEmpty {
@ -10755,7 +10755,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
photoOnly = true
}
presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: settings.storeEditedPhotos, mediaGrouping: true, initialCaption: inputText.string, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, photoOnly: photoOnly, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: settings.storeEditedPhotos, mediaGrouping: true, initialCaption: inputText, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, photoOnly: photoOnly, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
if let strongSelf = self {
if editMediaOptions != nil {
strongSelf.editMessageMediaWithLegacySignals(signals!)

View File

@ -191,14 +191,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
var telegramDice: TelegramMediaDice?
private let disposable = MetaDisposable()
private let disposables = DisposableSet()
private var forwardInfoNode: ChatMessageForwardInfoNode?
private var forwardBackgroundNode: NavigationBackgroundNode?
private var viaBotNode: TextNode?
private let dateAndStatusNode: ChatMessageDateAndStatusNode
private var replyInfoNode: ChatMessageReplyInfoNode?
private var replyBackgroundNode: NavigationBackgroundNode?
private var forwardInfoNode: ChatMessageForwardInfoNode?
private var actionButtonsNode: ChatMessageActionButtonsNode?
private var reactionButtonsNode: ChatMessageReactionButtonsNode?
@ -991,11 +989,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
}
}
if replyInfoApply != nil || viaBotApply != nil {
needsReplyBackground = true
}
var updatedShareButtonNode: ChatMessageShareButton?
if needsShareButton {
if let currentShareButtonNode = currentShareButtonNode {
@ -1013,7 +1007,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
var forwardPsaType: String?
var forwardInfoSizeApply: (CGSize, (CGFloat) -> ChatMessageForwardInfoNode)?
var needsForwardBackground = false
if !ignoreForward, let forwardInfo = item.message.forwardInfo {
forwardPsaType = forwardInfo.psaType
@ -1038,8 +1031,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
let availableWidth = max(60.0, availableContentWidth + 6.0)
forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, forwardPsaType, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
needsForwardBackground = true
}
if replyInfoApply != nil || viaBotApply != nil || forwardInfoSizeApply != nil {
needsReplyBackground = true
}
var maxContentWidth = imageSize.width
@ -1194,22 +1189,59 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
replyBackgroundNode.removeFromSupernode()
}
if let (viaBotLayout, viaBotApply) = viaBotApply {
var messageInfoSize = CGSize()
if let (viaBotLayout, _) = viaBotApply, forwardInfoSizeApply == nil {
messageInfoSize = CGSize(width: viaBotLayout.size.width + 1.0, height: 0.0)
}
if let (forwardInfoSize, _) = forwardInfoSizeApply {
messageInfoSize = CGSize(width: max(messageInfoSize.width, forwardInfoSize.width + 2.0), height: 0.0)
}
if let (replyInfoSize, _) = replyInfoApply {
messageInfoSize = CGSize(width: max(messageInfoSize.width, replyInfoSize.width), height: 0.0)
}
if let (viaBotLayout, viaBotApply) = viaBotApply, forwardInfoSizeApply == nil {
let viaBotNode = viaBotApply()
if strongSelf.viaBotNode == nil {
strongSelf.viaBotNode = viaBotNode
strongSelf.contextSourceNode.contentNode.addSubnode(viaBotNode)
}
let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 15.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 14.0)), y: 8.0), size: viaBotLayout.size)
let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 11.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 9.0)), y: 8.0), size: viaBotLayout.size)
viaBotNode.frame = viaBotFrame
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
replyBackgroundNode.frame = CGRect(origin: CGPoint(x: viaBotFrame.minX - 6.0, y: viaBotFrame.minY - 2.0 - UIScreenPixel), size: CGSize(width: viaBotFrame.size.width + 11.0, height: viaBotFrame.size.height + 5.0))
replyBackgroundNode.update(size: replyBackgroundNode.bounds.size, cornerRadius: 8.0, transition: .immediate)
}
messageInfoSize = CGSize(width: messageInfoSize.width, height: viaBotLayout.size.height)
} else if let viaBotNode = strongSelf.viaBotNode {
viaBotNode.removeFromSupernode()
strongSelf.viaBotNode = nil
}
if let (forwardInfoSize, forwardInfoApply) = forwardInfoSizeApply {
let forwardInfoNode = forwardInfoApply(forwardInfoSize.width)
if strongSelf.forwardInfoNode == nil {
strongSelf.forwardInfoNode = forwardInfoNode
strongSelf.contextSourceNode.contentNode.addSubnode(forwardInfoNode)
if animation.isAnimated {
forwardInfoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 8.0)), y: 8.0 + messageInfoSize.height), size: forwardInfoSize)
forwardInfoNode.frame = forwardInfoFrame
messageInfoSize = CGSize(width: messageInfoSize.width, height: messageInfoSize.height + forwardInfoSize.height - 1.0)
} else if let forwardInfoNode = strongSelf.forwardInfoNode {
if animation.isAnimated {
if let forwardInfoNode = strongSelf.forwardInfoNode {
strongSelf.forwardInfoNode = nil
forwardInfoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak forwardInfoNode] _ in
forwardInfoNode?.removeFromSupernode()
})
}
} else {
forwardInfoNode.removeFromSupernode()
strongSelf.forwardInfoNode = nil
}
}
if let (replyInfoSize, replyInfoApply) = replyInfoApply {
let replyInfoNode = replyInfoApply()
@ -1217,31 +1249,29 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
strongSelf.replyInfoNode = replyInfoNode
strongSelf.contextSourceNode.contentNode.addSubnode(replyInfoNode)
}
var viaBotSize = CGSize()
if let viaBotNode = strongSelf.viaBotNode {
viaBotSize = viaBotNode.frame.size
}
let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - max(replyInfoSize.width, viaBotSize.width) - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0 + viaBotSize.height), size: replyInfoSize)
if let viaBotNode = strongSelf.viaBotNode {
if replyInfoFrame.minX < viaBotNode.frame.minX {
viaBotNode.frame = viaBotNode.frame.offsetBy(dx: replyInfoFrame.minX - viaBotNode.frame.minX, dy: 0.0)
}
}
let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 11.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 9.0)), y: 8.0 + messageInfoSize.height), size: replyInfoSize)
replyInfoNode.frame = replyInfoFrame
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
replyBackgroundNode.frame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - viaBotSize.height - 2.0), size: CGSize(width: max(replyInfoFrame.size.width, viaBotSize.width) + 8.0, height: replyInfoFrame.size.height + viaBotSize.height + 5.0))
replyBackgroundNode.update(size: replyBackgroundNode.bounds.size, cornerRadius: 8.0, transition: .immediate)
}
if let _ = item.controllerInteraction.selectionState, isEmoji {
replyInfoNode.alpha = 0.0
strongSelf.replyBackgroundNode?.alpha = 0.0
}
messageInfoSize = CGSize(width: max(messageInfoSize.width, replyInfoSize.width), height: messageInfoSize.height + replyInfoSize.height)
} else if let replyInfoNode = strongSelf.replyInfoNode {
replyInfoNode.removeFromSupernode()
strongSelf.replyInfoNode = nil
}
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
replyBackgroundNode.frame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 10.0)) - 4.0, y: 6.0), size: CGSize(width: messageInfoSize.width + 8.0, height: messageInfoSize.height + 5.0))
let cornerRadius = replyBackgroundNode.frame.height <= 22.0 ? replyBackgroundNode.frame.height / 2.0 : 8.0
replyBackgroundNode.update(size: replyBackgroundNode.bounds.size, cornerRadius: cornerRadius, transition: .immediate)
}
let panelsAlpha: CGFloat = item.controllerInteraction.selectionState == nil ? 1.0 : 0.0
strongSelf.replyInfoNode?.alpha = panelsAlpha
strongSelf.viaBotNode?.alpha = panelsAlpha
strongSelf.forwardInfoNode?.alpha = panelsAlpha
strongSelf.replyBackgroundNode?.alpha = panelsAlpha
if isFailed {
let deliveryFailedNode: ChatMessageDeliveryFailedNode
var isAppearing = false
@ -1272,61 +1302,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
deliveryFailedNode?.removeFromSupernode()
})
}
if needsForwardBackground {
if let forwardBackgroundNode = strongSelf.forwardBackgroundNode {
forwardBackgroundNode.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate)
} else {
let forwardBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper))
strongSelf.forwardBackgroundNode = forwardBackgroundNode
strongSelf.contextSourceNode.contentNode.addSubnode(forwardBackgroundNode)
if animation.isAnimated {
forwardBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
} else if let forwardBackgroundNode = strongSelf.forwardBackgroundNode {
if animation.isAnimated {
strongSelf.forwardBackgroundNode = nil
forwardBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak forwardBackgroundNode] _ in
forwardBackgroundNode?.removeFromSupernode()
})
} else {
forwardBackgroundNode.removeFromSupernode()
strongSelf.forwardBackgroundNode = nil
}
}
if let (forwardInfoSize, forwardInfoApply) = forwardInfoSizeApply {
let forwardInfoNode = forwardInfoApply(forwardInfoSize.width)
if strongSelf.forwardInfoNode == nil {
strongSelf.forwardInfoNode = forwardInfoNode
strongSelf.contextSourceNode.contentNode.addSubnode(forwardInfoNode)
if animation.isAnimated {
forwardInfoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - forwardInfoSize.width - layoutConstants.bubble.edgeInset - 12.0)), y: 8.0), size: forwardInfoSize)
forwardInfoNode.frame = forwardInfoFrame
if let forwardBackgroundNode = strongSelf.forwardBackgroundNode {
forwardBackgroundNode.frame = CGRect(origin: CGPoint(x: forwardInfoFrame.minX - 6.0, y: forwardInfoFrame.minY - 2.0), size: CGSize(width: forwardInfoFrame.size.width + 10.0, height: forwardInfoFrame.size.height + 4.0))
forwardBackgroundNode.update(size: forwardBackgroundNode.bounds.size, cornerRadius: 8.0, transition: .immediate)
}
} else if let forwardInfoNode = strongSelf.forwardInfoNode {
if animation.isAnimated {
if let forwardInfoNode = strongSelf.forwardInfoNode {
strongSelf.forwardInfoNode = nil
forwardInfoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak forwardInfoNode] _ in
forwardInfoNode?.removeFromSupernode()
})
}
} else {
forwardInfoNode.removeFromSupernode()
strongSelf.forwardInfoNode = nil
}
}
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
let actionButtonsNode = actionButtonsSizeAndApply.1(animation)
let previousFrame = actionButtonsNode.frame
@ -2057,12 +2033,18 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate
let replyAlpha: CGFloat = item.controllerInteraction.selectionState == nil ? 1.0 : 0.0
let panelsAlpha: CGFloat = item.controllerInteraction.selectionState == nil ? 1.0 : 0.0
if let replyInfoNode = self.replyInfoNode {
transition.updateAlpha(node: replyInfoNode, alpha: replyAlpha)
transition.updateAlpha(node: replyInfoNode, alpha: panelsAlpha)
}
if let viaBotNode = self.viaBotNode {
transition.updateAlpha(node: viaBotNode, alpha: panelsAlpha)
}
if let forwardInfoNode = self.forwardInfoNode {
transition.updateAlpha(node: forwardInfoNode, alpha: panelsAlpha)
}
if let replyBackgroundNode = self.replyBackgroundNode {
transition.updateAlpha(node: replyBackgroundNode, alpha: replyAlpha)
transition.updateAlpha(node: replyBackgroundNode, alpha: panelsAlpha)
}
if let selectionState = item.controllerInteraction.selectionState {

View File

@ -805,7 +805,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
var rects: [CGRect] = []
for contentNode in self.contentNodes {
if let contentNode = contentNode as? ChatMessageMediaBubbleContentNode {
rects.append(contentNode.frame.offsetBy(dx: -76.0, dy: 0.0))
rects.append(contentNode.frame.offsetBy(dx: -self.clippingNode.frame.minX, dy: 0.0))
}
}
return rects

View File

@ -148,7 +148,7 @@ class ChatMessageForwardInfoNode: ASDisplayNode {
}
} else {
titleColor = incoming ? presentationData.theme.theme.chat.message.incoming.accentTextColor : presentationData.theme.theme.chat.message.outgoing.accentTextColor
completeSourceString = strings.Message_ForwardedMessage(peerString)
completeSourceString = strings.Message_ForwardedMessageShort(peerString)
}
case .standalone:
let serviceColor = serviceMessageColorComponents(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)

View File

@ -42,12 +42,10 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
return self.apparentHeightTransition != nil
}
private var forwardInfoNode: ChatMessageForwardInfoNode?
private var forwardBackgroundNode: NavigationBackgroundNode?
private var viaBotNode: TextNode?
private var replyInfoNode: ChatMessageReplyInfoNode?
private var replyBackgroundNode: NavigationBackgroundNode?
private var forwardInfoNode: ChatMessageForwardInfoNode?
private var actionButtonsNode: ChatMessageActionButtonsNode?
private var reactionButtonsNode: ChatMessageReactionButtonsNode?
@ -259,7 +257,6 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
let currentShareButtonNode = self.shareButtonNode
let makeForwardInfoLayout = ChatMessageForwardInfoNode.asyncLayout(self.forwardInfoNode)
let currentForwardBackgroundNode = self.forwardBackgroundNode
let actionButtonsLayout = ChatMessageActionButtonsNode.asyncLayout(self.actionButtonsNode)
let reactionButtonsLayout = ChatMessageReactionButtonsNode.asyncLayout(self.reactionButtonsNode)
@ -472,17 +469,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
replyMarkup = attribute
}
}
if replyInfoApply != nil || viaBotApply != nil {
if let currentReplyBackgroundNode = currentReplyBackgroundNode {
updatedReplyBackgroundNode = currentReplyBackgroundNode
} else {
updatedReplyBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper))
}
updatedReplyBackgroundNode?.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate)
}
var updatedShareButtonNode: ChatMessageShareButton?
if needsShareButton {
if currentShareButtonNode != nil {
@ -499,7 +486,6 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
var forwardAuthorSignature: String?
var forwardInfoSizeApply: (CGSize, (CGFloat) -> ChatMessageForwardInfoNode)?
var updatedForwardBackgroundNode: NavigationBackgroundNode?
if !ignoreForward, let forwardInfo = item.message.forwardInfo {
let forwardPsaType = forwardInfo.psaType
@ -524,14 +510,16 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
}
let availableWidth = max(60.0, availableContentWidth - normalDisplaySize.width + 6.0)
forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, forwardPsaType, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
if let currentForwardBackgroundNode = currentForwardBackgroundNode {
updatedForwardBackgroundNode = currentForwardBackgroundNode
}
if replyInfoApply != nil || viaBotApply != nil || forwardInfoSizeApply != nil {
if let currentReplyBackgroundNode = currentReplyBackgroundNode {
updatedReplyBackgroundNode = currentReplyBackgroundNode
} else {
updatedForwardBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper))
updatedReplyBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper))
}
updatedForwardBackgroundNode?.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate)
updatedReplyBackgroundNode?.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate)
}
var maxContentWidth = normalDisplaySize.width
@ -657,21 +645,59 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
strongSelf.replyBackgroundNode = nil
}
if let (viaBotLayout, viaBotApply) = viaBotApply {
var messageInfoSize = CGSize()
if let (viaBotLayout, _) = viaBotApply, forwardInfoSizeApply == nil {
messageInfoSize = CGSize(width: viaBotLayout.size.width + 1.0, height: 0.0)
}
if let (forwardInfoSize, _) = forwardInfoSizeApply {
messageInfoSize = CGSize(width: max(messageInfoSize.width, forwardInfoSize.width + 2.0), height: 0.0)
}
if let (replyInfoSize, _) = replyInfoApply {
messageInfoSize = CGSize(width: max(messageInfoSize.width, replyInfoSize.width), height: 0.0)
}
if let (viaBotLayout, viaBotApply) = viaBotApply, forwardInfoSizeApply == nil {
let viaBotNode = viaBotApply()
if strongSelf.viaBotNode == nil {
strongSelf.viaBotNode = viaBotNode
strongSelf.contextSourceNode.contentNode.addSubnode(viaBotNode)
}
let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0), size: viaBotLayout.size)
let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 11.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 9.0)), y: 8.0), size: viaBotLayout.size)
viaBotNode.frame = viaBotFrame
let replyBackgroundFrame = CGRect(origin: CGPoint(x: viaBotFrame.minX - 4.0, y: viaBotFrame.minY - 2.0), size: CGSize(width: viaBotFrame.size.width + 8.0, height: viaBotFrame.size.height + 5.0))
strongSelf.replyBackgroundNode?.frame = replyBackgroundFrame
strongSelf.replyBackgroundNode?.update(size: replyBackgroundFrame.size, cornerRadius: 8.0, transition: .immediate)
messageInfoSize = CGSize(width: messageInfoSize.width, height: viaBotLayout.size.height)
} else if let viaBotNode = strongSelf.viaBotNode {
viaBotNode.removeFromSupernode()
strongSelf.viaBotNode = nil
}
if let (forwardInfoSize, forwardInfoApply) = forwardInfoSizeApply {
let forwardInfoNode = forwardInfoApply(forwardInfoSize.width)
if strongSelf.forwardInfoNode == nil {
strongSelf.forwardInfoNode = forwardInfoNode
strongSelf.contextSourceNode.contentNode.addSubnode(forwardInfoNode)
if animation.isAnimated {
forwardInfoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 8.0)), y: 8.0 + messageInfoSize.height), size: forwardInfoSize)
forwardInfoNode.frame = forwardInfoFrame
messageInfoSize = CGSize(width: messageInfoSize.width, height: messageInfoSize.height + forwardInfoSize.height - 1.0)
} else if let forwardInfoNode = strongSelf.forwardInfoNode {
if animation.isAnimated {
if let forwardInfoNode = strongSelf.forwardInfoNode {
strongSelf.forwardInfoNode = nil
forwardInfoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak forwardInfoNode] _ in
forwardInfoNode?.removeFromSupernode()
})
}
} else {
forwardInfoNode.removeFromSupernode()
strongSelf.forwardInfoNode = nil
}
}
if let (replyInfoSize, replyInfoApply) = replyInfoApply {
let replyInfoNode = replyInfoApply()
@ -679,25 +705,23 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
strongSelf.replyInfoNode = replyInfoNode
strongSelf.contextSourceNode.contentNode.addSubnode(replyInfoNode)
}
var viaBotSize = CGSize()
if let viaBotNode = strongSelf.viaBotNode {
viaBotSize = viaBotNode.frame.size
}
let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - max(replyInfoSize.width, viaBotSize.width) - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0 + viaBotSize.height), size: replyInfoSize)
if let viaBotNode = strongSelf.viaBotNode {
if replyInfoFrame.minX < viaBotNode.frame.minX {
viaBotNode.frame = viaBotNode.frame.offsetBy(dx: replyInfoFrame.minX - viaBotNode.frame.minX, dy: 0.0)
}
}
let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 11.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 9.0)), y: 8.0 + messageInfoSize.height), size: replyInfoSize)
replyInfoNode.frame = replyInfoFrame
let replyBackgroundFrame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - viaBotSize.height - 2.0), size: CGSize(width: max(replyInfoFrame.size.width, viaBotSize.width) + 8.0, height: replyInfoFrame.size.height + viaBotSize.height + 5.0))
strongSelf.replyBackgroundNode?.frame = replyBackgroundFrame
strongSelf.replyBackgroundNode?.update(size: replyBackgroundFrame.size, cornerRadius: 8.0, transition: .immediate)
messageInfoSize = CGSize(width: max(messageInfoSize.width, replyInfoSize.width), height: messageInfoSize.height + replyInfoSize.height)
} else if let replyInfoNode = strongSelf.replyInfoNode {
replyInfoNode.removeFromSupernode()
strongSelf.replyInfoNode = nil
}
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
replyBackgroundNode.frame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 10.0)) - 4.0, y: 6.0), size: CGSize(width: messageInfoSize.width + 8.0, height: messageInfoSize.height + 5.0))
let cornerRadius = replyBackgroundNode.frame.height <= 22.0 ? replyBackgroundNode.frame.height / 2.0 : 8.0
replyBackgroundNode.update(size: replyBackgroundNode.bounds.size, cornerRadius: cornerRadius, transition: .immediate)
}
if isFailed {
let deliveryFailedNode: ChatMessageDeliveryFailedNode
var isAppearing = false
@ -728,63 +752,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
deliveryFailedNode?.removeFromSupernode()
})
}
if let updatedForwardBackgroundNode = updatedForwardBackgroundNode {
if strongSelf.forwardBackgroundNode == nil {
strongSelf.forwardBackgroundNode = updatedForwardBackgroundNode
strongSelf.contextSourceNode.contentNode.addSubnode(updatedForwardBackgroundNode)
if animation.isAnimated {
updatedForwardBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
} else if let forwardBackgroundNode = strongSelf.forwardBackgroundNode {
if animation.isAnimated {
strongSelf.forwardBackgroundNode = nil
forwardBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak forwardBackgroundNode] _ in
forwardBackgroundNode?.removeFromSupernode()
})
} else {
forwardBackgroundNode.removeFromSupernode()
strongSelf.forwardBackgroundNode = nil
}
}
if let (forwardInfoSize, forwardInfoApply) = forwardInfoSizeApply {
let forwardInfoNode = forwardInfoApply(forwardInfoSize.width)
if strongSelf.forwardInfoNode == nil {
strongSelf.forwardInfoNode = forwardInfoNode
strongSelf.contextSourceNode.contentNode.addSubnode(forwardInfoNode)
forwardInfoNode.openPsa = { [weak strongSelf] type, sourceNode in
guard let strongSelf = strongSelf, let item = strongSelf.item else {
return
}
item.controllerInteraction.displayPsa(type, sourceNode)
}
if animation.isAnimated {
forwardInfoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - forwardInfoSize.width - layoutConstants.bubble.edgeInset - 12.0)), y: 8.0), size: forwardInfoSize)
forwardInfoNode.frame = forwardInfoFrame
let forwardBackgroundFrame = CGRect(origin: CGPoint(x: forwardInfoFrame.minX - 6.0, y: forwardInfoFrame.minY - 2.0), size: CGSize(width: forwardInfoFrame.size.width + 10.0, height: forwardInfoFrame.size.height + 4.0))
strongSelf.forwardBackgroundNode?.frame = forwardBackgroundFrame
strongSelf.forwardBackgroundNode?.update(size: forwardBackgroundFrame.size, cornerRadius: 8.0, transition: .immediate)
} else if let forwardInfoNode = strongSelf.forwardInfoNode {
if animation.isAnimated {
if let forwardInfoNode = strongSelf.forwardInfoNode {
strongSelf.forwardInfoNode = nil
forwardInfoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak forwardInfoNode] _ in
forwardInfoNode?.removeFromSupernode()
})
}
} else {
forwardInfoNode.removeFromSupernode()
strongSelf.forwardInfoNode = nil
}
}
if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply {
let reactionButtonsNode = reactionButtonsSizeAndApply.1(animation)
var reactionButtonsFrame = CGRect(origin: CGPoint(x: videoFrame.minX, y: videoFrame.maxY + 6.0), size: reactionButtonsSizeAndApply.0)
@ -869,9 +837,6 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
}
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
if let forwardBackgroundNode = strongSelf.forwardBackgroundNode {
transition.updateAlpha(node: forwardBackgroundNode, alpha: isPlaying ? 0.0 : 1.0)
}
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
transition.updateAlpha(node: replyBackgroundNode, alpha: isPlaying ? 0.0 : 1.0)
}
@ -1312,28 +1277,28 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
shareButtonNode.frame = CGRect(origin: CGPoint(x: min(params.width - buttonSize.width - 8.0, videoFrame.maxX - 7.0), y: videoFrame.maxY - 24.0 - buttonSize.height), size: buttonSize)
}
if let viaBotNode = self.viaBotNode {
let viaBotLayout = viaBotNode.frame
let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0), size: viaBotLayout.size)
viaBotNode.frame = viaBotFrame
self.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: viaBotFrame.minX - 4.0, y: viaBotFrame.minY - 2.0), size: CGSize(width: viaBotFrame.size.width + 8.0, height: viaBotFrame.size.height + 5.0))
}
if let replyInfoNode = self.replyInfoNode {
var viaBotSize = CGSize()
if let viaBotNode = self.viaBotNode {
viaBotSize = viaBotNode.frame.size
}
let replyInfoSize = replyInfoNode.frame.size
let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - max(replyInfoSize.width, viaBotSize.width) - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0 + viaBotSize.height), size: replyInfoSize)
if let viaBotNode = self.viaBotNode {
if replyInfoFrame.minX < viaBotNode.frame.minX {
viaBotNode.frame = viaBotNode.frame.offsetBy(dx: replyInfoFrame.minX - viaBotNode.frame.minX, dy: 0.0)
}
}
replyInfoNode.frame = replyInfoFrame
self.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - viaBotSize.height - 2.0), size: CGSize(width: max(replyInfoFrame.size.width, viaBotSize.width) + 8.0, height: replyInfoFrame.size.height + viaBotSize.height + 5.0))
}
// if let viaBotNode = self.viaBotNode {
// let viaBotLayout = viaBotNode.frame
// let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0), size: viaBotLayout.size)
// viaBotNode.frame = viaBotFrame
// self.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: viaBotFrame.minX - 4.0, y: viaBotFrame.minY - 2.0), size: CGSize(width: viaBotFrame.size.width + 8.0, height: viaBotFrame.size.height + 5.0))
// }
//
// if let replyInfoNode = self.replyInfoNode {
// var viaBotSize = CGSize()
// if let viaBotNode = self.viaBotNode {
// viaBotSize = viaBotNode.frame.size
// }
// let replyInfoSize = replyInfoNode.frame.size
// let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - max(replyInfoSize.width, viaBotSize.width) - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0 + viaBotSize.height), size: replyInfoSize)
// if let viaBotNode = self.viaBotNode {
// if replyInfoFrame.minX < viaBotNode.frame.minX {
// viaBotNode.frame = viaBotNode.frame.offsetBy(dx: replyInfoFrame.minX - viaBotNode.frame.minX, dy: 0.0)
// }
// }
// replyInfoNode.frame = replyInfoFrame
// self.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - viaBotSize.height - 2.0), size: CGSize(width: max(replyInfoFrame.size.width, viaBotSize.width) + 8.0, height: replyInfoFrame.size.height + viaBotSize.height + 5.0))
// }
if let deliveryFailedNode = self.deliveryFailedNode {
let deliveryFailedSize = deliveryFailedNode.frame.size
@ -1341,12 +1306,11 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
deliveryFailedNode.frame = deliveryFailedFrame
}
if let forwardInfoNode = self.forwardInfoNode {
let forwardInfoSize = forwardInfoNode.frame.size
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - forwardInfoSize.width - layoutConstants.bubble.edgeInset - 12.0)), y: 8.0), size: forwardInfoSize)
forwardInfoNode.frame = forwardInfoFrame
self.forwardBackgroundNode?.frame = CGRect(origin: CGPoint(x: forwardInfoFrame.minX - 6.0, y: forwardInfoFrame.minY - 2.0), size: CGSize(width: forwardInfoFrame.size.width + 10.0, height: forwardInfoFrame.size.height + 4.0))
}
// if let forwardInfoNode = self.forwardInfoNode {
// let forwardInfoSize = forwardInfoNode.frame.size
// let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - forwardInfoSize.width - layoutConstants.bubble.edgeInset - 12.0)), y: 8.0), size: forwardInfoSize)
// forwardInfoNode.frame = forwardInfoFrame
// }
if let actionButtonsNode = self.actionButtonsNode {
let actionButtonsSize = actionButtonsNode.frame.size

View File

@ -46,7 +46,6 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
}
class func asyncLayout(_ maybeNode: ChatMessageReplyInfoNode?) -> (_ theme: ChatPresentationData, _ strings: PresentationStrings, _ context: AccountContext, _ type: ChatMessageReplyInfoType, _ message: Message, _ constrainedSize: CGSize) -> (CGSize, () -> ChatMessageReplyInfoNode) {
let titleNodeLayout = TextNode.asyncLayout(maybeNode?.titleNode)
let textNodeLayout = TextNode.asyncLayout(maybeNode?.textNode)
let imageNodeLayout = TransformImageNode.asyncLayout(maybeNode?.imageNode)
@ -57,7 +56,13 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
let titleFont = Font.medium(fontSize)
let textFont = Font.regular(fontSize)
var titleString = message.effectiveAuthor.flatMap(EnginePeer.init)?.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder) ?? strings.User_DeletedAccount
var author: Peer?
if let forwardAuthor = message.forwardInfo?.author {
author = forwardAuthor
} else {
author = message.effectiveAuthor
}
var titleString = author.flatMap(EnginePeer.init)?.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder) ?? strings.User_DeletedAccount
if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported) {
if let author = forwardInfo.author {
@ -242,9 +247,9 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
}
node.imageNode?.captureProtected = message.isCopyProtected()
titleNode.frame = CGRect(origin: CGPoint(x: leftInset - textInsets.left, y: spacing - textInsets.top), size: titleLayout.size)
titleNode.frame = CGRect(origin: CGPoint(x: leftInset - textInsets.left - 2.0, y: spacing - textInsets.top + 1.0), size: titleLayout.size)
let textFrame = CGRect(origin: CGPoint(x: leftInset - textInsets.left, y: titleNode.frame.maxY - textInsets.bottom + spacing - textInsets.top), size: textLayout.size)
let textFrame = CGRect(origin: CGPoint(x: leftInset - textInsets.left - 2.0, y: titleNode.frame.maxY - textInsets.bottom + spacing - textInsets.top - 2.0), size: textLayout.size)
textNode.frame = textFrame
if !textLayout.spoilers.isEmpty {
@ -265,7 +270,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
}
node.lineNode.image = lineImage
node.lineNode.frame = CGRect(origin: CGPoint(x: 1.0, y: 3.0), size: CGSize(width: 2.0, height: max(0.0, size.height - 5.0)))
node.lineNode.frame = CGRect(origin: CGPoint(x: 1.0, y: 3.0), size: CGSize(width: 2.0, height: max(0.0, size.height - 4.0)))
node.contentNode.frame = CGRect(origin: CGPoint(), size: size)

View File

@ -36,13 +36,11 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
var telegramFile: TelegramMediaFile?
private let fetchDisposable = MetaDisposable()
private var forwardInfoNode: ChatMessageForwardInfoNode?
private var forwardBackgroundNode: NavigationBackgroundNode?
private var viaBotNode: TextNode?
private let dateAndStatusNode: ChatMessageDateAndStatusNode
private var replyInfoNode: ChatMessageReplyInfoNode?
private var replyBackgroundNode: NavigationBackgroundNode?
private var forwardInfoNode: ChatMessageForwardInfoNode?
private var actionButtonsNode: ChatMessageActionButtonsNode?
private var reactionButtonsNode: ChatMessageReactionButtonsNode?
@ -588,12 +586,6 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
var needsReplyBackground = false
if replyInfoApply != nil || viaBotApply != nil {
needsReplyBackground = true
}
var updatedShareButtonNode: ChatMessageShareButton?
if needsShareButton {
if let currentShareButtonNode = currentShareButtonNode {
@ -611,7 +603,6 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
var forwardPsaType: String?
var forwardInfoSizeApply: (CGSize, (CGFloat) -> ChatMessageForwardInfoNode)?
var needsForwardBackground = false
if !ignoreForward, let forwardInfo = item.message.forwardInfo {
forwardPsaType = forwardInfo.psaType
@ -636,8 +627,11 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
let availableForwardWidth = max(60.0, availableWidth + 6.0)
forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, forwardPsaType, CGSize(width: availableForwardWidth, height: CGFloat.greatestFiniteMagnitude))
needsForwardBackground = true
}
var needsReplyBackground = false
if replyInfoApply != nil || viaBotApply != nil || forwardInfoSizeApply != nil {
needsReplyBackground = true
}
var maxContentWidth = imageSize.width
@ -829,48 +823,89 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
strongSelf.replyBackgroundNode = nil
}
if let (_, viaBotApply) = viaBotApply, let viaBotFrame = viaBotFrame {
var messageInfoSize = CGSize()
if let (viaBotLayout, _) = viaBotApply, forwardInfoSizeApply == nil {
messageInfoSize = CGSize(width: viaBotLayout.size.width + 1.0, height: 0.0)
}
if let (forwardInfoSize, _) = forwardInfoSizeApply {
messageInfoSize = CGSize(width: max(messageInfoSize.width, forwardInfoSize.width + 2.0), height: 0.0)
}
if let (replyInfoSize, _) = replyInfoApply {
messageInfoSize = CGSize(width: max(messageInfoSize.width, replyInfoSize.width), height: 0.0)
}
if let (viaBotLayout, viaBotApply) = viaBotApply, forwardInfoSizeApply == nil {
let viaBotNode = viaBotApply()
if strongSelf.viaBotNode == nil {
strongSelf.viaBotNode = viaBotNode
strongSelf.contextSourceNode.contentNode.addSubnode(viaBotNode)
}
let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 11.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 9.0)), y: 8.0), size: viaBotLayout.size)
viaBotNode.frame = viaBotFrame
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
replyBackgroundNode.frame = CGRect(origin: CGPoint(x: viaBotFrame.minX - 6.0, y: viaBotFrame.minY - 2.0 - UIScreenPixel), size: CGSize(width: viaBotFrame.size.width + 11.0, height: viaBotFrame.size.height + 5.0))
replyBackgroundNode.update(size: replyBackgroundNode.bounds.size, cornerRadius: 8.0, transition: .immediate)
}
messageInfoSize = CGSize(width: messageInfoSize.width, height: viaBotLayout.size.height)
} else if let viaBotNode = strongSelf.viaBotNode {
viaBotNode.removeFromSupernode()
strongSelf.viaBotNode = nil
}
if let (forwardInfoSize, forwardInfoApply) = forwardInfoSizeApply {
let forwardInfoNode = forwardInfoApply(forwardInfoSize.width)
if strongSelf.forwardInfoNode == nil {
strongSelf.forwardInfoNode = forwardInfoNode
strongSelf.contextSourceNode.contentNode.addSubnode(forwardInfoNode)
if animation.isAnimated {
forwardInfoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 8.0)), y: 8.0 + messageInfoSize.height), size: forwardInfoSize)
forwardInfoNode.frame = forwardInfoFrame
messageInfoSize = CGSize(width: messageInfoSize.width, height: messageInfoSize.height + forwardInfoSize.height - 1.0)
} else if let forwardInfoNode = strongSelf.forwardInfoNode {
if animation.isAnimated {
if let forwardInfoNode = strongSelf.forwardInfoNode {
strongSelf.forwardInfoNode = nil
forwardInfoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak forwardInfoNode] _ in
forwardInfoNode?.removeFromSupernode()
})
}
} else {
forwardInfoNode.removeFromSupernode()
strongSelf.forwardInfoNode = nil
}
}
if let (_, replyInfoApply) = replyInfoApply, let replyInfoFrame = replyInfoFrame {
if let (replyInfoSize, replyInfoApply) = replyInfoApply {
let replyInfoNode = replyInfoApply()
if strongSelf.replyInfoNode == nil {
strongSelf.replyInfoNode = replyInfoNode
strongSelf.contextSourceNode.contentNode.addSubnode(replyInfoNode)
}
let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 11.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 9.0)), y: 8.0 + messageInfoSize.height), size: replyInfoSize)
replyInfoNode.frame = replyInfoFrame
if let replyBackgroundNode = strongSelf.replyBackgroundNode, let replyBackgroundFrame = replyBackgroundFrame {
replyBackgroundNode.frame = replyBackgroundFrame
replyBackgroundNode.update(size: replyBackgroundNode.bounds.size, cornerRadius: 8.0, transition: .immediate)
}
if isEmoji && !incoming {
if let _ = item.controllerInteraction.selectionState {
replyInfoNode.alpha = 0.0
strongSelf.replyBackgroundNode?.alpha = 0.0
} else {
replyInfoNode.alpha = 1.0
strongSelf.replyBackgroundNode?.alpha = 1.0
}
}
messageInfoSize = CGSize(width: max(messageInfoSize.width, replyInfoSize.width), height: messageInfoSize.height + replyInfoSize.height)
} else if let replyInfoNode = strongSelf.replyInfoNode {
replyInfoNode.removeFromSupernode()
strongSelf.replyInfoNode = nil
}
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
replyBackgroundNode.frame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 10.0)) - 4.0, y: 6.0), size: CGSize(width: messageInfoSize.width + 8.0, height: messageInfoSize.height + 5.0))
let cornerRadius = replyBackgroundNode.frame.height <= 22.0 ? replyBackgroundNode.frame.height / 2.0 : 8.0
replyBackgroundNode.update(size: replyBackgroundNode.bounds.size, cornerRadius: cornerRadius, transition: .immediate)
}
let panelsAlpha: CGFloat = item.controllerInteraction.selectionState == nil ? 1.0 : 0.0
strongSelf.replyInfoNode?.alpha = panelsAlpha
strongSelf.viaBotNode?.alpha = panelsAlpha
strongSelf.forwardInfoNode?.alpha = panelsAlpha
strongSelf.replyBackgroundNode?.alpha = panelsAlpha
if isFailed {
let deliveryFailedNode: ChatMessageDeliveryFailedNode
var isAppearing = false
@ -901,61 +936,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
deliveryFailedNode?.removeFromSupernode()
})
}
if needsForwardBackground {
if let forwardBackgroundNode = strongSelf.forwardBackgroundNode {
forwardBackgroundNode.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate)
} else {
let forwardBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper))
strongSelf.forwardBackgroundNode = forwardBackgroundNode
strongSelf.contextSourceNode.contentNode.addSubnode(forwardBackgroundNode)
if animation.isAnimated {
forwardBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
} else if let forwardBackgroundNode = strongSelf.forwardBackgroundNode {
if animation.isAnimated {
strongSelf.forwardBackgroundNode = nil
forwardBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak forwardBackgroundNode] _ in
forwardBackgroundNode?.removeFromSupernode()
})
} else {
forwardBackgroundNode.removeFromSupernode()
strongSelf.forwardBackgroundNode = nil
}
}
if let (forwardInfoSize, forwardInfoApply) = forwardInfoSizeApply {
let forwardInfoNode = forwardInfoApply(forwardInfoSize.width)
if strongSelf.forwardInfoNode == nil {
strongSelf.forwardInfoNode = forwardInfoNode
strongSelf.contextSourceNode.contentNode.addSubnode(forwardInfoNode)
if animation.isAnimated {
forwardInfoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - forwardInfoSize.width - layoutConstants.bubble.edgeInset - 12.0)), y: 8.0), size: forwardInfoSize)
forwardInfoNode.frame = forwardInfoFrame
if let forwardBackgroundNode = strongSelf.forwardBackgroundNode {
forwardBackgroundNode.frame = CGRect(origin: CGPoint(x: forwardInfoFrame.minX - 6.0, y: forwardInfoFrame.minY - 2.0), size: CGSize(width: forwardInfoFrame.size.width + 10.0, height: forwardInfoFrame.size.height + 4.0))
forwardBackgroundNode.update(size: forwardBackgroundNode.bounds.size, cornerRadius: 8.0, transition: .immediate)
}
} else if let forwardInfoNode = strongSelf.forwardInfoNode {
if animation.isAnimated {
if let forwardInfoNode = strongSelf.forwardInfoNode {
strongSelf.forwardInfoNode = nil
forwardInfoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak forwardInfoNode] _ in
forwardInfoNode?.removeFromSupernode()
})
}
} else {
forwardInfoNode.removeFromSupernode()
strongSelf.forwardInfoNode = nil
}
}
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
let actionButtonsNode = actionButtonsSizeAndApply.1(animation)
let previousFrame = actionButtonsNode.frame
@ -1333,12 +1314,18 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate
let replyAlpha: CGFloat = item.controllerInteraction.selectionState == nil ? 1.0 : 0.0
let panelsAlpha: CGFloat = item.controllerInteraction.selectionState == nil ? 1.0 : 0.0
if let replyInfoNode = self.replyInfoNode {
transition.updateAlpha(node: replyInfoNode, alpha: replyAlpha)
transition.updateAlpha(node: replyInfoNode, alpha: panelsAlpha)
}
if let viaBotNode = self.viaBotNode {
transition.updateAlpha(node: viaBotNode, alpha: panelsAlpha)
}
if let forwardInfoNode = self.forwardInfoNode {
transition.updateAlpha(node: forwardInfoNode, alpha: panelsAlpha)
}
if let replyBackgroundNode = self.replyBackgroundNode {
transition.updateAlpha(node: replyBackgroundNode, alpha: replyAlpha)
transition.updateAlpha(node: replyBackgroundNode, alpha: panelsAlpha)
}
if let selectionState = item.controllerInteraction.selectionState {

View File

@ -521,6 +521,62 @@ public final class ChatMessageTransitionNode: ASDisplayNode {
}
case let .mediaInput(mediaInput):
if let snapshotView = mediaInput.extractSnapshot() {
Queue.mainQueue().justDispatch {
if let itemNode = self.itemNode as? ChatMessageBubbleItemNode {
itemNode.cancelInsertionAnimations()
self.contextSourceNode.isExtractedToContextPreview = true
self.contextSourceNode.isExtractedToContextPreviewUpdated?(true)
self.containerNode.addSubnode(self.contextSourceNode.contentNode)
let targetAbsoluteRect = self.contextSourceNode.view.convert(self.contextSourceNode.contentRect, to: self.view)
let sourceBackgroundAbsoluteRect = snapshotView.frame
let sourceAbsoluteRect = CGRect(origin: CGPoint(x: sourceBackgroundAbsoluteRect.midX - self.contextSourceNode.contentRect.size.width / 2.0, y: sourceBackgroundAbsoluteRect.midY - self.contextSourceNode.contentRect.size.height / 2.0), size: self.contextSourceNode.contentRect.size)
let combinedTransition = CombinedTransition(horizontal: .animated(duration: horizontalDuration, curve: ChatMessageTransitionNode.horizontalAnimationCurve), vertical: .animated(duration: verticalDuration, curve: ChatMessageTransitionNode.verticalAnimationCurve))
if let itemNode = self.itemNode as? ChatMessageBubbleItemNode {
itemNode.animateContentFromMediaInput(snapshotView: snapshotView, transition: combinedTransition)
}
self.containerNode.frame = targetAbsoluteRect.offsetBy(dx: -self.contextSourceNode.contentRect.minX, dy: -self.contextSourceNode.contentRect.minY)
snapshotView.center = targetAbsoluteRect.center.offsetBy(dx: -self.containerNode.frame.minX, dy: -self.containerNode.frame.minY)
self.containerNode.view.addSubview(snapshotView)
self.contextSourceNode.updateAbsoluteRect?(self.containerNode.frame, UIScreen.main.bounds.size)
self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: ChatMessageTransitionNode.horizontalAnimationCurve.mediaTimingFunction, additive: true, force: true)
self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: ChatMessageTransitionNode.verticalAnimationCurve.mediaTimingFunction, additive: true, force: true, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.endAnimation()
})
combinedTransition.horizontal.animateTransformScale(node: self.contextSourceNode.contentNode, from: CGPoint(x: sourceBackgroundAbsoluteRect.width / targetAbsoluteRect.width, y: sourceBackgroundAbsoluteRect.height / targetAbsoluteRect.height))
combinedTransition.horizontal.updateTransformScale(layer: snapshotView.layer, scale: CGPoint(x: 1.0 / (sourceBackgroundAbsoluteRect.width / targetAbsoluteRect.width), y: 1.0 / (sourceBackgroundAbsoluteRect.height / targetAbsoluteRect.height)))
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: sourceAbsoluteRect.minX - targetAbsoluteRect.minX, y: 0.0), ChatMessageTransitionNode.horizontalAnimationCurve, horizontalDuration)
self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: sourceAbsoluteRect.maxY - targetAbsoluteRect.maxY), ChatMessageTransitionNode.verticalAnimationCurve, verticalDuration)
}
}
} else {
self.endAnimation()
}
case let .groupedMediaInput(groupedMediaInput):
let snapshotViews = groupedMediaInput.extractSnapshots()
if snapshotViews.isEmpty {
self.endAnimation()
return
}
Queue.mainQueue().justDispatch {
if let itemNode = self.itemNode as? ChatMessageBubbleItemNode {
itemNode.cancelInsertionAnimations()
@ -529,20 +585,43 @@ public final class ChatMessageTransitionNode: ASDisplayNode {
self.containerNode.addSubnode(self.contextSourceNode.contentNode)
let targetAbsoluteRect = self.contextSourceNode.view.convert(self.contextSourceNode.contentRect, to: self.view)
let sourceBackgroundAbsoluteRect = snapshotView.frame
let sourceAbsoluteRect = CGRect(origin: CGPoint(x: sourceBackgroundAbsoluteRect.midX - self.contextSourceNode.contentRect.size.width / 2.0, y: sourceBackgroundAbsoluteRect.midY - self.contextSourceNode.contentRect.size.height / 2.0), size: self.contextSourceNode.contentRect.size)
let combinedTransition = CombinedTransition(horizontal: .animated(duration: horizontalDuration, curve: ChatMessageTransitionNode.horizontalAnimationCurve), vertical: .animated(duration: verticalDuration, curve: ChatMessageTransitionNode.verticalAnimationCurve))
var targetContentRects: [CGRect] = []
if let itemNode = self.itemNode as? ChatMessageBubbleItemNode {
itemNode.animateContentFromMediaInput(snapshotView: snapshotView, transition: combinedTransition)
targetContentRects = itemNode.animateContentFromGroupedMediaInput(transition: combinedTransition)
}
let targetAbsoluteRect = self.contextSourceNode.view.convert(self.contextSourceNode.contentRect, to: self.view)
func boundingRect(for views: [UIView]) -> CGRect {
var minX: CGFloat = .greatestFiniteMagnitude
var minY: CGFloat = .greatestFiniteMagnitude
var maxX: CGFloat = .leastNonzeroMagnitude
var maxY: CGFloat = .leastNonzeroMagnitude
for view in views {
let rect = view.frame
if rect.minX < minX {
minX = rect.minX
}
if rect.minY < minY {
minY = rect.minY
}
if rect.maxX > maxX {
maxX = rect.maxX
}
if rect.maxY > maxY {
maxY = rect.maxY
}
}
return CGRect(origin: CGPoint(x: minX, y: minY), size: CGSize(width: maxX - minX, height: maxY - minY))
}
self.containerNode.frame = targetAbsoluteRect.offsetBy(dx: -self.contextSourceNode.contentRect.minX, dy: -self.contextSourceNode.contentRect.minY)
let sourceBackgroundAbsoluteRect = boundingRect(for: snapshotViews)
let sourceAbsoluteRect = CGRect(origin: CGPoint(x: sourceBackgroundAbsoluteRect.midX - self.contextSourceNode.contentRect.size.width / 2.0, y: sourceBackgroundAbsoluteRect.midY - self.contextSourceNode.contentRect.size.height / 2.0), size: self.contextSourceNode.contentRect.size)
snapshotView.center = targetAbsoluteRect.center.offsetBy(dx: -self.containerNode.frame.minX, dy: -self.containerNode.frame.minY)
self.containerNode.view.addSubview(snapshotView)
self.containerNode.frame = targetAbsoluteRect.offsetBy(dx: -self.contextSourceNode.contentRect.minX, dy: -self.contextSourceNode.contentRect.minY)
self.contextSourceNode.updateAbsoluteRect?(self.containerNode.frame, UIScreen.main.bounds.size)
@ -556,101 +635,26 @@ public final class ChatMessageTransitionNode: ASDisplayNode {
combinedTransition.horizontal.animateTransformScale(node: self.contextSourceNode.contentNode, from: CGPoint(x: sourceBackgroundAbsoluteRect.width / targetAbsoluteRect.width, y: sourceBackgroundAbsoluteRect.height / targetAbsoluteRect.height))
combinedTransition.horizontal.updateTransformScale(layer: snapshotView.layer, scale: CGPoint(x: 1.0 / (sourceBackgroundAbsoluteRect.width / targetAbsoluteRect.width), y: 1.0 / (sourceBackgroundAbsoluteRect.height / targetAbsoluteRect.height)))
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
var index = 0
for snapshotView in snapshotViews {
let targetContentRect = targetContentRects[index]
let targetAbsoluteContentRect = targetContentRect.offsetBy(dx: targetAbsoluteRect.minX, dy: targetAbsoluteRect.minY)
snapshotView.center = targetAbsoluteContentRect.center.offsetBy(dx: -self.containerNode.frame.minX, dy: -self.containerNode.frame.minY)
self.containerNode.view.addSubview(snapshotView)
combinedTransition.horizontal.updateTransformScale(layer: snapshotView.layer, scale: CGPoint(x: 1.0 / (snapshotView.frame.width / targetContentRect.width), y: 1.0 / (snapshotView.frame.height / targetContentRect.height)))
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
index += 1
}
self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: sourceAbsoluteRect.minX - targetAbsoluteRect.minX, y: 0.0), ChatMessageTransitionNode.horizontalAnimationCurve, horizontalDuration)
self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: sourceAbsoluteRect.maxY - targetAbsoluteRect.maxY), ChatMessageTransitionNode.verticalAnimationCurve, verticalDuration)
}
} else {
self.endAnimation()
}
case let .groupedMediaInput(groupedMediaInput):
let snapshotViews = groupedMediaInput.extractSnapshots()
if snapshotViews.isEmpty {
self.endAnimation()
return
}
if let itemNode = self.itemNode as? ChatMessageBubbleItemNode {
itemNode.cancelInsertionAnimations()
self.contextSourceNode.isExtractedToContextPreview = true
self.contextSourceNode.isExtractedToContextPreviewUpdated?(true)
self.containerNode.addSubnode(self.contextSourceNode.contentNode)
let combinedTransition = CombinedTransition(horizontal: .animated(duration: horizontalDuration, curve: ChatMessageTransitionNode.horizontalAnimationCurve), vertical: .animated(duration: verticalDuration, curve: ChatMessageTransitionNode.verticalAnimationCurve))
var targetContentRects: [CGRect] = []
if let itemNode = self.itemNode as? ChatMessageBubbleItemNode {
targetContentRects = itemNode.animateContentFromGroupedMediaInput(transition: combinedTransition)
}
let targetAbsoluteRect = self.contextSourceNode.view.convert(self.contextSourceNode.contentRect, to: self.view)
func boundingRect(for views: [UIView]) -> CGRect {
var minX: CGFloat = .greatestFiniteMagnitude
var minY: CGFloat = .greatestFiniteMagnitude
var maxX: CGFloat = .leastNonzeroMagnitude
var maxY: CGFloat = .leastNonzeroMagnitude
for view in views {
let rect = view.frame
if rect.minX < minX {
minX = rect.minX
}
if rect.minY < minY {
minY = rect.minY
}
if rect.maxX > maxX {
maxX = rect.maxX
}
if rect.maxY > maxY {
maxY = rect.maxY
}
}
return CGRect(origin: CGPoint(x: minX, y: minY), size: CGSize(width: maxX - minX, height: maxY - minY))
}
let sourceBackgroundAbsoluteRect = boundingRect(for: snapshotViews)
let sourceAbsoluteRect = CGRect(origin: CGPoint(x: sourceBackgroundAbsoluteRect.midX - self.contextSourceNode.contentRect.size.width / 2.0, y: sourceBackgroundAbsoluteRect.midY - self.contextSourceNode.contentRect.size.height / 2.0), size: self.contextSourceNode.contentRect.size)
self.containerNode.frame = targetAbsoluteRect.offsetBy(dx: -self.contextSourceNode.contentRect.minX, dy: -self.contextSourceNode.contentRect.minY)
self.contextSourceNode.updateAbsoluteRect?(self.containerNode.frame, UIScreen.main.bounds.size)
self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: ChatMessageTransitionNode.horizontalAnimationCurve.mediaTimingFunction, additive: true, force: true)
self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: ChatMessageTransitionNode.verticalAnimationCurve.mediaTimingFunction, additive: true, force: true, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.endAnimation()
})
combinedTransition.horizontal.animateTransformScale(node: self.contextSourceNode.contentNode, from: CGPoint(x: sourceBackgroundAbsoluteRect.width / targetAbsoluteRect.width, y: sourceBackgroundAbsoluteRect.height / targetAbsoluteRect.height))
var index = 0
for snapshotView in snapshotViews {
let targetContentRect = targetContentRects[index]
let targetAbsoluteContentRect = targetContentRect.offsetBy(dx: targetAbsoluteRect.minX, dy: targetAbsoluteRect.minY)
snapshotView.center = targetAbsoluteContentRect.center.offsetBy(dx: -self.containerNode.frame.minX, dy: -self.containerNode.frame.minY)
self.containerNode.view.addSubview(snapshotView)
combinedTransition.horizontal.updateTransformScale(layer: snapshotView.layer, scale: CGPoint(x: 1.0 / (snapshotView.frame.width / targetContentRect.width), y: 1.0 / (snapshotView.frame.height / targetContentRect.height)))
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
index += 1
}
self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: sourceAbsoluteRect.minX - targetAbsoluteRect.minX, y: 0.0), ChatMessageTransitionNode.horizontalAnimationCurve, horizontalDuration)
self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: sourceAbsoluteRect.maxY - targetAbsoluteRect.maxY), ChatMessageTransitionNode.verticalAnimationCurve, verticalDuration)
}
}
}

View File

@ -10,7 +10,7 @@ import ShareController
import LegacyUI
import LegacyMediaPickerUI
func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocation: ChatLocation, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, attachmentController: ViewController? = nil, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: String, hasSchedule: Bool, photoOnly: Bool, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, dismissedWithResult: @escaping () -> Void = {}, finishedTransitionIn: @escaping () -> Void = {}) {
func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocation: ChatLocation, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, attachmentController: ViewController? = nil, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: NSAttributedString, hasSchedule: Bool, photoOnly: Bool, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, dismissedWithResult: @escaping () -> Void = {}, finishedTransitionIn: @escaping () -> Void = {}) {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)
@ -28,6 +28,10 @@ func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocation: Ch
}
controller.inhibitMultipleCapture = editingMedia
if !initialCaption.string.isEmpty {
controller.forcedCaption = initialCaption
}
controller.presentScheduleController = { _, done in
presentSchedulePicker(true, { time in
done?(time)

View File

@ -251,20 +251,56 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti
break
}
var addedAttributes: [(NSRange, ChatTextFontAttributes)] = []
func addFont(ranges: [NSRange], fontAttributes: ChatTextFontAttributes) {
for range in ranges {
var font: UIFont?
if fontAttributes == [.bold, .italic] {
font = boldItalicFont
} else if fontAttributes == [.bold] {
font = boldFont
addedAttributes.append((range, fontAttributes))
} else if fontAttributes == [.italic] {
font = italicFont
addedAttributes.append((range, fontAttributes))
}
if let font = font {
string.addAttribute(NSAttributedString.Key.font, value: font, range: range)
}
}
}
for (range, fontAttributes) in fontAttributes {
var font: UIFont?
if fontAttributes.contains(.blockQuote) {
font = blockQuoteFont
} else if fontAttributes == [.bold, .italic] {
font = boldItalicFont
} else if fontAttributes == [.bold] {
font = boldFont
} else if fontAttributes == [.italic] {
font = italicFont
}
if let font = font {
string.addAttribute(NSAttributedString.Key.font, value: font, range: range)
var ranges = [range]
var fontAttributes = fontAttributes
if fontAttributes != [.bold, .italic] {
for (existingRange, existingAttributes) in addedAttributes {
if let intersection = existingRange.intersection(range) {
if intersection.length == range.length {
if existingAttributes == .bold || existingAttributes == .italic {
fontAttributes.insert(existingAttributes)
}
} else {
var fontAttributes = fontAttributes
if existingAttributes == .bold || existingAttributes == .italic {
fontAttributes.insert(existingAttributes)
}
addFont(ranges: [intersection], fontAttributes: fontAttributes)
ranges = []
if range.upperBound > existingRange.lowerBound {
ranges.append(NSRange(location: range.lowerBound, length: existingRange.lowerBound - range.lowerBound))
}
if range.upperBound > existingRange.upperBound {
ranges.append(NSRange(location: existingRange.upperBound, length: range.upperBound - existingRange.upperBound))
}
}
break
}
}
}
addFont(ranges: ranges, fontAttributes: fontAttributes)
}
}
return string