Merge commit 'd5b2d433635831d8b1e193dc6741b02810855f8b'

# Conflicts:
#	submodules/TelegramUI/Sources/AppDelegate.swift
This commit is contained in:
Ali 2023-02-03 20:39:44 +01:00
commit 6dd073e114
41 changed files with 487 additions and 189 deletions

View File

@ -352,6 +352,12 @@ public class AttachmentController: ViewController {
} }
} }
self.panel.longPressed = { [weak self] _ in
if let strongSelf = self, let currentController = strongSelf.currentControllers.last {
currentController.longTapWithTabBar?()
}
}
self.panel.beganTextEditing = { [weak self] in self.panel.beganTextEditing = { [weak self] in
if let strongSelf = self { if let strongSelf = self {
strongSelf.container.update(isExpanded: true, transition: .animated(duration: 0.4, curve: .spring)) strongSelf.container.update(isExpanded: true, transition: .animated(duration: 0.4, curve: .spring))

View File

@ -124,6 +124,7 @@ private final class AttachButtonComponent: CombinedComponent {
let strings: PresentationStrings let strings: PresentationStrings
let theme: PresentationTheme let theme: PresentationTheme
let action: () -> Void let action: () -> Void
let longPressAction: () -> Void
init( init(
context: AccountContext, context: AccountContext,
@ -131,7 +132,8 @@ private final class AttachButtonComponent: CombinedComponent {
isSelected: Bool, isSelected: Bool,
strings: PresentationStrings, strings: PresentationStrings,
theme: PresentationTheme, theme: PresentationTheme,
action: @escaping () -> Void action: @escaping () -> Void,
longPressAction: @escaping () -> Void
) { ) {
self.context = context self.context = context
self.type = type self.type = type
@ -139,6 +141,7 @@ private final class AttachButtonComponent: CombinedComponent {
self.strings = strings self.strings = strings
self.theme = theme self.theme = theme
self.action = action self.action = action
self.longPressAction = longPressAction
} }
static func ==(lhs: AttachButtonComponent, rhs: AttachButtonComponent) -> Bool { static func ==(lhs: AttachButtonComponent, rhs: AttachButtonComponent) -> Bool {
@ -293,6 +296,11 @@ private final class AttachButtonComponent: CombinedComponent {
.gesture(.tap { .gesture(.tap {
component.action() component.action()
}) })
.gesture(.longPress({ state in
if case .began = state {
component.longPressAction()
}
}))
) )
return context.availableSize return context.availableSize
@ -495,6 +503,8 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
var isStandalone: Bool = false var isStandalone: Bool = false
var selectionChanged: (AttachmentButtonType) -> Bool = { _ in return false } var selectionChanged: (AttachmentButtonType) -> Bool = { _ in return false }
var longPressed: (AttachmentButtonType) -> Void = { _ in }
var beganTextEditing: () -> Void = {} var beganTextEditing: () -> Void = {}
var textUpdated: (NSAttributedString) -> Void = { _ in } var textUpdated: (NSAttributedString) -> Void = { _ in }
var sendMessagePressed: (AttachmentTextInputPanelSendMode) -> Void = { _ in } var sendMessagePressed: (AttachmentTextInputPanelSendMode) -> Void = { _ in }
@ -874,6 +884,10 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
} }
} }
} }
}, longPressAction: { [weak self] in
if let strongSelf = self, i == strongSelf.selectedIndex {
strongSelf.longPressed(type)
}
}) })
), ),
environment: {}, environment: {},

View File

@ -128,6 +128,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
private weak var copyProtectionTooltipController: TooltipController? private weak var copyProtectionTooltipController: TooltipController?
private lazy var hapticFeedback = { HapticFeedback() }()
private var didSetReady: Bool = false private var didSetReady: Bool = false
private let _ready = Promise<Void>() private let _ready = Promise<Void>()
public override func ready() -> Signal<Void, NoError> { public override func ready() -> Signal<Void, NoError> {
@ -1392,6 +1394,10 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
let peerId = peer.id let peerId = peer.id
if let strongSelf = self, let _ = peerSelectionController { if let strongSelf = self, let _ = peerSelectionController {
if peerId == strongSelf.context.account.peerId { if peerId == strongSelf.context.account.peerId {
Queue.mainQueue().after(0.88) {
strongSelf.hapticFeedback.success()
}
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
(strongSelf.navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .window(.root)) (strongSelf.navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .window(.root))

View File

@ -516,16 +516,51 @@ public final class ComposedPoll {
} }
} }
private class CreatePollControllerImpl: ItemListController, AttachmentContainable { private final class CreatePollContext: AttachmentMediaPickerContext {
var selectionCount: Signal<Int, NoError> {
return .single(0)
}
var caption: Signal<NSAttributedString?, NoError> {
return .single(nil)
}
public var loadingProgress: Signal<CGFloat?, NoError> {
return .single(nil)
}
public var mainButtonState: Signal<AttachmentMainButtonState?, NoError> {
return .single(nil)
}
func setCaption(_ caption: NSAttributedString) {
}
func send(silently: Bool, mode: AttachmentMediaPickerSendMode) {
}
func schedule() {
}
func mainButtonAction() {
}
}
public class CreatePollControllerImpl: ItemListController, AttachmentContainable {
public var requestAttachmentMenuExpansion: () -> Void = {} public var requestAttachmentMenuExpansion: () -> Void = {}
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in } 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 = { } public var cancelPanGesture: () -> Void = { }
public var isContainerPanning: () -> Bool = { return false } public var isContainerPanning: () -> Bool = { return false }
public var isContainerExpanded: () -> Bool = { return false } public var isContainerExpanded: () -> Bool = { return false }
public var mediaPickerContext: AttachmentMediaPickerContext? {
return CreatePollContext()
}
} }
public func createPollController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer, isQuiz: Bool? = nil, completion: @escaping (ComposedPoll) -> Void) -> AttachmentContainable { public func createPollController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer, isQuiz: Bool? = nil, completion: @escaping (ComposedPoll) -> Void) -> CreatePollControllerImpl {
var initialState = CreatePollControllerState() var initialState = CreatePollControllerState()
if let isQuiz = isQuiz { if let isQuiz = isQuiz {
initialState.isQuiz = isQuiz initialState.isQuiz = isQuiz

View File

@ -2,6 +2,8 @@
@interface DeviceProximityManager : NSObject @interface DeviceProximityManager : NSObject
@property (nonatomic, copy) void(^ _Nullable proximityChanged)(bool);
+ (DeviceProximityManager * _Nonnull)shared; + (DeviceProximityManager * _Nonnull)shared;
- (bool)currentValue; - (bool)currentValue;

View File

@ -46,6 +46,9 @@
for (void (^f)(bool) in [strongSelf->_subscribers copyItems]) { for (void (^f)(bool) in [strongSelf->_subscribers copyItems]) {
f(proximityState); f(proximityState);
} }
if (strongSelf.proximityChanged != nil) {
strongSelf.proximityChanged(proximityState);
}
} else if (!strongSelf->_proximityState && [strongSelf->_subscribers isEmpty]) { } else if (!strongSelf->_proximityState && [strongSelf->_subscribers isEmpty]) {
[UIDevice currentDevice].proximityMonitoringEnabled = false; [UIDevice currentDevice].proximityMonitoringEnabled = false;
} }
@ -90,6 +93,9 @@
for (void (^f)(bool) in [_subscribers copyItems]) { for (void (^f)(bool) in [_subscribers copyItems]) {
f(_proximityState); f(_proximityState);
} }
if (self.proximityChanged != nil) {
self.proximityChanged(_proximityState);
}
} }
} else { } else {
if (_proximityState) { if (_proximityState) {
@ -97,6 +103,9 @@
for (void (^f)(bool) in [_subscribers copyItems]) { for (void (^f)(bool) in [_subscribers copyItems]) {
f(_proximityState); f(_proximityState);
} }
if (self.proximityChanged != nil) {
self.proximityChanged(_proximityState);
}
} else { } else {
[UIDevice currentDevice].proximityMonitoringEnabled = false; [UIDevice currentDevice].proximityMonitoringEnabled = false;
} }

View File

@ -351,7 +351,7 @@ public func generateGradientTintedImage(image: UIImage?, colors: [UIColor]) -> U
let t = CGFloat(i) / CGFloat(colors.count - 1) let t = CGFloat(i) / CGFloat(colors.count - 1)
locations.append(t) locations.append(t)
} }
let colorSpace = CGColorSpaceCreateDeviceRGB() let colorSpace = DeviceGraphicsContextSettings.shared.colorSpace
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: imageRect.height), end: CGPoint(x: 0.0, y: 0.0), options: CGGradientDrawingOptions()) context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: imageRect.height), end: CGPoint(x: 0.0, y: 0.0), options: CGGradientDrawingOptions())
@ -381,7 +381,7 @@ public func generateGradientImage(size: CGSize, colors: [UIColor], locations: [C
UIGraphicsBeginImageContextWithOptions(size, false, 0.0) UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
if let context = UIGraphicsGetCurrentContext() { if let context = UIGraphicsGetCurrentContext() {
let gradientColors = colors.map { $0.cgColor } as CFArray let gradientColors = colors.map { $0.cgColor } as CFArray
let colorSpace = CGColorSpaceCreateDeviceRGB() let colorSpace = DeviceGraphicsContextSettings.shared.colorSpace
var locations = locations var locations = locations
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
@ -403,7 +403,7 @@ public func generateGradientFilledCircleImage(diameter: CGFloat, colors: NSArray
context.clip() context.clip()
var locations: [CGFloat] = [0.0, 1.0] var locations: [CGFloat] = [0.0, 1.0]
let colorSpace = CGColorSpaceCreateDeviceRGB() let colorSpace = DeviceGraphicsContextSettings.shared.colorSpace
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: &locations)! let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: bounds.size.height), options: CGGradientDrawingOptions()) context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: bounds.size.height), options: CGGradientDrawingOptions())

View File

@ -433,8 +433,12 @@ open class NavigationController: UINavigationController, ContainableController,
minHeight = 40.0 minHeight = 40.0
} }
var inCallStatusBarFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: max(layout.statusBarHeight ?? 0.0, max(minHeight, layout.safeInsets.top)))) var inCallStatusBarFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: max(layout.statusBarHeight ?? 0.0, max(minHeight, layout.safeInsets.top))))
if (layout.deviceMetrics.hasTopNotch || layout.deviceMetrics.hasDynamicIsland) && !isLandscape { if !isLandscape {
inCallStatusBarFrame.size.height += 12.0 if layout.deviceMetrics.hasTopNotch {
inCallStatusBarFrame.size.height += 12.0
} else if layout.deviceMetrics.hasDynamicIsland {
inCallStatusBarFrame.size.height += 20.0
}
} }
if inCallStatusBar.frame.isEmpty { if inCallStatusBar.frame.isEmpty {
inCallStatusBar.frame = inCallStatusBarFrame inCallStatusBar.frame = inCallStatusBarFrame

View File

@ -1190,6 +1190,7 @@ open class NavigationBar: ASDisplayNode {
self.badgeNode.updateTheme(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor) self.badgeNode.updateTheme(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor)
self.updateLeftButton(animated: false)
self.requestLayout() self.requestLayout()
} }
} }

View File

@ -1510,7 +1510,7 @@ open class TextNode: ASDisplayNode {
continue continue
} }
var fixCoupleEmoji = false var fixDoubleEmoji = false
if glyphCount == 2, let font = attributes["NSFont"] as? UIFont, font.fontName.contains("ColorEmoji"), let string = layout.attributedString { if glyphCount == 2, let font = attributes["NSFont"] as? UIFont, font.fontName.contains("ColorEmoji"), let string = layout.attributedString {
let range = CTRunGetStringRange(run) let range = CTRunGetStringRange(run)
let substring = string.attributedSubstring(from: NSMakeRange(range.location, range.length)).string let substring = string.attributedSubstring(from: NSMakeRange(range.location, range.length)).string
@ -1518,17 +1518,21 @@ open class TextNode: ASDisplayNode {
let heart = Unicode.Scalar(0x2764)! let heart = Unicode.Scalar(0x2764)!
let man = Unicode.Scalar(0x1F468)! let man = Unicode.Scalar(0x1F468)!
let woman = Unicode.Scalar(0x1F469)! let woman = Unicode.Scalar(0x1F469)!
let leftHand = Unicode.Scalar(0x1FAF1)!
let rightHand = Unicode.Scalar(0x1FAF2)!
if substring.unicodeScalars.contains(heart) && (substring.unicodeScalars.contains(man) || substring.unicodeScalars.contains(woman)) { if substring.unicodeScalars.contains(heart) && (substring.unicodeScalars.contains(man) || substring.unicodeScalars.contains(woman)) {
fixCoupleEmoji = true fixDoubleEmoji = true
} else if substring.unicodeScalars.contains(leftHand) && substring.unicodeScalars.contains(rightHand) {
fixDoubleEmoji = true
} }
} }
if fixCoupleEmoji { if fixDoubleEmoji {
context.setBlendMode(.normal) context.setBlendMode(.normal)
} }
CTRunDraw(run, context, CFRangeMake(0, glyphCount)) CTRunDraw(run, context, CFRangeMake(0, glyphCount))
if fixCoupleEmoji { if fixDoubleEmoji {
context.setBlendMode(blendMode) context.setBlendMode(blendMode)
} }
} }

View File

@ -241,6 +241,7 @@ public final class WindowKeyboardGestureRecognizerDelegate: NSObject, UIGestureR
public class Window1 { public class Window1 {
public let hostView: WindowHostView public let hostView: WindowHostView
public let badgeView: UIImageView public let badgeView: UIImageView
private let customProximityDimView: UIView
private var deviceMetrics: DeviceMetrics private var deviceMetrics: DeviceMetrics
@ -257,8 +258,6 @@ public class Window1 {
private var updatingLayout: UpdatingLayout? private var updatingLayout: UpdatingLayout?
private var updatedContainerLayout: ContainerViewLayout? private var updatedContainerLayout: ContainerViewLayout?
private var upperKeyboardInputPositionBound: CGFloat? private var upperKeyboardInputPositionBound: CGFloat?
private var cachedWindowSubviewCount: Int = 0
private var cachedHasPreview: Bool = false
private let presentationContext: PresentationContext private let presentationContext: PresentationContext
private let overlayPresentationContext: GlobalOverlayPresentationContext private let overlayPresentationContext: GlobalOverlayPresentationContext
@ -270,10 +269,7 @@ public class Window1 {
private var shouldInvalidateSupportedOrientations = false private var shouldInvalidateSupportedOrientations = false
private var statusBarHidden = false private var statusBarHidden = false
public var previewThemeAccentColor: UIColor = .blue
public var previewThemeDarkBlur: Bool = false
private var shouldNotAnimateLikelyKeyboardAutocorrectionSwitch: Bool = false private var shouldNotAnimateLikelyKeyboardAutocorrectionSwitch: Bool = false
public private(set) var forceInCallStatusBarText: String? = nil public private(set) var forceInCallStatusBarText: String? = nil
@ -333,6 +329,10 @@ public class Window1 {
self.badgeView.image = UIImage(bundleImageName: "Components/AppBadge") self.badgeView.image = UIImage(bundleImageName: "Components/AppBadge")
self.badgeView.isHidden = true self.badgeView.isHidden = true
self.customProximityDimView = UIView()
self.customProximityDimView.backgroundColor = .black
self.customProximityDimView.isHidden = true
self.systemUserInterfaceStyle = hostView.systemUserInterfaceStyle self.systemUserInterfaceStyle = hostView.systemUserInterfaceStyle
let boundsSize = self.hostView.eventView.bounds.size let boundsSize = self.hostView.eventView.bounds.size
@ -652,6 +652,7 @@ public class Window1 {
self.windowPanRecognizer = recognizer self.windowPanRecognizer = recognizer
self.hostView.containerView.addGestureRecognizer(recognizer) self.hostView.containerView.addGestureRecognizer(recognizer)
self.hostView.containerView.addSubview(self.badgeView) self.hostView.containerView.addSubview(self.badgeView)
self.hostView.containerView.addSubview(self.customProximityDimView)
} }
public required init(coder aDecoder: NSCoder) { public required init(coder aDecoder: NSCoder) {
@ -685,6 +686,13 @@ public class Window1 {
self.updateBadgeVisibility() self.updateBadgeVisibility()
} }
public func setProximityDimHidden(_ hidden: Bool) {
guard hidden != self.customProximityDimView.isHidden else {
return
}
self.customProximityDimView.isHidden = hidden
}
private func updateBadgeVisibility() { private func updateBadgeVisibility() {
let badgeIsHidden = !self.deviceMetrics.showAppBadge || self.forceBadgeHidden || self.windowLayout.size.width > self.windowLayout.size.height let badgeIsHidden = !self.deviceMetrics.showAppBadge || self.forceBadgeHidden || self.windowLayout.size.width > self.windowLayout.size.height
if badgeIsHidden != self.badgeView.isHidden && !badgeIsHidden { if badgeIsHidden != self.badgeView.isHidden && !badgeIsHidden {
@ -1141,6 +1149,8 @@ public class Window1 {
self.updateBadgeVisibility() self.updateBadgeVisibility()
self.badgeView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.windowLayout.size.width - image.size.width) / 2.0), y: 5.0), size: image.size) self.badgeView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.windowLayout.size.width - image.size.width) / 2.0), y: 5.0), size: image.size)
} }
self.customProximityDimView.frame = CGRect(origin: .zero, size: self.windowLayout.size)
} }
} }
} }

View File

@ -1112,6 +1112,10 @@ public class GalleryController: ViewController, StandalonePresentableController,
let animatedOutNode = !simpleAnimation let animatedOutNode = !simpleAnimation
if let chatController = strongSelf.baseNavigationController?.topViewController as? ChatController {
chatController.updatePushedTransition(0.0, transition: .animated(duration: 0.45, curve: .customSpring(damping: 180.0, initialVelocity: 0.0)))
}
strongSelf.galleryNode.animateOut(animateContent: animatedOutNode, completion: { strongSelf.galleryNode.animateOut(animateContent: animatedOutNode, completion: {
}) })
} }

View File

@ -363,4 +363,38 @@ public final class LocationPickerController: ViewController, AttachmentContainab
self.interaction?.dismissSearch() self.interaction?.dismissSearch()
self.scrollToTop?() self.scrollToTop?()
} }
public var mediaPickerContext: AttachmentMediaPickerContext? {
return LocationPickerContext()
}
}
private final class LocationPickerContext: AttachmentMediaPickerContext {
var selectionCount: Signal<Int, NoError> {
return .single(0)
}
var caption: Signal<NSAttributedString?, NoError> {
return .single(nil)
}
public var loadingProgress: Signal<CGFloat?, NoError> {
return .single(nil)
}
public var mainButtonState: Signal<AttachmentMainButtonState?, NoError> {
return .single(nil)
}
func setCaption(_ caption: NSAttributedString) {
}
func send(silently: Bool, mode: AttachmentMediaPickerSendMode) {
}
func schedule() {
}
func mainButtonAction() {
}
} }

View File

@ -140,7 +140,7 @@ private final class MediaGroupsGridAlbumItemNode : ListViewItemNode {
override func didLoad() { override func didLoad() {
super.didLoad() super.didLoad()
self.imageNode.cornerRadius = 10.0 self.imageNode.cornerRadius = 5.0
if #available(iOS 13.0, *) { if #available(iOS 13.0, *) {
self.imageNode.layer.cornerCurve = .continuous self.imageNode.layer.cornerCurve = .continuous
} }
@ -221,11 +221,11 @@ private func preparedTransition(action: @escaping (PHAssetCollection) -> Void, f
} }
final class MediaGroupsAlbumGridItem: ListViewItem { final class MediaGroupsAlbumGridItem: ListViewItem {
let presentationData: ItemListPresentationData let presentationData: PresentationData
let collections: [PHAssetCollection] let collections: [PHAssetCollection]
let action: (PHAssetCollection) -> Void let action: (PHAssetCollection) -> Void
public init(presentationData: ItemListPresentationData, collections: [PHAssetCollection], action: @escaping (PHAssetCollection) -> Void) { public init(presentationData: PresentationData, collections: [PHAssetCollection], action: @escaping (PHAssetCollection) -> Void) {
self.presentationData = presentationData self.presentationData = presentationData
self.collections = collections self.collections = collections
self.action = action self.action = action
@ -347,7 +347,8 @@ private class MediaGroupsAlbumGridItemNode: ListViewItemNode {
firstItem = result.firstObject firstItem = result.firstObject
} }
if let firstItem = firstItem { if let firstItem = firstItem {
entries.append(MediaGroupsGridAlbumEntry(theme: item.presentationData.theme, index: index, collection: collection, firstItem: firstItem, count: presentationStringsFormattedNumber(Int32(result.count)))) let count = presentationStringsFormattedNumber(Int32(result.count), item.presentationData.dateTimeFormat.groupingSeparator)
entries.append(MediaGroupsGridAlbumEntry(theme: item.presentationData.theme, index: index, collection: collection, firstItem: firstItem, count: count))
index += 1 index += 1
} }
} }

View File

@ -91,7 +91,7 @@ private enum MediaGroupsEntry: Comparable, Identifiable {
case let .albumsHeader(_, text), let .smartAlbumsHeader(_, text): case let .albumsHeader(_, text), let .smartAlbumsHeader(_, text):
return MediaGroupsHeaderItem(presentationData: ItemListPresentationData(presentationData), title: text) return MediaGroupsHeaderItem(presentationData: ItemListPresentationData(presentationData), title: text)
case let .albums(_, collections): case let .albums(_, collections):
return MediaGroupsAlbumGridItem(presentationData: ItemListPresentationData(presentationData), collections: collections, action: { collection in return MediaGroupsAlbumGridItem(presentationData: presentationData, collections: collections, action: { collection in
openGroup(collection) openGroup(collection)
}) })
case let .smartAlbum(_, _, collection, count): case let .smartAlbum(_, _, collection, count):

View File

@ -154,7 +154,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
public var openCamera: ((TGAttachmentCameraView?) -> Void)? public var openCamera: ((TGAttachmentCameraView?) -> Void)?
public var presentSchedulePicker: (Bool, @escaping (Int32) -> Void) -> Void = { _, _ in } public var presentSchedulePicker: (Bool, @escaping (Int32) -> Void) -> Void = { _, _ in }
public var presentTimerPicker: (@escaping (Int32) -> Void) -> Void = { _ in } public var presentTimerPicker: (@escaping (Int32) -> Void) -> Void = { _ in }
public var presentWebSearch: (MediaGroupsScreen) -> Void = { _ in } public var presentWebSearch: (MediaGroupsScreen, Bool) -> Void = { _, _ in }
public var getCaptionPanelView: () -> TGCaptionPanelView? = { return nil } public var getCaptionPanelView: () -> TGCaptionPanelView? = { return nil }
private var completed = false private var completed = false
@ -1350,6 +1350,12 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
} }
self.updateSelectionState(count: Int32(selectionContext.count())) self.updateSelectionState(count: Int32(selectionContext.count()))
self.longTapWithTabBar = { [weak self] in
if let strongSelf = self {
strongSelf.presentSearch(activateOnDisplay: false)
}
}
} }
required init(coder aDecoder: NSCoder) { required init(coder aDecoder: NSCoder) {
@ -1572,27 +1578,34 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.controllerNode.updateNavigation(delayDisappear: true, transition: .immediate) self.controllerNode.updateNavigation(delayDisappear: true, transition: .immediate)
} }
private func presentSearch(activateOnDisplay: Bool) {
guard self.moreButtonNode.iconNode.iconState == .search else {
return
}
self.requestAttachmentMenuExpansion()
self.presentWebSearch(MediaGroupsScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, mediaAssetsContext: self.controllerNode.mediaAssetsContext, openGroup: { [weak self] collection in
if let strongSelf = self {
let mediaPicker = MediaPickerScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: strongSelf.peer, threadTitle: strongSelf.threadTitle, chatLocation: strongSelf.chatLocation, bannedSendPhotos: strongSelf.bannedSendPhotos, bannedSendVideos: strongSelf.bannedSendVideos, subject: .assets(collection), editingContext: strongSelf.interaction?.editingState, selectionContext: strongSelf.interaction?.selectionState)
mediaPicker.presentSchedulePicker = strongSelf.presentSchedulePicker
mediaPicker.presentTimerPicker = strongSelf.presentTimerPicker
mediaPicker.getCaptionPanelView = strongSelf.getCaptionPanelView
mediaPicker.legacyCompletion = strongSelf.legacyCompletion
mediaPicker.dismissAll = { [weak self] in
self?.dismiss(animated: true, completion: nil)
}
mediaPicker._presentedInModal = true
mediaPicker.updateNavigationStack = strongSelf.updateNavigationStack
strongSelf.updateNavigationStack({ _ in return ([strongSelf, mediaPicker], strongSelf.mediaPickerContext)})
}
}), activateOnDisplay)
}
@objc private func searchOrMorePressed(node: ContextReferenceContentNode, gesture: ContextGesture?) { @objc private func searchOrMorePressed(node: ContextReferenceContentNode, gesture: ContextGesture?) {
switch self.moreButtonNode.iconNode.iconState { switch self.moreButtonNode.iconNode.iconState {
case .search: case .search:
self.requestAttachmentMenuExpansion() self.presentSearch(activateOnDisplay: true)
self.presentWebSearch(MediaGroupsScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, mediaAssetsContext: self.controllerNode.mediaAssetsContext, openGroup: { [weak self] collection in
if let strongSelf = self {
let mediaPicker = MediaPickerScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: strongSelf.peer, threadTitle: strongSelf.threadTitle, chatLocation: strongSelf.chatLocation, bannedSendPhotos: strongSelf.bannedSendPhotos, bannedSendVideos: strongSelf.bannedSendVideos, subject: .assets(collection), editingContext: strongSelf.interaction?.editingState, selectionContext: strongSelf.interaction?.selectionState)
mediaPicker.presentSchedulePicker = strongSelf.presentSchedulePicker
mediaPicker.presentTimerPicker = strongSelf.presentTimerPicker
mediaPicker.getCaptionPanelView = strongSelf.getCaptionPanelView
mediaPicker.legacyCompletion = strongSelf.legacyCompletion
mediaPicker.dismissAll = { [weak self] in
self?.dismiss(animated: true, completion: nil)
}
mediaPicker._presentedInModal = true
mediaPicker.updateNavigationStack = strongSelf.updateNavigationStack
strongSelf.updateNavigationStack({ _ in return ([strongSelf, mediaPicker], strongSelf.mediaPickerContext)})
}
}))
case .more: case .more:
let strings = self.presentationData.strings let strings = self.presentationData.strings
let selectionCount = self.selectionCount let selectionCount = self.selectionCount

View File

@ -272,17 +272,29 @@ private func scanFiles(at path: String, olderThan minTimestamp: Int32, includeSu
}*/ }*/
private func statForDirectory(path: String) -> Int64 { private func statForDirectory(path: String) -> Int64 {
var s = darwin_dirstat() if #available(macOS 10.13, *) {
var result = dirstat_np(path, 1, &s, MemoryLayout<darwin_dirstat>.size) var s = darwin_dirstat()
if result != -1 { var result = dirstat_np(path, 1, &s, MemoryLayout<darwin_dirstat>.size)
return Int64(s.total_size)
} else {
result = dirstat_np(path, 0, &s, MemoryLayout<darwin_dirstat>.size)
if result != -1 { if result != -1 {
return Int64(s.total_size) return Int64(s.total_size)
} else { } else {
return 0 result = dirstat_np(path, 0, &s, MemoryLayout<darwin_dirstat>.size)
if result != -1 {
return Int64(s.total_size)
} else {
return 0
}
} }
} else {
let fileManager = FileManager.default
let folderURL = URL(fileURLWithPath: path)
var folderSize: Int64 = 0
if let files = try? fileManager.contentsOfDirectory(at: folderURL, includingPropertiesForKeys: nil, options: []) {
for file in files {
folderSize += (fileSize(file.path) ?? 0)
}
}
return folderSize
} }
} }

View File

@ -612,7 +612,18 @@ final class LocalizationListControllerNode: ViewControllerTracingNode {
} }
func updatePresentationData(_ presentationData: PresentationData) { func updatePresentationData(_ presentationData: PresentationData) {
let stringsUpdated = self.presentationData.strings !== presentationData.strings
self.presentationData = presentationData self.presentationData = presentationData
if stringsUpdated {
if let snapshotView = self.view.snapshotView(afterScreenUpdates: false) {
self.view.addSubview(snapshotView)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
}
self.presentationDataValue.set(.single(presentationData)) self.presentationDataValue.set(.single(presentationData))
self.backgroundColor = presentationData.theme.list.blocksBackgroundColor self.backgroundColor = presentationData.theme.list.blocksBackgroundColor
self.listNode.keepTopItemOverscrollBackground = ListViewKeepTopItemOverscrollBackground(color: presentationData.theme.list.blocksBackgroundColor, direction: true) self.listNode.keepTopItemOverscrollBackground = ListViewKeepTopItemOverscrollBackground(color: presentationData.theme.list.blocksBackgroundColor, direction: true)

View File

@ -876,7 +876,14 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
}) })
self.completed?(peerIds) self.completed?(peerIds)
Queue.mainQueue().after(0.44) { let delay: Double
if let peerId = peerIds.first, peerIds.count == 1 && peerId == self.context?.account.peerId {
delay = 0.88
} else {
delay = 0.44
}
Queue.mainQueue().after(delay) {
if self.hapticFeedback == nil { if self.hapticFeedback == nil {
self.hapticFeedback = HapticFeedback() self.hapticFeedback = HapticFeedback()
} }

View File

@ -2658,6 +2658,9 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
if let strongSelf = self { if let strongSelf = self {
strongSelf.call.setShouldBeRecording(false, title: nil, videoOrientation: nil) strongSelf.call.setShouldBeRecording(false, title: nil, videoOrientation: nil)
Queue.mainQueue().after(0.88) {
strongSelf.hapticFeedback.success()
}
let text: String let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info { if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
@ -2665,7 +2668,6 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
} else { } else {
text = strongSelf.presentationData.strings.VideoChat_RecordingSaved text = strongSelf.presentationData.strings.VideoChat_RecordingSaved
} }
strongSelf.presentUndoOverlay(content: .forward(savedMessages: true, text: text), action: { [weak self] value in strongSelf.presentUndoOverlay(content: .forward(savedMessages: true, text: text), action: { [weak self] value in
if case .info = value, let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController { if case .info = value, let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController {
let context = strongSelf.context let context = strongSelf.context

View File

@ -292,7 +292,7 @@ func managedProfilePhotoEmoji(postbox: Postbox, network: Network) -> Signal<Void
func managedGroupPhotoEmoji(postbox: Postbox, network: Network) -> Signal<Void, NoError> { func managedGroupPhotoEmoji(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudFeaturedGroupPhotoEmoji, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudFeaturedGroupPhotoEmoji, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
return network.request(Api.functions.account.getDefaultProfilePhotoEmojis(hash: hash)) return network.request(Api.functions.account.getDefaultGroupPhotoEmojis(hash: hash))
|> retryRequest |> retryRequest
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in |> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
switch result { switch result {

View File

@ -143,17 +143,29 @@ private extension StorageUsageStats {
} }
private func statForDirectory(path: String) -> Int64 { private func statForDirectory(path: String) -> Int64 {
var s = darwin_dirstat() if #available(macOS 10.13, *) {
var result = dirstat_np(path, 1, &s, MemoryLayout<darwin_dirstat>.size) var s = darwin_dirstat()
if result != -1 { var result = dirstat_np(path, 1, &s, MemoryLayout<darwin_dirstat>.size)
return Int64(s.total_size)
} else {
result = dirstat_np(path, 0, &s, MemoryLayout<darwin_dirstat>.size)
if result != -1 { if result != -1 {
return Int64(s.total_size) return Int64(s.total_size)
} else { } else {
return 0 result = dirstat_np(path, 0, &s, MemoryLayout<darwin_dirstat>.size)
if result != -1 {
return Int64(s.total_size)
} else {
return 0
}
} }
} else {
let fileManager = FileManager.default
let folderURL = URL(fileURLWithPath: path)
var folderSize: Int64 = 0
if let files = try? fileManager.contentsOfDirectory(at: folderURL, includingPropertiesForKeys: nil, options: []) {
for file in files {
folderSize += (fileSize(file.path) ?? 0)
}
}
return folderSize
} }
} }

View File

@ -302,7 +302,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
public var switchToTextInput: (() -> Void)? public var switchToTextInput: (() -> Void)?
private var currentState: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, interfaceState: ChatPresentationInterfaceState, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool)? private var currentState: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, interfaceState: ChatPresentationInterfaceState, layoutMetrics: LayoutMetrics, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool)?
private var scheduledContentAnimationHint: EmojiPagerContentComponent.ContentAnimation? private var scheduledContentAnimationHint: EmojiPagerContentComponent.ContentAnimation?
private var scheduledInnerTransition: Transition? private var scheduledInnerTransition: Transition?
@ -1690,22 +1690,22 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
} }
private func performLayout(transition: Transition) { private func performLayout(transition: Transition) {
guard let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible, isExpanded) = self.currentState else { guard let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, layoutMetrics, deviceMetrics, isVisible, isExpanded) = self.currentState else {
return return
} }
self.scheduledInnerTransition = transition self.scheduledInnerTransition = transition
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .immediate, interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded) let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .immediate, interfaceState: interfaceState, layoutMetrics: layoutMetrics, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded)
} }
public func simulateUpdateLayout(isVisible: Bool) { public func simulateUpdateLayout(isVisible: Bool) {
guard let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, _, isExpanded) = self.currentState else { guard let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, layoutMetrics, deviceMetrics, _, isExpanded) = self.currentState else {
return return
} }
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .immediate, interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded) let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .immediate, interfaceState: interfaceState, layoutMetrics: layoutMetrics, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded)
} }
public override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool) -> (CGFloat, CGFloat) { public override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, layoutMetrics: LayoutMetrics, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool) -> (CGFloat, CGFloat) {
self.currentState = (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible, isExpanded) self.currentState = (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, layoutMetrics, deviceMetrics, isVisible, isExpanded)
let innerTransition: Transition let innerTransition: Transition
if let scheduledInnerTransition = self.scheduledInnerTransition { if let scheduledInnerTransition = self.scheduledInnerTransition {
@ -1774,13 +1774,17 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
let startTime = CFAbsoluteTimeGetCurrent() let startTime = CFAbsoluteTimeGetCurrent()
var keyboardBottomInset = bottomInset
if case .regular = layoutMetrics.widthClass, inputHeight > 0.0 && inputHeight < 100.0 {
keyboardBottomInset = inputHeight + 15.0
}
let entityKeyboardSize = self.entityKeyboardView.update( let entityKeyboardSize = self.entityKeyboardView.update(
transition: mappedTransition, transition: mappedTransition,
component: AnyComponent(EntityKeyboardComponent( component: AnyComponent(EntityKeyboardComponent(
theme: interfaceState.theme, theme: interfaceState.theme,
strings: interfaceState.strings, strings: interfaceState.strings,
isContentInFocus: isVisible, isContentInFocus: isVisible,
containerInsets: UIEdgeInsets(top: self.isEmojiSearchActive ? -34.0 : 0.0, left: leftInset, bottom: bottomInset, right: rightInset), containerInsets: UIEdgeInsets(top: self.isEmojiSearchActive ? -34.0 : 0.0, left: leftInset, bottom: keyboardBottomInset, right: rightInset),
topPanelInsets: UIEdgeInsets(), topPanelInsets: UIEdgeInsets(),
emojiContent: emojiContent, emojiContent: emojiContent,
stickerContent: stickerContent, stickerContent: stickerContent,
@ -2427,6 +2431,7 @@ public final class EntityInputView: UIInputView, AttachmentTextInputPanelInputVi
inputPanelHeight: 0.0, inputPanelHeight: 0.0,
transition: .immediate, transition: .immediate,
interfaceState: presentationInterfaceState, interfaceState: presentationInterfaceState,
layoutMetrics: LayoutMetrics(widthClass: .compact, heightClass: .compact),
deviceMetrics: DeviceMetrics.iPhone12, deviceMetrics: DeviceMetrics.iPhone12,
isVisible: true, isVisible: true,
isExpanded: false isExpanded: false

View File

@ -28,7 +28,7 @@ open class ChatInputNode: ASDisplayNode {
} }
open func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool) -> (CGFloat, CGFloat) { open func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, layoutMetrics: LayoutMetrics, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool) -> (CGFloat, CGFloat) {
return (0.0, 0.0) return (0.0, 0.0)
} }
} }

View File

@ -7141,7 +7141,7 @@ public final class EmojiPagerContentComponent: Component {
topReactions = orderedView topReactions = orderedView
} else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedProfilePhotoEmoji { } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedProfilePhotoEmoji {
featuredAvatarEmoji = orderedView featuredAvatarEmoji = orderedView
} else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedProfilePhotoEmoji { } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedGroupPhotoEmoji {
featuredAvatarEmoji = orderedView featuredAvatarEmoji = orderedView
} }
} }

View File

@ -37,6 +37,7 @@ import StoreKit
import PhoneNumberFormat import PhoneNumberFormat
import AuthorizationUI import AuthorizationUI
import ManagedFile import ManagedFile
import DeviceProximity
#if canImport(AppCenter) #if canImport(AppCenter)
import AppCenter import AppCenter
@ -338,6 +339,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
let launchIconSize = CGSize(width: 99.0, height: 99.0) let launchIconSize = CGSize(width: 99.0, height: 99.0)
let launchIconView = UIImageView(image: UIImage(bundleImageName: "Components/LaunchLogo")) let launchIconView = UIImageView(image: UIImage(bundleImageName: "Components/LaunchLogo"))
launchIconView.autoresizingMask = [.flexibleTopMargin, .flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin]
launchIconView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((hostView.containerView.frame.width - launchIconSize.width) / 2.0), y: floorToScreenPixels((hostView.containerView.frame.height - launchIconSize.height) / 2.0)), size: launchIconSize) launchIconView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((hostView.containerView.frame.width - launchIconSize.width) / 2.0), y: floorToScreenPixels((hostView.containerView.frame.height - launchIconSize.height) / 2.0)), size: launchIconSize)
hostView.containerView.addSubview(launchIconView) hostView.containerView.addSubview(launchIconView)
@ -1336,6 +1338,13 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
self.runForegroundTasks() self.runForegroundTasks()
} }
DeviceProximityManager.shared().proximityChanged = { [weak self] value in
if let strongSelf = self {
strongSelf.mainWindow.setProximityDimHidden(!value)
}
}
if UIApplication.shared.isStatusBarHidden { if UIApplication.shared.isStatusBarHidden {
UIApplication.shared.internalSetStatusBarHidden(false, animation: .none) UIApplication.shared.internalSetStatusBarHidden(false, animation: .none)
} }

View File

@ -165,9 +165,6 @@ final class AuthorizedApplicationContext {
self.notificationController = NotificationContainerController(context: context) self.notificationController = NotificationContainerController(context: context)
self.mainWindow.previewThemeAccentColor = presentationData.theme.rootController.navigationBar.accentTextColor
self.mainWindow.previewThemeDarkBlur = presentationData.theme.rootController.keyboardColor == .dark
self.rootController = TelegramRootController(context: context) self.rootController = TelegramRootController(context: context)
self.rootController.globalOverlayControllersUpdated = { [weak self] in self.rootController.globalOverlayControllersUpdated = { [weak self] in
@ -735,8 +732,6 @@ final class AuthorizedApplicationContext {
|> deliverOnMainQueue).start(next: { [weak self] presentationData in |> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self { if let strongSelf = self {
if previousTheme.swap(presentationData.theme) !== presentationData.theme { if previousTheme.swap(presentationData.theme) !== presentationData.theme {
strongSelf.mainWindow.previewThemeAccentColor = presentationData.theme.rootController.navigationBar.accentTextColor
strongSelf.mainWindow.previewThemeDarkBlur = presentationData.theme.rootController.keyboardColor == .dark
strongSelf.lockedCoveringView.updateTheme(presentationData.theme) strongSelf.lockedCoveringView.updateTheme(presentationData.theme)
strongSelf.rootController.updateTheme(NavigationControllerTheme(presentationTheme: presentationData.theme)) strongSelf.rootController.updateTheme(NavigationControllerTheme(presentationTheme: presentationData.theme))
} }

View File

@ -163,7 +163,37 @@ private func attachmentFileControllerEntries(presentationData: PresentationData,
return entries return entries
} }
private class AttachmentFileControllerImpl: ItemListController, AttachmentContainable { private final class AttachmentFileContext: AttachmentMediaPickerContext {
var selectionCount: Signal<Int, NoError> {
return .single(0)
}
var caption: Signal<NSAttributedString?, NoError> {
return .single(nil)
}
public var loadingProgress: Signal<CGFloat?, NoError> {
return .single(nil)
}
public var mainButtonState: Signal<AttachmentMainButtonState?, NoError> {
return .single(nil)
}
func setCaption(_ caption: NSAttributedString) {
}
func send(silently: Bool, mode: AttachmentMediaPickerSendMode) {
}
func schedule() {
}
func mainButtonAction() {
}
}
class AttachmentFileControllerImpl: ItemListController, AttachmentContainable {
public var requestAttachmentMenuExpansion: () -> Void = {} public var requestAttachmentMenuExpansion: () -> Void = {}
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in } public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
@ -174,23 +204,27 @@ private class AttachmentFileControllerImpl: ItemListController, AttachmentContai
var delayDisappear = false var delayDisappear = false
var resetForReuseImpl: () -> Void = {} var resetForReuseImpl: () -> Void = {}
public func resetForReuse() { func resetForReuse() {
self.resetForReuseImpl() self.resetForReuseImpl()
self.scrollToTop?() self.scrollToTop?()
} }
public func prepareForReuse() { func prepareForReuse() {
self.delayDisappear = true self.delayDisappear = true
self.visibleBottomContentOffsetChanged?(self.visibleBottomContentOffset) self.visibleBottomContentOffsetChanged?(self.visibleBottomContentOffset)
self.delayDisappear = false self.delayDisappear = false
} }
public var mediaPickerContext: AttachmentMediaPickerContext? {
return AttachmentFileContext()
}
} }
private struct AttachmentFileControllerState: Equatable { private struct AttachmentFileControllerState: Equatable {
var searching: Bool var searching: Bool
} }
public func attachmentFileController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, bannedSendMedia: (Int32, Bool)?, presentGallery: @escaping () -> Void, presentFiles: @escaping () -> Void, send: @escaping (AnyMediaReference) -> Void) -> AttachmentContainable { func attachmentFileController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, bannedSendMedia: (Int32, Bool)?, presentGallery: @escaping () -> Void, presentFiles: @escaping () -> Void, send: @escaping (AnyMediaReference) -> Void) -> AttachmentFileControllerImpl {
let actionsDisposable = DisposableSet() let actionsDisposable = DisposableSet()
let statePromise = ValuePromise(AttachmentFileControllerState(searching: false), ignoreRepeated: true) let statePromise = ValuePromise(AttachmentFileControllerState(searching: false), ignoreRepeated: true)

View File

@ -260,7 +260,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
} }
} }
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool) -> (CGFloat, CGFloat) { override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, layoutMetrics: LayoutMetrics, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool) -> (CGFloat, CGFloat) {
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: UIScreenPixel))) transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: UIScreenPixel)))
if self.backgroundNode == nil { if self.backgroundNode == nil {

View File

@ -10653,6 +10653,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if case .standard(false) = self.presentationInterfaceState.mode, self.raiseToListen == nil { if case .standard(false) = self.presentationInterfaceState.mode, self.raiseToListen == nil {
self.raiseToListen = RaiseToListenManager(shouldActivate: { [weak self] in self.raiseToListen = RaiseToListenManager(shouldActivate: { [weak self] in
if let strongSelf = self, strongSelf.isNodeLoaded && strongSelf.canReadHistoryValue, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil, strongSelf.playlistStateAndType == nil { if let strongSelf = self, strongSelf.isNodeLoaded && strongSelf.canReadHistoryValue, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil, strongSelf.playlistStateAndType == nil {
if !strongSelf.context.sharedContext.currentMediaInputSettings.with({ $0.enableRaiseToSpeak }) {
return false
}
if strongSelf.presentationInterfaceState.inputTextPanelState.mediaRecordingState != nil { if strongSelf.presentationInterfaceState.inputTextPanelState.mediaRecordingState != nil {
return false return false
} }
@ -10670,13 +10674,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return false return false
} }
if case let .media(_, expanded, _) = strongSelf.presentationInterfaceState.inputMode, expanded != nil { if case .media = strongSelf.presentationInterfaceState.inputMode {
return false return false
} }
if !strongSelf.context.sharedContext.currentMediaInputSettings.with({ $0.enableRaiseToSpeak }) {
return false
}
return true return true
} }
@ -10712,14 +10714,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
strongSelf.chatDisplayNode.historyNode.voicePlaylistItemChanged(previousItem, currentItem) strongSelf.chatDisplayNode.historyNode.voicePlaylistItemChanged(previousItem, currentItem)
// if let currentItem = currentItem?.id as? PeerMessagesMediaPlaylistItemId {
// self.controllerInteraction?.currentlyPlayingMessageId = currentItem.messageId
// if let previousItem = previousItem?.id as? PeerMessagesMediaPlaylistItemId, previousItem.messageId.peerId == peerId, currentItem.messageId.peerId == peerId, currentItem.messageId != previousItem.messageId {
// if strongSelf.chatDisplayNode.historyNode.isMessageVisibleOnScreen(currentItem.messageId) {
// strongSelf.navigateToMessage(from: nil, to: .id(currentItem.messageId, nil), scrollPosition: .center(.bottom), rememberInStack: false, animated: true, completion: nil)
// }
// }
// }
} }
} }
@ -12630,8 +12624,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText
let currentMediaController = Atomic<MediaPickerScreen?>(value: nil) let currentMediaController = Atomic<MediaPickerScreen?>(value: nil)
let currentFilesController = Atomic<AttachmentContainable?>(value: nil) let currentFilesController = Atomic<AttachmentFileControllerImpl?>(value: nil)
let currentLocationController = Atomic<AttachmentContainable?>(value: nil) let currentLocationController = Atomic<LocationPickerController?>(value: nil)
let attachmentController = AttachmentController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, chatLocation: strongSelf.chatLocation, buttons: buttons, initialButton: initialButton, makeEntityInputView: { [weak self] in let attachmentController = AttachmentController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, chatLocation: strongSelf.chatLocation, buttons: buttons, initialButton: initialButton, makeEntityInputView: { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
@ -12680,7 +12674,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.controllerNavigationDisposable.set(nil) strongSelf.controllerNavigationDisposable.set(nil)
let existingController = currentFilesController.with { $0 } let existingController = currentFilesController.with { $0 }
if let controller = existingController { if let controller = existingController {
completion(controller, nil) completion(controller, controller.mediaPickerContext)
controller.prepareForReuse() controller.prepareForReuse()
return return
} }
@ -12703,12 +12697,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}) })
}) })
let _ = currentFilesController.swap(controller) let _ = currentFilesController.swap(controller)
completion(controller, nil) completion(controller, controller.mediaPickerContext)
case .location: case .location:
strongSelf.controllerNavigationDisposable.set(nil) strongSelf.controllerNavigationDisposable.set(nil)
let existingController = currentLocationController.with { $0 } let existingController = currentLocationController.with { $0 }
if let controller = existingController { if let controller = existingController {
completion(controller, nil) completion(controller, controller.mediaPickerContext)
controller.prepareForReuse() controller.prepareForReuse()
return return
} }
@ -12743,7 +12737,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}, nil) }, nil)
strongSelf.sendMessages([message]) strongSelf.sendMessages([message])
}) })
completion(controller, nil) completion(controller, controller.mediaPickerContext)
let _ = currentLocationController.swap(controller) let _ = currentLocationController.swap(controller)
}) })
@ -12909,7 +12903,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})) }))
case .poll: case .poll:
let controller = strongSelf.configurePollCreation() let controller = strongSelf.configurePollCreation()
completion(controller, nil) completion(controller, controller?.mediaPickerContext)
strongSelf.controllerNavigationDisposable.set(nil) strongSelf.controllerNavigationDisposable.set(nil)
case let .app(bot, botName, _): case let .app(bot, botName, _):
let params = WebAppParameters(peerId: peer.id, botId: bot.id, botName: botName, url: nil, queryId: nil, payload: botPayload, buttonText: nil, keepAliveSignal: nil, fromMenu: false, isSimple: false) let params = WebAppParameters(peerId: peer.id, botId: bot.id, botName: botName, url: nil, queryId: nil, payload: botPayload, buttonText: nil, keepAliveSignal: nil, fromMenu: false, isSimple: false)
@ -13398,8 +13392,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
controller.openCamera = { [weak self] cameraView in controller.openCamera = { [weak self] cameraView in
self?.openCamera(cameraView: cameraView) self?.openCamera(cameraView: cameraView)
} }
controller.presentWebSearch = { [weak self, weak controller] mediaGroups in controller.presentWebSearch = { [weak self, weak controller] mediaGroups, activateOnDisplay in
self?.presentWebSearch(editingMessage: false, attachment: true, present: { [weak controller] c, a in self?.presentWebSearch(editingMessage: false, attachment: true, activateOnDisplay: activateOnDisplay, present: { [weak controller] c, a in
controller?.present(c, in: .current) controller?.present(c, in: .current)
if let webSearchController = c as? WebSearchController { if let webSearchController = c as? WebSearchController {
webSearchController.searchingUpdated = { [weak mediaGroups] searching in webSearchController.searchingUpdated = { [weak mediaGroups] searching in
@ -13560,7 +13554,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}) })
} }
private func presentWebSearch(editingMessage: Bool, attachment: Bool, present: @escaping (ViewController, Any?) -> Void) { private func presentWebSearch(editingMessage: Bool, attachment: Bool, activateOnDisplay: Bool = true, present: @escaping (ViewController, Any?) -> Void) {
guard let peer = self.presentationInterfaceState.renderedPeer?.peer else { guard let peer = self.presentationInterfaceState.renderedPeer?.peer else {
return return
} }
@ -13583,7 +13577,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
} }
}) })
})) }), activateOnDisplay: activateOnDisplay)
controller.attemptItemSelection = { [weak strongSelf] item in controller.attemptItemSelection = { [weak strongSelf] item in
guard let strongSelf, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { guard let strongSelf, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
return false return false
@ -14158,7 +14152,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.present(tooltipScreen, in: .current) self.present(tooltipScreen, in: .current)
} }
private func configurePollCreation(isQuiz: Bool? = nil) -> AttachmentContainable? { private func configurePollCreation(isQuiz: Bool? = nil) -> CreatePollControllerImpl? {
guard let peer = self.presentationInterfaceState.renderedPeer?.peer else { guard let peer = self.presentationInterfaceState.renderedPeer?.peer else {
return nil return nil
} }
@ -16072,6 +16066,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.searchResultsController = nil strongSelf.searchResultsController = nil
strongController.dismiss() strongController.dismiss()
} else if peerId == strongSelf.context.account.peerId { } else if peerId == strongSelf.context.account.peerId {
Queue.mainQueue().after(0.88) {
strongSelf.chatDisplayNode.hapticFeedback.success()
}
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] value in strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] value in
if case .info = value, let strongSelf = self { if case .info = value, let strongSelf = self {
@ -18218,6 +18216,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
public func updatePushedTransition(_ fraction: CGFloat, transition: ContainedViewLayoutTransition) { public func updatePushedTransition(_ fraction: CGFloat, transition: ContainedViewLayoutTransition) {
if !transition.isAnimated {
self.chatDisplayNode.historyNodeContainer.layer.removeAllAnimations()
}
let scale: CGFloat = 1.0 - 0.06 * fraction let scale: CGFloat = 1.0 - 0.06 * fraction
transition.updateTransformScale(node: self.chatDisplayNode.historyNodeContainer, scale: scale) transition.updateTransformScale(node: self.chatDisplayNode.historyNodeContainer, scale: scale)
} }

View File

@ -164,7 +164,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
private var scheduledAnimateInAsOverlayFromNode: ASDisplayNode? private var scheduledAnimateInAsOverlayFromNode: ASDisplayNode?
private var dismissAsOverlayLayout: ContainerViewLayout? private var dismissAsOverlayLayout: ContainerViewLayout?
private var hapticFeedback: HapticFeedback? lazy var hapticFeedback = { HapticFeedback() }()
private var scrollViewDismissStatus = false private var scrollViewDismissStatus = false
var chatPresentationInterfaceState: ChatPresentationInterfaceState var chatPresentationInterfaceState: ChatPresentationInterfaceState
@ -1398,7 +1398,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
let inputHeight = layout.standardInputHeight + self.inputPanelContainerNode.expansionFraction * (maximumInputNodeHeight - layout.standardInputHeight) let inputHeight = layout.standardInputHeight + self.inputPanelContainerNode.expansionFraction * (maximumInputNodeHeight - layout.standardInputHeight)
let heightAndOverflow = inputNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, standardInputHeight: inputHeight, inputHeight: layout.inputHeight ?? 0.0, maximumHeight: maximumInputNodeHeight, inputPanelHeight: inputPanelNodeBaseHeight, transition: immediatelyLayoutInputNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: layout.deviceMetrics, isVisible: self.isInFocus, isExpanded: self.inputPanelContainerNode.stableIsExpanded) let heightAndOverflow = inputNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, standardInputHeight: inputHeight, inputHeight: layout.inputHeight ?? 0.0, maximumHeight: maximumInputNodeHeight, inputPanelHeight: inputPanelNodeBaseHeight, transition: immediatelyLayoutInputNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState, layoutMetrics: layout.metrics, deviceMetrics: layout.deviceMetrics, isVisible: self.isInFocus, isExpanded: self.inputPanelContainerNode.stableIsExpanded)
let boundedHeight = inputNode.followsDefaultHeight ? min(heightAndOverflow.0, layout.standardInputHeight) : heightAndOverflow.0 let boundedHeight = inputNode.followsDefaultHeight ? min(heightAndOverflow.0, layout.standardInputHeight) : heightAndOverflow.0
@ -1449,10 +1449,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.historyNode.isSelectionGestureEnabled = isSelectionEnabled self.historyNode.isSelectionGestureEnabled = isSelectionEnabled
if let inputMediaNode = self.inputMediaNode, inputMediaNode != self.inputNode { if let inputMediaNode = self.inputMediaNode, inputMediaNode != self.inputNode {
let _ = inputMediaNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, standardInputHeight: layout.standardInputHeight, inputHeight: layout.inputHeight ?? 0.0, maximumHeight: maximumInputNodeHeight, inputPanelHeight: inputPanelSize?.height ?? 0.0, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: layout.deviceMetrics, isVisible: false, isExpanded: self.inputPanelContainerNode.stableIsExpanded) let _ = inputMediaNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, standardInputHeight: layout.standardInputHeight, inputHeight: layout.inputHeight ?? 0.0, maximumHeight: maximumInputNodeHeight, inputPanelHeight: inputPanelSize?.height ?? 0.0, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, layoutMetrics: layout.metrics, deviceMetrics: layout.deviceMetrics, isVisible: false, isExpanded: self.inputPanelContainerNode.stableIsExpanded)
} }
transition.updateFrame(node: self.titleAccessoryPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: 116.0))) transition.updateFrame(node: self.titleAccessoryPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: 200.0)))
transition.updateFrame(node: self.inputContextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height))) transition.updateFrame(node: self.inputContextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)))
transition.updateFrame(node: self.inputContextOverTextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height))) transition.updateFrame(node: self.inputContextOverTextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)))
@ -2684,7 +2684,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
} }
self.inputMediaNode = inputNode self.inputMediaNode = inputNode
if let (validLayout, _) = self.validLayout { if let (validLayout, _) = self.validLayout {
let _ = inputNode.updateLayout(width: validLayout.size.width, leftInset: validLayout.safeInsets.left, rightInset: validLayout.safeInsets.right, bottomInset: validLayout.intrinsicInsets.bottom, standardInputHeight: validLayout.standardInputHeight, inputHeight: validLayout.inputHeight ?? 0.0, maximumHeight: validLayout.standardInputHeight, inputPanelHeight: 44.0, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: validLayout.deviceMetrics, isVisible: false, isExpanded: self.inputPanelContainerNode.stableIsExpanded) let _ = inputNode.updateLayout(width: validLayout.size.width, leftInset: validLayout.safeInsets.left, rightInset: validLayout.safeInsets.right, bottomInset: validLayout.intrinsicInsets.bottom, standardInputHeight: validLayout.standardInputHeight, inputHeight: validLayout.inputHeight ?? 0.0, maximumHeight: validLayout.standardInputHeight, inputPanelHeight: 44.0, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, layoutMetrics: validLayout.metrics, deviceMetrics: validLayout.deviceMetrics, isVisible: false, isExpanded: self.inputPanelContainerNode.stableIsExpanded)
} }
} }
@ -2904,10 +2904,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
if let scrollContainerNode = self.scrollContainerNode, scrollView === scrollContainerNode.view { if let scrollContainerNode = self.scrollContainerNode, scrollView === scrollContainerNode.view {
if self.hapticFeedback == nil { self.hapticFeedback.prepareImpact()
self.hapticFeedback = HapticFeedback()
}
self.hapticFeedback?.prepareImpact()
} }
} }
@ -2917,7 +2914,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
if dismissStatus != self.scrollViewDismissStatus { if dismissStatus != self.scrollViewDismissStatus {
self.scrollViewDismissStatus = dismissStatus self.scrollViewDismissStatus = dismissStatus
if !self.dismissedAsOverlay { if !self.dismissedAsOverlay {
self.hapticFeedback?.impact() self.hapticFeedback.impact()
} }
} }
} }

View File

@ -543,7 +543,7 @@ final class ChatMediaInputNode: ChatInputNode {
var requestDisableStickerAnimations: ((Bool) -> Void)? var requestDisableStickerAnimations: ((Bool) -> Void)?
private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, ChatPresentationInterfaceState, DeviceMetrics, Bool, Bool)? private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, ChatPresentationInterfaceState, LayoutMetrics, DeviceMetrics, Bool, Bool)?
private var paneArrangement: ChatMediaInputPaneArrangement private var paneArrangement: ChatMediaInputPaneArrangement
private var initializedArrangement = false private var initializedArrangement = false
@ -1439,7 +1439,7 @@ final class ChatMediaInputNode: ChatInputNode {
} }
}))) })))
if let (_, _, _, _, _, _, _, _, interfaceState, _, _, _) = strongSelf.validLayout { if let (_, _, _, _, _, _, _, _, interfaceState, _, _, _, _) = strongSelf.validLayout {
var isScheduledMessages = false var isScheduledMessages = false
if case .scheduledMessages = interfaceState.subject { if case .scheduledMessages = interfaceState.subject {
isScheduledMessages = true isScheduledMessages = true
@ -1561,7 +1561,7 @@ final class ChatMediaInputNode: ChatInputNode {
|> map { isStarred -> (UIView, CGRect, PeekControllerContent)? in |> map { isStarred -> (UIView, CGRect, PeekControllerContent)? in
if let strongSelf = self { if let strongSelf = self {
var menuItems: [ContextMenuItem] = [] var menuItems: [ContextMenuItem] = []
if let (_, _, _, _, _, _, _, _, interfaceState, _, _, _) = strongSelf.validLayout { if let (_, _, _, _, _, _, _, _, interfaceState, _, _, _, _) = strongSelf.validLayout {
var isScheduledMessages = false var isScheduledMessages = false
if case .scheduledMessages = interfaceState.subject { if case .scheduledMessages = interfaceState.subject {
isScheduledMessages = true isScheduledMessages = true
@ -1715,7 +1715,7 @@ final class ChatMediaInputNode: ChatInputNode {
|> map { isStarred, hasPremium -> (UIView, CGRect, PeekControllerContent)? in |> map { isStarred, hasPremium -> (UIView, CGRect, PeekControllerContent)? in
if let strongSelf = self { if let strongSelf = self {
var menuItems: [ContextMenuItem] = [] var menuItems: [ContextMenuItem] = []
if let (_, _, _, _, _, _, _, _, interfaceState, _, _, _) = strongSelf.validLayout { if let (_, _, _, _, _, _, _, _, interfaceState, _, _, _, _) = strongSelf.validLayout {
var isScheduledMessages = false var isScheduledMessages = false
if case .scheduledMessages = interfaceState.subject { if case .scheduledMessages = interfaceState.subject {
isScheduledMessages = true isScheduledMessages = true
@ -1892,8 +1892,8 @@ final class ChatMediaInputNode: ChatInputNode {
self.paneArrangement = self.paneArrangement.withIndexTransition(0.0).withCurrentIndex(index) self.paneArrangement = self.paneArrangement.withIndexTransition(0.0).withCurrentIndex(index)
let updatedGifPanelWasActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .gifs let updatedGifPanelWasActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .gifs
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible, isExpanded) = self.validLayout { if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, layoutMetrics, deviceMetrics, isVisible, isExpanded) = self.validLayout {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: transition, interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded) let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: transition, interfaceState: interfaceState, layoutMetrics: layoutMetrics, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded)
self.updateAppearanceTransition(transition: transition) self.updateAppearanceTransition(transition: transition)
} }
if updatedGifPanelWasActive != previousGifPanelWasActive { if updatedGifPanelWasActive != previousGifPanelWasActive {
@ -1910,8 +1910,8 @@ final class ChatMediaInputNode: ChatInputNode {
} }
} }
} else { } else {
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible, isExpanded) = self.validLayout { if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, layoutMetrics, deviceMetrics, isVisible, isExpanded) = self.validLayout {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .animated(duration: 0.25, curve: .spring), interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded) let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .animated(duration: 0.25, curve: .spring), interfaceState: interfaceState, layoutMetrics: layoutMetrics, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded)
} }
} }
} }
@ -2149,20 +2149,20 @@ final class ChatMediaInputNode: ChatInputNode {
} }
func simulateUpdateLayout(isVisible: Bool) { func simulateUpdateLayout(isVisible: Bool) {
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, _, isExpanded) = self.validLayout { if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, layoutMetrics, deviceMetrics, _, isExpanded) = self.validLayout {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .immediate, interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded) let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .immediate, interfaceState: interfaceState, layoutMetrics: layoutMetrics, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded)
} }
} }
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool) -> (CGFloat, CGFloat) { override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, layoutMetrics: LayoutMetrics, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool) -> (CGFloat, CGFloat) {
var searchMode: ChatMediaInputSearchMode? var searchMode: ChatMediaInputSearchMode?
if let (_, _, _, _, _, _, _, _, interfaceState, _, _, _) = self.validLayout, case let .media(_, maybeExpanded, _) = interfaceState.inputMode, let expanded = maybeExpanded, case let .search(mode) = expanded { if let (_, _, _, _, _, _, _, _, interfaceState, _, _, _, _) = self.validLayout, case let .media(_, maybeExpanded, _) = interfaceState.inputMode, let expanded = maybeExpanded, case let .search(mode) = expanded {
searchMode = mode searchMode = mode
} }
let wasVisible = self.validLayout?.10 ?? false let wasVisible = self.validLayout?.11 ?? false
self.validLayout = (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible, isExpanded) self.validLayout = (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, layoutMetrics, deviceMetrics, isVisible, isExpanded)
if self.theme !== interfaceState.theme || self.strings !== interfaceState.strings { if self.theme !== interfaceState.theme || self.strings !== interfaceState.strings {
self.updateThemeAndStrings(chatWallpaper: interfaceState.chatWallpaper, theme: interfaceState.theme, strings: interfaceState.strings) self.updateThemeAndStrings(chatWallpaper: interfaceState.chatWallpaper, theme: interfaceState.theme, strings: interfaceState.strings)
@ -2515,7 +2515,7 @@ final class ChatMediaInputNode: ChatInputNode {
self.stickerPane.removeFromSupernode() self.stickerPane.removeFromSupernode()
} }
case .changed: case .changed:
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible, isExpanded) = self.validLayout { if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, layoutMetrics, deviceMetrics, isVisible, isExpanded) = self.validLayout {
let translationX = -recognizer.translation(in: self.view).x let translationX = -recognizer.translation(in: self.view).x
var indexTransition = translationX / width var indexTransition = translationX / width
if self.paneArrangement.currentIndex == 0 { if self.paneArrangement.currentIndex == 0 {
@ -2524,10 +2524,10 @@ final class ChatMediaInputNode: ChatInputNode {
indexTransition = min(0.0, indexTransition) indexTransition = min(0.0, indexTransition)
} }
self.paneArrangement = self.paneArrangement.withIndexTransition(indexTransition) self.paneArrangement = self.paneArrangement.withIndexTransition(indexTransition)
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .immediate, interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded) let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .immediate, interfaceState: interfaceState, layoutMetrics: layoutMetrics, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded)
} }
case .ended: case .ended:
if let (width, _, _, _, _, _, _, _, _, _, _, _) = self.validLayout { if let (width, _, _, _, _, _, _, _, _, _, _, _, _) = self.validLayout {
var updatedIndex = self.paneArrangement.currentIndex var updatedIndex = self.paneArrangement.currentIndex
if abs(self.paneArrangement.indexTransition * width) > 30.0 { if abs(self.paneArrangement.indexTransition * width) > 30.0 {
if self.paneArrangement.indexTransition < 0.0 { if self.paneArrangement.indexTransition < 0.0 {
@ -2540,9 +2540,9 @@ final class ChatMediaInputNode: ChatInputNode {
self.setCurrentPane(self.paneArrangement.panes[updatedIndex], transition: .animated(duration: 0.25, curve: .spring)) self.setCurrentPane(self.paneArrangement.panes[updatedIndex], transition: .animated(duration: 0.25, curve: .spring))
} }
case .cancelled: case .cancelled:
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible, isExpanded) = self.validLayout { if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, layoutMetrics, deviceMetrics, isVisible, isExpanded) = self.validLayout {
self.paneArrangement = self.paneArrangement.withIndexTransition(0.0) self.paneArrangement = self.paneArrangement.withIndexTransition(0.0)
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .animated(duration: 0.25, curve: .spring), interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded) let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .animated(duration: 0.25, curve: .spring), interfaceState: interfaceState, layoutMetrics: layoutMetrics, deviceMetrics: deviceMetrics, isVisible: isVisible, isExpanded: isExpanded)
} }
default: default:
break break

View File

@ -1297,6 +1297,34 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
} }
} }
if let forwardInfoNode = self.forwardInfoNode, forwardInfoNode.frame.contains(location) {
if let item = self.item, let forwardInfo = item.message.forwardInfo {
let performAction: () -> Void = {
if let sourceMessageId = forwardInfo.sourceMessageId {
if !item.message.id.peerId.isReplies, let channel = forwardInfo.author as? TelegramChannel, channel.addressName == nil {
if case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
} else if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, forwardInfoNode, nil)
return
}
}
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
} else if let peer = forwardInfo.source ?? forwardInfo.author {
item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default)
} else if let _ = forwardInfo.authorSignature {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
}
}
if forwardInfoNode.hasAction(at: self.view.convert(location, to: forwardInfoNode.view)) {
return .action({})
} else {
return .optionalAction(performAction)
}
}
}
if let item = self.item, self.imageNode.frame.contains(location) { if let item = self.item, self.imageNode.frame.contains(location) {
return .optionalAction({ return .optionalAction({
let _ = item.controllerInteraction.openMessage(item.message, .default) let _ = item.controllerInteraction.openMessage(item.message, .default)

View File

@ -735,7 +735,7 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte
} }
func update(presentationData: PresentationData, constrainedWidth: CGFloat, maxHeight: CGFloat, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) -> (cleanSize: CGSize, apparentHeight: CGFloat) { func update(presentationData: PresentationData, constrainedWidth: CGFloat, maxHeight: CGFloat, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) -> (cleanSize: CGSize, apparentHeight: CGFloat) {
let constrainedSize = CGSize(width: min(260.0, constrainedWidth), height: min(604.0, maxHeight)) let constrainedSize = CGSize(width: min(220.0, constrainedWidth), height: min(604.0, maxHeight))
var topContentHeight: CGFloat = 0.0 var topContentHeight: CGFloat = 0.0
if let backButtonNode = self.backButtonNode { if let backButtonNode = self.backButtonNode {

View File

@ -306,7 +306,7 @@ final class PeerInfoHeaderNavigationTransition {
final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
let context: AccountContext let context: AccountContext
private let containerNode: ContextControllerSourceNode let containerNode: ContextControllerSourceNode
let avatarNode: AvatarNode let avatarNode: AvatarNode
fileprivate var videoNode: UniversalVideoNode? fileprivate var videoNode: UniversalVideoNode?
@ -2509,11 +2509,11 @@ final class PeerInfoHeaderNode: ASDisplayNode {
func updateAvatarIsHidden(entry: AvatarGalleryEntry?) { func updateAvatarIsHidden(entry: AvatarGalleryEntry?) {
if let entry = entry { if let entry = entry {
self.avatarListNode.avatarContainerNode.avatarNode.isHidden = entry == self.avatarListNode.listContainerNode.galleryEntries.first self.avatarListNode.avatarContainerNode.containerNode.isHidden = entry == self.avatarListNode.listContainerNode.galleryEntries.first
self.editingContentNode.avatarNode.isHidden = entry == self.avatarListNode.listContainerNode.galleryEntries.first self.editingContentNode.isHidden = entry == self.avatarListNode.listContainerNode.galleryEntries.first
} else { } else {
self.avatarListNode.avatarContainerNode.avatarNode.isHidden = false self.avatarListNode.avatarContainerNode.containerNode.isHidden = false
self.editingContentNode.avatarNode.isHidden = false self.editingContentNode.isHidden = false
} }
self.avatarListNode.listContainerNode.updateEntryIsHidden(entry: entry) self.avatarListNode.listContainerNode.updateEntryIsHidden(entry: entry)
} }

View File

@ -1950,7 +1950,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
private var editingSections: [AnyHashable: PeerInfoScreenItemSectionContainerNode] = [:] private var editingSections: [AnyHashable: PeerInfoScreenItemSectionContainerNode] = [:]
private let paneContainerNode: PeerInfoPaneContainerNode private let paneContainerNode: PeerInfoPaneContainerNode
private var ignoreScrolling: Bool = false private var ignoreScrolling: Bool = false
private var hapticFeedback: HapticFeedback? private lazy var hapticFeedback = { HapticFeedback() }()
private var customStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?) private var customStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?)
private let customStatusPromise = Promise<(PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?)>((nil, nil, nil)) private let customStatusPromise = Promise<(PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?)>((nil, nil, nil))
@ -3096,7 +3096,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
for (_, section) in strongSelf.editingSections { for (_, section) in strongSelf.editingSections {
section.animateErrorIfNeeded() section.animateErrorIfNeeded()
} }
strongSelf.hapticFeedback?.error() strongSelf.hapticFeedback.error()
return return
} }
} }
@ -3146,10 +3146,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
if (peer.firstName ?? "") != firstName || (peer.lastName ?? "") != lastName { if (peer.firstName ?? "") != firstName || (peer.lastName ?? "") != lastName {
if firstName.isEmpty && lastName.isEmpty { if firstName.isEmpty && lastName.isEmpty {
if strongSelf.hapticFeedback == nil { strongSelf.hapticFeedback.error()
strongSelf.hapticFeedback = HapticFeedback()
}
strongSelf.hapticFeedback?.error()
strongSelf.headerNode.editingContentNode.shakeTextForKey(.firstName) strongSelf.headerNode.editingContentNode.shakeTextForKey(.firstName)
} else { } else {
var dismissStatus: (() -> Void)? var dismissStatus: (() -> Void)?
@ -3212,10 +3209,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
let description = strongSelf.headerNode.editingContentNode.editingTextForKey(.description) ?? "" let description = strongSelf.headerNode.editingContentNode.editingTextForKey(.description) ?? ""
if title.isEmpty { if title.isEmpty {
if strongSelf.hapticFeedback == nil { strongSelf.hapticFeedback.error()
strongSelf.hapticFeedback = HapticFeedback()
}
strongSelf.hapticFeedback?.error()
strongSelf.headerNode.editingContentNode.shakeTextForKey(.title) strongSelf.headerNode.editingContentNode.shakeTextForKey(.title)
} else { } else {
@ -8024,6 +8018,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
if let strongSelf = self, let _ = peerSelectionController { if let strongSelf = self, let _ = peerSelectionController {
if peerId == strongSelf.context.account.peerId { if peerId == strongSelf.context.account.peerId {
Queue.mainQueue().after(0.88) {
strongSelf.hapticFeedback.success()
}
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
@ -9055,10 +9053,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
shouldBeExpanded = true shouldBeExpanded = true
if self.canOpenAvatarByDragging && self.headerNode.isAvatarExpanded && offsetY <= -32.0 { if self.canOpenAvatarByDragging && self.headerNode.isAvatarExpanded && offsetY <= -32.0 {
if self.hapticFeedback == nil { self.hapticFeedback.impact()
self.hapticFeedback = HapticFeedback()
}
self.hapticFeedback?.impact()
self.canOpenAvatarByDragging = false self.canOpenAvatarByDragging = false
let contentOffset = scrollView.contentOffset.y let contentOffset = scrollView.contentOffset.y
@ -9078,13 +9073,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
if let shouldBeExpanded = shouldBeExpanded, shouldBeExpanded != self.headerNode.isAvatarExpanded { if let shouldBeExpanded = shouldBeExpanded, shouldBeExpanded != self.headerNode.isAvatarExpanded {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.35, curve: .spring) let transition: ContainedViewLayoutTransition = .animated(duration: 0.35, curve: .spring)
if self.hapticFeedback == nil {
self.hapticFeedback = HapticFeedback()
}
if shouldBeExpanded { if shouldBeExpanded {
self.hapticFeedback?.impact() self.hapticFeedback.impact()
} else { } else {
self.hapticFeedback?.tap() self.hapticFeedback.tap()
} }
self.headerNode.updateIsAvatarExpanded(shouldBeExpanded, transition: transition) self.headerNode.updateIsAvatarExpanded(shouldBeExpanded, transition: transition)

View File

@ -9,6 +9,7 @@ import TelegramAudio
import AccountContext import AccountContext
import TelegramUniversalVideoContent import TelegramUniversalVideoContent
import DeviceProximity import DeviceProximity
import RaiseToListen
private enum SharedMediaPlaybackItem: Equatable { private enum SharedMediaPlaybackItem: Equatable {
case audio(MediaPlayer) case audio(MediaPlayer)
@ -120,7 +121,9 @@ final class SharedMediaPlayer {
private var playbackRate: AudioPlaybackRate private var playbackRate: AudioPlaybackRate
private var proximityManagerIndex: Int? //private var proximityManagerIndex: Int?
private var raiseToListen: RaiseToListenManager?
private let controlPlaybackWithProximity: Bool private let controlPlaybackWithProximity: Bool
private var forceAudioToSpeaker = false private var forceAudioToSpeaker = false
@ -346,9 +349,10 @@ final class SharedMediaPlayer {
} else { } else {
strongSelf.playbackStateValue.set(.single(nil)) strongSelf.playbackStateValue.set(.single(nil))
if !state.loading { if !state.loading {
if let proximityManagerIndex = strongSelf.proximityManagerIndex { strongSelf.raiseToListen = nil
DeviceProximityManager.shared().remove(proximityManagerIndex) // if let proximityManagerIndex = strongSelf.proximityManagerIndex {
} // DeviceProximityManager.shared().remove(proximityManagerIndex)
// }
} }
} }
} }
@ -362,18 +366,44 @@ final class SharedMediaPlayer {
}) })
if controlPlaybackWithProximity { if controlPlaybackWithProximity {
self.proximityManagerIndex = DeviceProximityManager.shared().add { [weak self] value in self.raiseToListen = RaiseToListenManager(shouldActivate: {
let forceAudioToSpeaker = !value return true
if let strongSelf = self, strongSelf.forceAudioToSpeaker != forceAudioToSpeaker { }, activate: { [weak self] in
strongSelf.forceAudioToSpeaker = forceAudioToSpeaker if let strongSelf = self {
strongSelf.playbackItem?.setForceAudioToSpeaker(forceAudioToSpeaker) let forceAudioToSpeaker = false
if !forceAudioToSpeaker { if strongSelf.forceAudioToSpeaker != forceAudioToSpeaker {
strongSelf.control(.playback(.play)) strongSelf.forceAudioToSpeaker = forceAudioToSpeaker
} else { strongSelf.playbackItem?.setForceAudioToSpeaker(forceAudioToSpeaker)
strongSelf.control(.playback(.pause)) if !forceAudioToSpeaker {
strongSelf.control(.playback(.play))
}
} }
} }
} }, deactivate: { [weak self] in
if let strongSelf = self {
let forceAudioToSpeaker = true
if strongSelf.forceAudioToSpeaker != forceAudioToSpeaker {
strongSelf.forceAudioToSpeaker = forceAudioToSpeaker
strongSelf.playbackItem?.setForceAudioToSpeaker(forceAudioToSpeaker)
if forceAudioToSpeaker {
strongSelf.control(.playback(.pause))
}
}
}
})
self.raiseToListen?.enabled = true
// self.proximityManagerIndex = DeviceProximityManager.shared().add { [weak self] value in
// let forceAudioToSpeaker = !value
// if let strongSelf = self, strongSelf.forceAudioToSpeaker != forceAudioToSpeaker {
// strongSelf.forceAudioToSpeaker = forceAudioToSpeaker
// strongSelf.playbackItem?.setForceAudioToSpeaker(forceAudioToSpeaker)
// if !forceAudioToSpeaker {
// strongSelf.control(.playback(.play))
// } else {
// strongSelf.control(.playback(.pause))
// }
// }
// }
} }
} }
@ -384,9 +414,9 @@ final class SharedMediaPlayer {
self.playbackStateValueDisposable?.dispose() self.playbackStateValueDisposable?.dispose()
self.prefetchDisposable.dispose() self.prefetchDisposable.dispose()
if let proximityManagerIndex = self.proximityManagerIndex { // if let proximityManagerIndex = self.proximityManagerIndex {
DeviceProximityManager.shared().remove(proximityManagerIndex) // DeviceProximityManager.shared().remove(proximityManagerIndex)
} // }
if let playbackItem = self.playbackItem { if let playbackItem = self.playbackItem {
switch playbackItem { switch playbackItem {

View File

@ -222,14 +222,24 @@ public func chatTranslationState(context: AccountContext, peerId: EnginePeer.Id)
let hypotheses = languageRecognizer.languageHypotheses(withMaximum: 4) let hypotheses = languageRecognizer.languageHypotheses(withMaximum: 4)
languageRecognizer.reset() languageRecognizer.reset()
let filteredLanguages = hypotheses.filter { supportedTranslationLanguages.contains($0.key.rawValue) }.sorted(by: { $0.value > $1.value }) func normalize(_ code: String) -> String {
if let language = filteredLanguages.first(where: { supportedTranslationLanguages.contains($0.key.rawValue) }) { if code.contains("-") {
let fromLang = language.key.rawValue return code.components(separatedBy: "-").first ?? code
} else if code == "nb" {
return "no"
} else {
return code
}
}
let filteredLanguages = hypotheses.filter { supportedTranslationLanguages.contains(normalize($0.key.rawValue)) }.sorted(by: { $0.value > $1.value })
if let language = filteredLanguages.first {
let fromLang = normalize(language.key.rawValue)
fromLangs[fromLang] = (fromLangs[fromLang] ?? 0) + message.text.count fromLangs[fromLang] = (fromLangs[fromLang] ?? 0) + message.text.count
count += 1 count += 1
} }
} }
if count >= 10 { if count >= 16 {
break break
} }
} }

View File

@ -25,9 +25,6 @@ public var supportedTranslationLanguages = [
"ca", "ca",
"ceb", "ceb",
"zh", "zh",
// "zh-Hant",
// "zh-CN", "zh"
// "zh-TW"
"co", "co",
"hr", "hr",
"cs", "cs",
@ -168,9 +165,20 @@ public func canTranslateText(context: AccountContext, text: String, showTranslat
supportedTranslationLanguages = ["uk", "ru"] supportedTranslationLanguages = ["uk", "ru"]
} }
let filteredLanguages = hypotheses.filter { supportedTranslationLanguages.contains($0.key.rawValue) }.sorted(by: { $0.value > $1.value }) func normalize(_ code: String) -> String {
if let language = filteredLanguages.first(where: { supportedTranslationLanguages.contains($0.key.rawValue) }) { if code.contains("-") {
return (!dontTranslateLanguages.contains(language.key.rawValue), language.key.rawValue) return code.components(separatedBy: "-").first ?? code
} else if code == "nb" {
return "no"
} else {
return code
}
}
let filteredLanguages = hypotheses.filter { supportedTranslationLanguages.contains(normalize($0.key.rawValue)) }.sorted(by: { $0.value > $1.value })
if let language = filteredLanguages.first {
let languageCode = normalize(language.key.rawValue)
return (!dontTranslateLanguages.contains(languageCode), languageCode)
} else { } else {
return (false, nil) return (false, nil)
} }

View File

@ -85,6 +85,7 @@ public final class WebSearchController: ViewController {
private let peer: EnginePeer? private let peer: EnginePeer?
private let chatLocation: ChatLocation? private let chatLocation: ChatLocation?
private let configuration: EngineConfiguration.SearchBots private let configuration: EngineConfiguration.SearchBots
private let activateOnDisplay: Bool
private var controllerNode: WebSearchControllerNode { private var controllerNode: WebSearchControllerNode {
return self.displayNode as! WebSearchControllerNode return self.displayNode as! WebSearchControllerNode
@ -121,12 +122,13 @@ public final class WebSearchController: ViewController {
public var attemptItemSelection: (ChatContextResult) -> Bool = { _ in return true } public var attemptItemSelection: (ChatContextResult) -> Bool = { _ in return true }
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer?, chatLocation: ChatLocation?, configuration: EngineConfiguration.SearchBots, mode: WebSearchControllerMode) { public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer?, chatLocation: ChatLocation?, configuration: EngineConfiguration.SearchBots, mode: WebSearchControllerMode, activateOnDisplay: Bool = true) {
self.context = context self.context = context
self.mode = mode self.mode = mode
self.peer = peer self.peer = peer
self.chatLocation = chatLocation self.chatLocation = chatLocation
self.configuration = configuration self.configuration = configuration
self.activateOnDisplay = activateOnDisplay
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
self.interfaceState = WebSearchInterfaceState(presentationData: presentationData) self.interfaceState = WebSearchInterfaceState(presentationData: presentationData)
@ -329,7 +331,7 @@ public final class WebSearchController: ViewController {
self.didPlayPresentationAnimation = true self.didPlayPresentationAnimation = true
self.controllerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.controllerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
} }
if !self.didActivateSearch { if !self.didActivateSearch && self.activateOnDisplay {
self.didActivateSearch = true self.didActivateSearch = true
self.navigationContentNode?.activate(select: select) self.navigationContentNode?.activate(select: select)
} }

View File

@ -245,9 +245,9 @@ class WebSearchControllerNode: ASDisplayNode {
self.addSubnode(self.segmentedContainerNode) self.addSubnode(self.segmentedContainerNode)
self.segmentedContainerNode.addSubnode(self.segmentedBackgroundNode) self.segmentedContainerNode.addSubnode(self.segmentedBackgroundNode)
self.segmentedContainerNode.addSubnode(self.segmentedSeparatorNode) self.segmentedContainerNode.addSubnode(self.segmentedSeparatorNode)
if case .media = mode { // if case .media = mode {
self.segmentedContainerNode.addSubnode(self.segmentedControlNode) // self.segmentedContainerNode.addSubnode(self.segmentedControlNode)
} // }
if !attachment { if !attachment {
self.addSubnode(self.toolbarBackgroundNode) self.addSubnode(self.toolbarBackgroundNode)
self.addSubnode(self.toolbarSeparatorNode) self.addSubnode(self.toolbarSeparatorNode)