mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Voice Chat UI improvements
This commit is contained in:
parent
dc1006617b
commit
49df81422b
@ -5962,7 +5962,7 @@ Sorry for the inconvenience.";
|
||||
"VoiceChat.InvitedPeerText" = "You invited %@ to the voice chat";
|
||||
"VoiceChat.RemovedPeerText" = "You removed %@ from this group";
|
||||
|
||||
"Notification.VoiceChatStarted" = "Voice chat started";
|
||||
"Notification.VoiceChatStarted" = "%1$@ started a voice chat";
|
||||
"Notification.VoiceChatEnded" = "Voice chat ended (%@)";
|
||||
|
||||
"VoiceChat.Panel.TapToJoin" = "Tap to join";
|
||||
|
@ -474,6 +474,8 @@ public protocol ChatController: ViewController {
|
||||
func updatePresentationMode(_ mode: ChatControllerPresentationMode)
|
||||
func beginMessageSearch(_ query: String)
|
||||
func displayPromoAnnouncement(text: String)
|
||||
|
||||
var isSendButtonVisible: Bool { get }
|
||||
}
|
||||
|
||||
public protocol ChatMessagePreviewItemNode: class {
|
||||
|
@ -19,7 +19,8 @@ typedef enum {
|
||||
TGCameraControllerPassportIdIntent,
|
||||
TGCameraControllerPassportMultipleIntent,
|
||||
TGCameraControllerAvatarIntent,
|
||||
TGCameraControllerSignupAvatarIntent
|
||||
TGCameraControllerSignupAvatarIntent,
|
||||
TGCameraControllerGenericPhotoOnlyIntent
|
||||
} TGCameraControllerIntent;
|
||||
|
||||
@interface TGCameraControllerWindow : TGOverlayControllerWindow
|
||||
|
@ -1250,7 +1250,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
|
||||
}
|
||||
}];
|
||||
|
||||
bool hasCamera = !self.inhibitMultipleCapture && ((_intent == TGCameraControllerGenericIntent && !_shortcut) || (_intent == TGCameraControllerPassportMultipleIntent));
|
||||
bool hasCamera = !self.inhibitMultipleCapture && (((_intent == TGCameraControllerGenericIntent || _intent == TGCameraControllerGenericPhotoOnlyIntent) && !_shortcut) || (_intent == TGCameraControllerPassportMultipleIntent));
|
||||
TGMediaPickerGalleryModel *model = [[TGMediaPickerGalleryModel alloc] initWithContext:windowContext items:galleryItems focusItem:focusItem selectionContext:_items.count > 1 ? selectionContext : nil editingContext:editingContext hasCaptions:self.allowCaptions allowCaptionEntities:self.allowCaptionEntities hasTimer:self.hasTimer onlyCrop:_intent == TGCameraControllerPassportIntent || _intent == TGCameraControllerPassportIdIntent || _intent == TGCameraControllerPassportMultipleIntent inhibitDocumentCaptions:self.inhibitDocumentCaptions hasSelectionPanel:true hasCamera:hasCamera recipientName:self.recipientName];
|
||||
model.inhibitMute = self.inhibitMute;
|
||||
model.controller = galleryController;
|
||||
|
@ -37,6 +37,7 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
|
||||
case speaker
|
||||
case airpods
|
||||
case airpodsPro
|
||||
case headphones
|
||||
case accept
|
||||
case end
|
||||
}
|
||||
@ -221,6 +222,8 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
|
||||
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAirpodsButton"), color: imageColor)
|
||||
case .airpodsPro:
|
||||
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAirpodsProButton"), color: imageColor)
|
||||
case .headphones:
|
||||
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallHeadphonesButton"), color: imageColor)
|
||||
case .accept:
|
||||
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAcceptButton"), color: imageColor)
|
||||
case .end:
|
||||
|
@ -51,6 +51,7 @@ private enum ButtonDescription: Equatable {
|
||||
case bluetooth
|
||||
case airpods
|
||||
case airpodsPro
|
||||
case headphones
|
||||
}
|
||||
|
||||
enum EndType {
|
||||
@ -205,7 +206,7 @@ final class CallControllerButtonsNode: ASDisplayNode {
|
||||
case .speaker:
|
||||
soundOutput = .speaker
|
||||
case .headphones:
|
||||
soundOutput = .bluetooth
|
||||
soundOutput = .headphones
|
||||
case let .bluetooth(type):
|
||||
switch type {
|
||||
case .generic:
|
||||
@ -296,7 +297,7 @@ final class CallControllerButtonsNode: ASDisplayNode {
|
||||
case .speaker:
|
||||
soundOutput = .speaker
|
||||
case .headphones:
|
||||
soundOutput = .builtin
|
||||
soundOutput = .headphones
|
||||
case let .bluetooth(type):
|
||||
switch type {
|
||||
case .generic:
|
||||
@ -467,6 +468,9 @@ final class CallControllerButtonsNode: ASDisplayNode {
|
||||
case .airpodsPro:
|
||||
image = .airpodsPro
|
||||
title = strings.Call_Audio
|
||||
case .headphones:
|
||||
image = .headphones
|
||||
title = strings.Call_Audio
|
||||
}
|
||||
buttonContent = CallControllerButtonItemNode.Content(
|
||||
appearance: .blurred(isFilled: isFilled),
|
||||
|
@ -296,7 +296,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
membersText = self.strings.VoiceChat_Panel_Members(Int32(data.participantCount))
|
||||
}
|
||||
|
||||
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }.filter { $0.id != self.context.account.peerId }, animated: false)
|
||||
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }, animated: false)
|
||||
|
||||
self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor)
|
||||
|
||||
@ -321,7 +321,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
|
||||
strongSelf.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: strongSelf.theme.chat.inputPanel.secondaryTextColor)
|
||||
|
||||
strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.map { $0.peer }.filter { $0.id != strongSelf.context.account.peerId }, animated: false)
|
||||
strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.map { $0.peer }, animated: false)
|
||||
|
||||
if let (size, leftInset, rightInset) = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate)
|
||||
@ -400,7 +400,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
|
||||
self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor)
|
||||
|
||||
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }.filter { $0.id != self.context.account.peerId }, animated: false)
|
||||
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }, animated: false)
|
||||
|
||||
updateAudioLevels = true
|
||||
}
|
||||
|
@ -82,8 +82,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
||||
|> distinctUntilChanged
|
||||
}
|
||||
|
||||
public var hasActiveGroupCall: Bool {
|
||||
return self.currentGroupCall != nil
|
||||
public var hasActiveCall: Bool {
|
||||
return self.currentCall != nil || self.currentGroupCall != nil
|
||||
}
|
||||
|
||||
private let currentCallPromise = Promise<PresentationCall?>(nil)
|
||||
|
@ -20,8 +20,8 @@ private let areaSize = CGSize(width: 440.0, height: 440.0)
|
||||
private let blobSize = CGSize(width: 244.0, height: 244.0)
|
||||
|
||||
final class VoiceChatActionButton: HighlightTrackingButtonNode {
|
||||
enum State {
|
||||
enum ActiveState {
|
||||
enum State: Equatable {
|
||||
enum ActiveState: Equatable {
|
||||
case cantSpeak
|
||||
case muted
|
||||
case on
|
||||
@ -31,6 +31,14 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
|
||||
case active(state: ActiveState)
|
||||
}
|
||||
|
||||
var stateValue: State {
|
||||
return self.currentParams?.state ?? .connecting
|
||||
}
|
||||
var statePromise = ValuePromise<State>()
|
||||
var state: Signal<State, NoError> {
|
||||
return self.statePromise.get()
|
||||
}
|
||||
|
||||
private let containerNode: ASDisplayNode
|
||||
private let backgroundNode: VoiceChatActionButtonBackgroundNode
|
||||
private let iconNode: VoiceChatMicrophoneNode
|
||||
@ -167,7 +175,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
|
||||
let subtitleSize = self.subtitleLabel.updateLayout(CGSize(width: size.width, height: .greatestFiniteMagnitude))
|
||||
let totalHeight = titleSize.height + subtitleSize.height + 1.0
|
||||
|
||||
self.titleLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: floor(size.height - totalHeight / 2.0) - 110.0), size: titleSize)
|
||||
self.titleLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: floor(size.height - totalHeight / 2.0) - 112.0), size: titleSize)
|
||||
self.subtitleLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - subtitleSize.width) / 2.0), y: self.titleLabel.frame.maxY + 1.0), size: subtitleSize)
|
||||
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
@ -209,7 +217,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
|
||||
}
|
||||
|
||||
private func applyIconParams() {
|
||||
guard let (size, _, state, _, small, title, subtitle, snap) = self.currentParams else {
|
||||
guard let (_, _, state, _, _, _, _, snap) = self.currentParams else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -250,6 +258,8 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
|
||||
let previousState = previous?.state
|
||||
self.currentParams = (size, buttonSize, state, dark, small, title, subtitle, previous?.snap ?? false)
|
||||
|
||||
self.statePromise.set(state)
|
||||
|
||||
var backgroundState: VoiceChatActionButtonBackgroundNode.State
|
||||
switch state {
|
||||
case let .active(state):
|
||||
@ -392,6 +402,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
||||
|
||||
private var state: State
|
||||
private var hasState = false
|
||||
|
||||
private var transition: State?
|
||||
|
||||
var audioLevel: CGFloat = 0.0 {
|
||||
|
@ -106,7 +106,7 @@ private final class VoiceChatControllerTitleNode: ASDisplayNode {
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(17.0), textColor: UIColor(rgb: 0xffffff))
|
||||
self.infoNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(13.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.5))
|
||||
|
||||
let constrainedSize = CGSize(width: size.width - 80.0, height: size.height)
|
||||
let constrainedSize = CGSize(width: size.width - 120.0, height: size.height)
|
||||
let titleSize = self.titleNode.measure(constrainedSize)
|
||||
let infoSize = self.infoNode.measure(constrainedSize)
|
||||
let titleInfoSpacing: CGFloat = 0.0
|
||||
@ -128,7 +128,6 @@ public final class VoiceChatController: ViewController {
|
||||
let isEmpty: Bool
|
||||
let crossFade: Bool
|
||||
let count: Int
|
||||
let isExpanded: Bool
|
||||
let animated: Bool
|
||||
}
|
||||
|
||||
@ -363,14 +362,14 @@ public final class VoiceChatController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
private func preparedTransition(from fromEntries: [ListEntry], to toEntries: [ListEntry], isLoading: Bool, isEmpty: Bool, crossFade: Bool, context: AccountContext, presentationData: PresentationData, interaction: Interaction, isExpanded: Bool) -> ListTransition {
|
||||
private func preparedTransition(from fromEntries: [ListEntry], to toEntries: [ListEntry], isLoading: Bool, isEmpty: Bool, crossFade: Bool, context: AccountContext, presentationData: PresentationData, interaction: Interaction) -> ListTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
||||
|
||||
return ListTransition(deletions: deletions, insertions: insertions, updates: updates, isLoading: isLoading, isEmpty: isEmpty, crossFade: crossFade, count: toEntries.count, isExpanded: isExpanded, animated: true)
|
||||
return ListTransition(deletions: deletions, insertions: insertions, updates: updates, isLoading: isLoading, isEmpty: isEmpty, crossFade: crossFade, count: toEntries.count, animated: true)
|
||||
}
|
||||
|
||||
private weak var controller: VoiceChatController?
|
||||
@ -414,7 +413,6 @@ public final class VoiceChatController: ViewController {
|
||||
private var currentCallMembers: [GroupCallParticipantsContext.Participant]?
|
||||
private var currentInvitedPeers: [Peer]?
|
||||
private var currentSpeakingPeers: Set<PeerId>?
|
||||
private var currentIsExpanded: Bool = false
|
||||
private var currentContentOffset: CGFloat?
|
||||
private var ignoreScrolling = false
|
||||
private var accountPeer: Peer?
|
||||
@ -476,6 +474,7 @@ public final class VoiceChatController: ViewController {
|
||||
self.listNode = ListView()
|
||||
self.listNode.verticalScrollIndicatorColor = UIColor(white: 1.0, alpha: 0.3)
|
||||
self.listNode.clipsToBounds = true
|
||||
self.listNode.scroller.bounces = false
|
||||
|
||||
self.topPanelNode = ASDisplayNode()
|
||||
self.topPanelNode.clipsToBounds = false
|
||||
@ -886,7 +885,7 @@ public final class VoiceChatController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: callMembers?.participants ?? [], invitedPeers: invitedPeers, speakingPeers: callMembers?.speakingParticipants ?? [], isExpanded: strongSelf.currentIsExpanded)
|
||||
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: callMembers?.participants ?? [], invitedPeers: invitedPeers, speakingPeers: callMembers?.speakingParticipants ?? [])
|
||||
|
||||
let subtitle = strongSelf.presentationData.strings.VoiceChat_Panel_Members(Int32(max(1, callMembers?.totalCount ?? 0)))
|
||||
strongSelf.currentSubtitle = subtitle
|
||||
@ -907,7 +906,7 @@ public final class VoiceChatController: ViewController {
|
||||
}
|
||||
if !strongSelf.didSetDataReady {
|
||||
strongSelf.accountPeer = accountPeer
|
||||
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: strongSelf.currentCallMembers ?? [], invitedPeers: strongSelf.currentInvitedPeers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? Set(), isExpanded: strongSelf.currentIsExpanded)
|
||||
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: strongSelf.currentCallMembers ?? [], invitedPeers: strongSelf.currentInvitedPeers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? Set())
|
||||
|
||||
if let peer = peerViewMainPeer(view), let channel = peer as? TelegramChannel {
|
||||
let addressName = channel.addressName ?? ""
|
||||
@ -1100,40 +1099,11 @@ public final class VoiceChatController: ViewController {
|
||||
self.listNode.updateFloatingHeaderOffset = { [weak self] offset, transition in
|
||||
if let strongSelf = self {
|
||||
strongSelf.currentContentOffset = offset
|
||||
strongSelf.updateFloatingHeaderOffset(offset: offset, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
self.listNode.endedInteractiveDragging = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if strongSelf.ignoreScrolling {
|
||||
Queue.mainQueue().after(0.5) {
|
||||
strongSelf.ignoreScrolling = false
|
||||
if strongSelf.animation == nil && !strongSelf.animatingExpansion {
|
||||
strongSelf.updateFloatingHeaderOffset(offset: offset, transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.listNode.visibleContentOffsetChanged = { [weak self] offset in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
switch offset {
|
||||
case let .known(value):
|
||||
// strongSelf.updateFloatingHeaderOffset(offset: -value + strongSelf.listNode.insets.top, transition: strongSelf.listNode.isTracking ? .immediate : .animated(duration: 0.4, curve: .linear))
|
||||
|
||||
if value > 5, !strongSelf.currentIsExpanded && strongSelf.listNode.isTracking && !strongSelf.ignoreScrolling {
|
||||
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers:strongSelf.currentCallMembers ?? [], invitedPeers: strongSelf.currentInvitedPeers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? Set(), isExpanded: true)
|
||||
strongSelf.ignoreScrolling = true
|
||||
} else if value < -5, strongSelf.currentIsExpanded && strongSelf.listNode.isTracking && !strongSelf.ignoreScrolling {
|
||||
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: strongSelf.currentCallMembers ?? [], invitedPeers: strongSelf.currentInvitedPeers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? [], isExpanded: false)
|
||||
strongSelf.ignoreScrolling = true
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -1223,7 +1193,7 @@ public final class VoiceChatController: ViewController {
|
||||
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
|
||||
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? [], invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set(), isExpanded: self.currentIsExpanded)
|
||||
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? [], invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set())
|
||||
}
|
||||
|
||||
@objc private func actionButtonPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer) {
|
||||
@ -1268,7 +1238,7 @@ public final class VoiceChatController: ViewController {
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? [], invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set(), isExpanded: self.currentIsExpanded)
|
||||
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? [], invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set())
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -1341,7 +1311,7 @@ public final class VoiceChatController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
private func updateFloatingHeaderOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
private func updateFloatingHeaderOffset(offset: CGFloat, transition: ContainedViewLayoutTransition, completion: (() -> Void)? = nil) {
|
||||
guard let (layout, _) = self.validLayout else {
|
||||
return
|
||||
}
|
||||
@ -1352,9 +1322,22 @@ public final class VoiceChatController: ViewController {
|
||||
let bottomAreaHeight: CGFloat = 268.0
|
||||
let bottomPanelHeight = bottomAreaHeight + layout.intrinsicInsets.bottom
|
||||
let listSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset - bottomPanelHeight)
|
||||
let topInset = self.topInset ?? listSize.height
|
||||
let topInset: CGFloat
|
||||
if let (panInitialTopInset, panOffset) = self.panGestureArguments {
|
||||
if self.isExpanded {
|
||||
topInset = min(self.topInset ?? listSize.height, panInitialTopInset + max(0.0, panOffset))
|
||||
} else {
|
||||
topInset = max(0.0, panInitialTopInset + min(0.0, panOffset))
|
||||
}
|
||||
} else if let _ = self.animation {
|
||||
topInset = self.listNode.frame.minY - listTopInset
|
||||
} else if let currentTopInset = self.topInset {
|
||||
topInset = self.isExpanded ? 0.0 : currentTopInset
|
||||
} else {
|
||||
topInset = listSize.height
|
||||
}
|
||||
|
||||
var offset = offset + topInset
|
||||
let offset = offset + topInset
|
||||
self.floatingHeaderOffset = offset
|
||||
|
||||
let rawPanelOffset = offset + listTopInset - topPanelHeight
|
||||
@ -1374,7 +1357,7 @@ public final class VoiceChatController: ViewController {
|
||||
if !topPanelFrame.equalTo(previousTopPanelFrame) {
|
||||
self.topPanelNode.frame = topPanelFrame
|
||||
let positionDelta = CGPoint(x: 0.0, y: topPanelFrame.minY - previousTopPanelFrame.minY)
|
||||
transition.animateOffsetAdditive(node: self.topPanelNode, offset: positionDelta.y)
|
||||
transition.animateOffsetAdditive(layer: self.topPanelNode.layer, offset: positionDelta.y, completion: completion)
|
||||
|
||||
self.backgroundNode.frame = backgroundFrame
|
||||
let backgroundPositionDelta = CGPoint(x: 0.0, y: previousBackgroundFrame.minY - backgroundFrame.minY)
|
||||
@ -1387,6 +1370,8 @@ public final class VoiceChatController: ViewController {
|
||||
self.rightBorderNode.frame = rightBorderFrame
|
||||
let rightBorderPositionDelta = CGPoint(x: 0.0, y: previousRightBorderFrame.minY - rightBorderFrame.minY)
|
||||
transition.animatePositionAdditive(node: self.rightBorderNode, offset: rightBorderPositionDelta)
|
||||
} else {
|
||||
completion?()
|
||||
}
|
||||
self.topPanelBackgroundNode.frame = CGRect(x: 0.0, y: topPanelHeight - 24.0, width: layout.size.width, height: 24.0)
|
||||
|
||||
@ -1422,19 +1407,17 @@ public final class VoiceChatController: ViewController {
|
||||
}
|
||||
|
||||
var isFullscreen = false
|
||||
func updateColors(fullscreen: Bool) {
|
||||
guard self.isFullscreen != fullscreen, let (layout, _) = self.validLayout else {
|
||||
func updateIsFullscreen(_ isFullscreen: Bool) {
|
||||
guard self.isFullscreen != isFullscreen, let (layout, _) = self.validLayout else {
|
||||
return
|
||||
}
|
||||
self.isFullscreen = fullscreen
|
||||
|
||||
self.controller?.statusBar.statusBarStyle = fullscreen ? .White : .Ignore
|
||||
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .linear)
|
||||
self.isFullscreen = isFullscreen
|
||||
|
||||
self.controller?.statusBar.statusBarStyle = isFullscreen ? .White : .Ignore
|
||||
|
||||
let topPanelHeight: CGFloat = 63.0
|
||||
let topEdgeFrame: CGRect
|
||||
if self.isFullscreen {
|
||||
if isFullscreen {
|
||||
let offset: CGFloat
|
||||
if let statusBarHeight = layout.statusBarHeight {
|
||||
offset = statusBarHeight
|
||||
@ -1445,16 +1428,17 @@ public final class VoiceChatController: ViewController {
|
||||
} else {
|
||||
topEdgeFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: topPanelHeight)
|
||||
}
|
||||
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .linear)
|
||||
transition.updateFrame(node: self.topPanelEdgeNode, frame: topEdgeFrame)
|
||||
transition.updateCornerRadius(node: self.topPanelEdgeNode, cornerRadius: fullscreen ? layout.deviceMetrics.screenCornerRadius - 0.5 : 12.0)
|
||||
// transition.updateBackgroundColor(node: self.dimNode, color: fullscreen ? fullscreenBackgroundColor : dimColor)
|
||||
transition.updateBackgroundColor(node: self.topPanelBackgroundNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||
transition.updateBackgroundColor(node: self.topPanelEdgeNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||
transition.updateBackgroundColor(node: self.backgroundNode, color: fullscreen ? panelBackgroundColor : secondaryPanelBackgroundColor)
|
||||
transition.updateBackgroundColor(node: self.bottomPanelBackgroundNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||
transition.updateBackgroundColor(node: self.leftBorderNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||
transition.updateBackgroundColor(node: self.rightBorderNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||
transition.updateBackgroundColor(node: self.rightBorderNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||
transition.updateCornerRadius(node: self.topPanelEdgeNode, cornerRadius: isFullscreen ? layout.deviceMetrics.screenCornerRadius - 0.5 : 12.0)
|
||||
transition.updateBackgroundColor(node: self.topPanelBackgroundNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||
transition.updateBackgroundColor(node: self.topPanelEdgeNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||
transition.updateBackgroundColor(node: self.backgroundNode, color: isFullscreen ? panelBackgroundColor : secondaryPanelBackgroundColor)
|
||||
transition.updateBackgroundColor(node: self.bottomPanelBackgroundNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||
transition.updateBackgroundColor(node: self.leftBorderNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||
transition.updateBackgroundColor(node: self.rightBorderNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||
transition.updateBackgroundColor(node: self.rightBorderNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||
|
||||
if let snapshotView = self.topCornersNode.view.snapshotContentTree() {
|
||||
snapshotView.frame = self.topCornersNode.frame
|
||||
@ -1464,7 +1448,7 @@ public final class VoiceChatController: ViewController {
|
||||
snapshotView?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
self.topCornersNode.image = cornersImage(top: true, bottom: false, dark: fullscreen)
|
||||
self.topCornersNode.image = cornersImage(top: true, bottom: false, dark: isFullscreen)
|
||||
|
||||
if let snapshotView = self.bottomCornersNode.view.snapshotContentTree() {
|
||||
snapshotView.frame = self.bottomCornersNode.bounds
|
||||
@ -1474,10 +1458,10 @@ public final class VoiceChatController: ViewController {
|
||||
snapshotView?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
self.bottomCornersNode.image = cornersImage(top: false, bottom: true, dark: fullscreen)
|
||||
self.bottomCornersNode.image = cornersImage(top: false, bottom: true, dark: isFullscreen)
|
||||
|
||||
self.optionsButton.setImage(optionsButtonImage(dark: fullscreen), animated: transition.isAnimated)
|
||||
self.closeButton.setImage(closeButtonImage(dark: fullscreen), animated: transition.isAnimated)
|
||||
self.optionsButton.setImage(optionsButtonImage(dark: isFullscreen), animated: transition.isAnimated)
|
||||
self.closeButton.setImage(closeButtonImage(dark: isFullscreen), animated: transition.isAnimated)
|
||||
|
||||
self.updateTitle(transition: transition)
|
||||
}
|
||||
@ -1536,6 +1520,7 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
let soundImage: CallControllerButtonItemNode.Content.Image
|
||||
var soundAppearance: CallControllerButtonItemNode.Content.Appearance = audioButtonAppearance
|
||||
var soundTitle: String = self.presentationData.strings.Call_Speaker
|
||||
switch audioMode {
|
||||
case .none, .builtin:
|
||||
soundImage = .speaker
|
||||
@ -1543,7 +1528,8 @@ public final class VoiceChatController: ViewController {
|
||||
soundImage = .speaker
|
||||
soundAppearance = .blurred(isFilled: true)
|
||||
case .headphones:
|
||||
soundImage = .bluetooth
|
||||
soundImage = .headphones
|
||||
soundTitle = self.presentationData.strings.Call_Audio
|
||||
case let .bluetooth(type):
|
||||
switch type {
|
||||
case .generic:
|
||||
@ -1553,10 +1539,11 @@ public final class VoiceChatController: ViewController {
|
||||
case .airpodsPro:
|
||||
soundImage = .airpodsPro
|
||||
}
|
||||
soundTitle = self.presentationData.strings.Call_Audio
|
||||
}
|
||||
|
||||
let sideButtonSize = CGSize(width: 60.0, height: 60.0)
|
||||
self.audioOutputNode.update(size: sideButtonSize, content: CallControllerButtonItemNode.Content(appearance: soundAppearance, image: soundImage), text: self.presentationData.strings.VoiceChat_Audio, transition: .animated(duration: 0.3, curve: .linear))
|
||||
self.audioOutputNode.update(size: sideButtonSize, content: CallControllerButtonItemNode.Content(appearance: soundAppearance, image: soundImage), text: soundTitle, transition: .animated(duration: 0.3, curve: .linear))
|
||||
|
||||
self.leaveNode.update(size: sideButtonSize, content: CallControllerButtonItemNode.Content(appearance: .color(.custom(0x602522)), image: .end), text: self.presentationData.strings.VoiceChat_Leave, transition: .immediate)
|
||||
}
|
||||
@ -1583,25 +1570,43 @@ public final class VoiceChatController: ViewController {
|
||||
insets.right = layout.safeInsets.right + sideInset
|
||||
|
||||
let topPanelHeight: CGFloat = 63.0
|
||||
let topEdgeFrame: CGRect
|
||||
if self.isFullscreen {
|
||||
let offset: CGFloat
|
||||
if let statusBarHeight = layout.statusBarHeight {
|
||||
offset = statusBarHeight
|
||||
} else {
|
||||
offset = 44.0
|
||||
}
|
||||
topEdgeFrame = CGRect(x: 0.0, y: -offset, width: layout.size.width, height: topPanelHeight + offset)
|
||||
if let _ = self.panGestureArguments {
|
||||
} else {
|
||||
topEdgeFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: topPanelHeight)
|
||||
let topEdgeFrame: CGRect
|
||||
if self.isFullscreen {
|
||||
let offset: CGFloat
|
||||
if let statusBarHeight = layout.statusBarHeight {
|
||||
offset = statusBarHeight
|
||||
} else {
|
||||
offset = 44.0
|
||||
}
|
||||
topEdgeFrame = CGRect(x: 0.0, y: -offset, width: layout.size.width, height: topPanelHeight + offset)
|
||||
} else {
|
||||
topEdgeFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: topPanelHeight)
|
||||
}
|
||||
transition.updateFrame(node: self.topPanelEdgeNode, frame: topEdgeFrame)
|
||||
}
|
||||
transition.updateFrame(node: self.topPanelEdgeNode, frame: topEdgeFrame)
|
||||
|
||||
let bottomPanelHeight = bottomAreaHeight + layout.intrinsicInsets.bottom
|
||||
let listTopInset = layoutTopInset + topPanelHeight
|
||||
let listSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset - bottomPanelHeight)
|
||||
|
||||
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(x: 0.0, y: listTopInset + (self.topInset ?? listSize.height)), size: listSize))
|
||||
|
||||
let topInset: CGFloat
|
||||
if let (panInitialTopInset, panOffset) = self.panGestureArguments {
|
||||
if self.isExpanded {
|
||||
topInset = min(self.topInset ?? listSize.height, panInitialTopInset + max(0.0, panOffset))
|
||||
} else {
|
||||
topInset = max(0.0, panInitialTopInset + min(0.0, panOffset))
|
||||
}
|
||||
} else if let currentTopInset = self.topInset {
|
||||
topInset = self.isExpanded ? 0.0 : currentTopInset
|
||||
} else {
|
||||
topInset = listSize.height
|
||||
}
|
||||
|
||||
if self.animation == nil {
|
||||
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(x: 0.0, y: listTopInset + topInset), size: listSize))
|
||||
}
|
||||
|
||||
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listSize, insets: insets, duration: duration, curve: curve)
|
||||
@ -1695,7 +1700,7 @@ public final class VoiceChatController: ViewController {
|
||||
}, completion: { _ in
|
||||
if self.actionButton.supernode !== self.bottomPanelNode {
|
||||
self.bottomPanelNode.addSubnode(self.actionButton)
|
||||
self.containerLayoutUpdated(layout, navigationHeight:navigationHeight, transition: .immediate)
|
||||
self.containerLayoutUpdated(layout, navigationHeight :navigationHeight, transition: .immediate)
|
||||
}
|
||||
|
||||
self.controller?.currentOverlayController?.dismiss()
|
||||
@ -1778,24 +1783,24 @@ public final class VoiceChatController: ViewController {
|
||||
let listTopInset = layoutTopInset + 63.0
|
||||
let listSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset - bottomPanelHeight)
|
||||
|
||||
let previousIsExpanded = self.currentIsExpanded
|
||||
self.currentIsExpanded = transition.isExpanded
|
||||
self.topInset = max(0.0, transition.isExpanded ? 0.0 : max(listSize.height - itemsHeight, listSize.height - 46.0 - floor(56.0 * 3.5)))
|
||||
self.topInset = max(0.0, max(listSize.height - itemsHeight, listSize.height - 46.0 - floor(56.0 * 3.5)))
|
||||
|
||||
let frameTransition: ContainedViewLayoutTransition
|
||||
if previousIsExpanded != self.currentIsExpanded {
|
||||
frameTransition = .animated(duration: 0.4, curve: .spring)
|
||||
} else {
|
||||
frameTransition = .animated(duration: 0.4, curve: .easeInOut)
|
||||
if !self.isExpanded {
|
||||
let targetY = listTopInset + (self.topInset ?? listSize.height)
|
||||
if self.listNode.frame.minY != targetY && !self.animatingExpansion && self.panGestureArguments == nil {
|
||||
self.animation = ListViewAnimation(from: self.listNode.frame.minY, to: targetY, duration: 0.4, curve: listViewAnimationCurveEaseInOut, beginAt: CACurrentMediaTime(), update: { [weak self] _, currentValue in
|
||||
if let strongSelf = self {
|
||||
var frame = strongSelf.listNode.frame
|
||||
frame.origin.y = currentValue
|
||||
strongSelf.listNode.frame = frame
|
||||
strongSelf.updateFloatingHeaderOffset(offset: strongSelf.currentContentOffset ?? 0.0, transition: .immediate)
|
||||
}
|
||||
})
|
||||
self.updateAnimation()
|
||||
}
|
||||
}
|
||||
frameTransition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(x: 0.0, y: listTopInset + (self.topInset ?? listSize.height)), size: listSize))
|
||||
|
||||
let (duration, curve) = listViewAnimationDurationAndCurve(transition: frameTransition)
|
||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listSize, insets: insets, duration: duration, curve: curve)
|
||||
|
||||
self.updateColors(fullscreen: transition.isExpanded)
|
||||
|
||||
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: nil, updateSizeAndInsets: updateSizeAndInsets, updateOpaqueState: nil, completion: { [weak self] _ in
|
||||
|
||||
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: nil, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -1804,12 +1809,42 @@ public final class VoiceChatController: ViewController {
|
||||
strongSelf.controller?.contentsReady.set(true)
|
||||
}
|
||||
})
|
||||
if previousIsExpanded != self.currentIsExpanded {
|
||||
self.updateFloatingHeaderOffset(offset: self.currentContentOffset ?? 0.0, transition: frameTransition)
|
||||
}
|
||||
|
||||
|
||||
private var animator: ConstantDisplayLinkAnimator?
|
||||
private var animation: ListViewAnimation?
|
||||
private func updateAnimation() {
|
||||
var animate = false
|
||||
let timestamp = CACurrentMediaTime()
|
||||
|
||||
if let animation = self.animation {
|
||||
animation.applyAt(timestamp)
|
||||
|
||||
if animation.completeAt(timestamp) {
|
||||
self.animation = nil
|
||||
} else {
|
||||
animate = true
|
||||
}
|
||||
}
|
||||
|
||||
if animate {
|
||||
let animator: ConstantDisplayLinkAnimator
|
||||
if let current = self.animator {
|
||||
animator = current
|
||||
} else {
|
||||
animator = ConstantDisplayLinkAnimator(update: { [weak self] in
|
||||
self?.updateAnimation()
|
||||
})
|
||||
self.animator = animator
|
||||
}
|
||||
animator.isPaused = false
|
||||
} else {
|
||||
self.animator?.isPaused = true
|
||||
}
|
||||
}
|
||||
|
||||
private func updateMembers(muteState: GroupCallParticipantsContext.Participant.MuteState?, callMembers: [GroupCallParticipantsContext.Participant], invitedPeers: [Peer], speakingPeers: Set<PeerId>, isExpanded: Bool) {
|
||||
private func updateMembers(muteState: GroupCallParticipantsContext.Participant.MuteState?, callMembers: [GroupCallParticipantsContext.Participant], invitedPeers: [Peer], speakingPeers: Set<PeerId>) {
|
||||
self.currentCallMembers = callMembers
|
||||
self.currentSpeakingPeers = speakingPeers
|
||||
self.currentInvitedPeers = invitedPeers
|
||||
@ -1874,7 +1909,7 @@ public final class VoiceChatController: ViewController {
|
||||
self.currentEntries = entries
|
||||
|
||||
let presentationData = self.presentationData.withUpdated(theme: self.darkTheme)
|
||||
let transition = preparedTransition(from: previousEntries, to: entries, isLoading: false, isEmpty: false, crossFade: false, context: self.context, presentationData: presentationData, interaction: self.itemInteraction!, isExpanded: isExpanded ?? self.currentIsExpanded)
|
||||
let transition = preparedTransition(from: previousEntries, to: entries, isLoading: false, isEmpty: false, crossFade: false, context: self.context, presentationData: presentationData, interaction: self.itemInteraction!)
|
||||
self.enqueueTransition(transition)
|
||||
}
|
||||
|
||||
@ -1882,56 +1917,190 @@ public final class VoiceChatController: ViewController {
|
||||
// if let callState = self.callState, case .connected = callState.networkState, let muteState = callState.muteState, !muteState.canUnmute {
|
||||
// return false
|
||||
// }
|
||||
if let recognizer = gestureRecognizer as? UIPanGestureRecognizer {
|
||||
let location = recognizer.location(in: self.view)
|
||||
if let view = super.hitTest(location, with: nil) {
|
||||
if let gestureRecognizers = view.gestureRecognizers, view != self.view {
|
||||
for gestureRecognizer in gestureRecognizers {
|
||||
if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer, gestureRecognizer.isEnabled {
|
||||
if panGestureRecognizer.state != .began {
|
||||
panGestureRecognizer.isEnabled = false
|
||||
panGestureRecognizer.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if let recognizer = gestureRecognizer as? UIPanGestureRecognizer {
|
||||
// let location = recognizer.location(in: self.view)
|
||||
// if let view = super.hitTest(location, with: nil) {
|
||||
// if let gestureRecognizers = view.gestureRecognizers, view != self.view {
|
||||
// for gestureRecognizer in gestureRecognizers {
|
||||
// if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer, gestureRecognizer.isEnabled {
|
||||
// if panGestureRecognizer.state != .began {
|
||||
// panGestureRecognizer.isEnabled = false
|
||||
// panGestureRecognizer.isEnabled = true
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return true
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private var isExpanded = false
|
||||
private var animatingExpansion = false
|
||||
private var panGestureArguments: (topInset: CGFloat, offset: CGFloat)?
|
||||
|
||||
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
break
|
||||
let topInset: CGFloat
|
||||
if self.isExpanded {
|
||||
topInset = 0.0
|
||||
} else if let currentTopInset = self.topInset {
|
||||
topInset = currentTopInset
|
||||
} else {
|
||||
topInset = self.listNode.frame.height
|
||||
}
|
||||
self.panGestureArguments = (topInset, 0.0)
|
||||
case .changed:
|
||||
var translation = recognizer.translation(in: self.contentContainer.view).y
|
||||
var topInset: CGFloat = 0.0
|
||||
if let (currentTopInset, currentPanOffset) = self.panGestureArguments {
|
||||
topInset = currentTopInset
|
||||
|
||||
if case let .known(value) = self.listNode.visibleContentOffset(), value > 0 {
|
||||
translation = currentPanOffset
|
||||
if self.isExpanded {
|
||||
recognizer.setTranslation(CGPoint(), in: self.contentContainer.view)
|
||||
}
|
||||
}
|
||||
|
||||
self.panGestureArguments = (currentTopInset, translation)
|
||||
}
|
||||
|
||||
let currentOffset = topInset + translation
|
||||
if currentOffset < 20.0 {
|
||||
self.updateIsFullscreen(true)
|
||||
} else if currentOffset > 40.0 {
|
||||
self.updateIsFullscreen(false)
|
||||
}
|
||||
|
||||
if self.isExpanded {
|
||||
|
||||
} else {
|
||||
if currentOffset > 0.0 {
|
||||
self.listNode.scroller.panGestureRecognizer.setTranslation(CGPoint(), in: self.listNode.scroller)
|
||||
}
|
||||
}
|
||||
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .immediate)
|
||||
self.updateFloatingHeaderOffset(offset: self.currentContentOffset ?? 0.0, transition: .immediate)
|
||||
}
|
||||
|
||||
if !self.isExpanded {
|
||||
var bounds = self.contentContainer.bounds
|
||||
bounds.origin.y = -translation
|
||||
bounds.origin.y = min(0.0, bounds.origin.y)
|
||||
self.contentContainer.bounds = bounds
|
||||
}
|
||||
case .ended:
|
||||
let translation = recognizer.translation(in: self.contentContainer.view)
|
||||
var velocity = recognizer.velocity(in: self.contentContainer.view)
|
||||
|
||||
if case let .known(value) = self.listNode.visibleContentOffset(), value > 0 {
|
||||
velocity = CGPoint()
|
||||
}
|
||||
|
||||
var bounds = self.contentContainer.bounds
|
||||
bounds.origin.y = -translation.y
|
||||
bounds.origin.y = min(0.0, bounds.origin.y)
|
||||
self.contentContainer.bounds = bounds
|
||||
case .ended:
|
||||
let translation = recognizer.translation(in: self.contentContainer.view)
|
||||
var bounds = self.contentContainer.bounds
|
||||
bounds.origin.y = -translation.y
|
||||
|
||||
let velocity = recognizer.velocity(in: self.contentContainer.view)
|
||||
|
||||
if (bounds.minY < -60.0 || velocity.y > 300.0) {
|
||||
self.controller?.dismiss(closing: false)
|
||||
let offset: CGFloat
|
||||
if let (inset, panOffset) = self.panGestureArguments {
|
||||
offset = inset + panOffset
|
||||
} else {
|
||||
var bounds = self.contentContainer.bounds
|
||||
let previousBounds = bounds
|
||||
bounds.origin.y = 0.0
|
||||
self.contentContainer.bounds = bounds
|
||||
self.contentContainer.layer.animateBounds(from: previousBounds, to: self.contentContainer.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
offset = 0.0
|
||||
}
|
||||
|
||||
let topInset: CGFloat
|
||||
if let currentTopInset = self.topInset {
|
||||
topInset = currentTopInset
|
||||
} else {
|
||||
topInset = self.listNode.frame.height
|
||||
}
|
||||
|
||||
if self.isExpanded {
|
||||
self.panGestureArguments = nil
|
||||
if velocity.y > 300.0 || offset > topInset / 2.0 {
|
||||
self.isExpanded = false
|
||||
self.updateIsFullscreen(false)
|
||||
self.animatingExpansion = true
|
||||
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
}
|
||||
self.updateFloatingHeaderOffset(offset: self.currentContentOffset ?? 0.0, transition: .animated(duration: 0.3, curve: .easeInOut), completion: {
|
||||
self.animatingExpansion = false
|
||||
})
|
||||
} else {
|
||||
self.updateIsFullscreen(true)
|
||||
self.animatingExpansion = true
|
||||
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
}
|
||||
self.updateFloatingHeaderOffset(offset: self.currentContentOffset ?? 0.0, transition: .animated(duration: 0.3, curve: .easeInOut), completion: {
|
||||
self.animatingExpansion = false
|
||||
})
|
||||
}
|
||||
} else {
|
||||
self.panGestureArguments = nil
|
||||
var dismissing = false
|
||||
if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) {
|
||||
self.controller?.dismiss(closing: false)
|
||||
dismissing = true
|
||||
} else if velocity.y < -300.0 || offset < topInset / 2.0 {
|
||||
self.isExpanded = true
|
||||
self.updateIsFullscreen(true)
|
||||
self.animatingExpansion = true
|
||||
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
}
|
||||
self.updateFloatingHeaderOffset(offset: self.currentContentOffset ?? 0.0, transition: .animated(duration: 0.3, curve: .easeInOut), completion: {
|
||||
self.animatingExpansion = false
|
||||
})
|
||||
} else {
|
||||
self.updateIsFullscreen(false)
|
||||
self.animatingExpansion = true
|
||||
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
}
|
||||
self.updateFloatingHeaderOffset(offset: self.currentContentOffset ?? 0.0, transition: .animated(duration: 0.3, curve: .easeInOut), completion: {
|
||||
self.animatingExpansion = false
|
||||
})
|
||||
}
|
||||
if !dismissing {
|
||||
var bounds = self.contentContainer.bounds
|
||||
let previousBounds = bounds
|
||||
bounds.origin.y = 0.0
|
||||
self.contentContainer.bounds = bounds
|
||||
self.contentContainer.layer.animateBounds(from: previousBounds, to: self.contentContainer.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
}
|
||||
}
|
||||
case .cancelled:
|
||||
self.panGestureArguments = nil
|
||||
|
||||
let previousBounds = self.contentContainer.bounds
|
||||
var bounds = self.contentContainer.bounds
|
||||
bounds.origin.y = 0.0
|
||||
self.contentContainer.bounds = bounds
|
||||
self.contentContainer.layer.animateBounds(from: previousBounds, to: self.contentContainer.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
}
|
||||
self.updateFloatingHeaderOffset(offset: self.currentContentOffset ?? 0.0, transition: .animated(duration: 0.3, curve: .easeInOut), completion: {
|
||||
self.animatingExpansion = false
|
||||
})
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -1941,7 +2110,7 @@ public final class VoiceChatController: ViewController {
|
||||
let result = super.hitTest(point, with: event)
|
||||
|
||||
if result === self.topPanelNode.view {
|
||||
return self.listNode.view
|
||||
return self.view
|
||||
}
|
||||
|
||||
if result === self.bottomPanelNode.view {
|
||||
@ -2071,7 +2240,11 @@ public final class VoiceChatController: ViewController {
|
||||
if let navigationController = self.navigationController as? NavigationController {
|
||||
let count = navigationController.viewControllers.count
|
||||
if count == 2 || navigationController.viewControllers[count - 2] is ChatController {
|
||||
self.detachActionButton()
|
||||
if case .active(.cantSpeak) = self.controllerNode.actionButton.stateValue {
|
||||
} else if let chatController = navigationController.viewControllers[count - 2] as? ChatController, chatController.isSendButtonVisible {
|
||||
} else {
|
||||
self.detachActionButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -53,6 +53,7 @@ public final class VoiceChatOverlayController: ViewController {
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
|
||||
if hidden {
|
||||
if slide {
|
||||
actionButton.isHidden = false
|
||||
transition.updateSublayerTransformOffset(layer: actionButton.layer, offset: CGPoint(x: slideOffset, y: 0.0))
|
||||
} else {
|
||||
actionButton.layer.removeAllAnimations()
|
||||
@ -64,10 +65,10 @@ public final class VoiceChatOverlayController: ViewController {
|
||||
}
|
||||
} else {
|
||||
actionButton.isHidden = false
|
||||
actionButton.layer.removeAllAnimations()
|
||||
if slide {
|
||||
transition.updateSublayerTransformOffset(layer: actionButton.layer, offset: CGPoint())
|
||||
} else {
|
||||
actionButton.layer.removeAllAnimations()
|
||||
actionButton.layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4)
|
||||
}
|
||||
}
|
||||
@ -91,10 +92,13 @@ public final class VoiceChatOverlayController: ViewController {
|
||||
return
|
||||
}
|
||||
|
||||
actionButton.update(snap: true, animated: !self.isSlidOffscreen)
|
||||
actionButton.update(snap: true, animated: !self.isSlidOffscreen && !self.isButtonHidden)
|
||||
if self.isSlidOffscreen {
|
||||
actionButton.layer.sublayerTransform = CATransform3DMakeTranslation(slideOffset, 0.0, 0.0)
|
||||
return
|
||||
} else if self.isButtonHidden {
|
||||
actionButton.isHidden = true
|
||||
return
|
||||
}
|
||||
|
||||
let targetPosition = actionButton.position
|
||||
@ -125,12 +129,14 @@ public final class VoiceChatOverlayController: ViewController {
|
||||
}
|
||||
|
||||
private var animating = false
|
||||
private var dismissed = false
|
||||
func animateOut(reclaim: Bool, completion: @escaping (Bool) -> Void) {
|
||||
guard let actionButton = self.controller?.actionButton, let layout = self.validLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
if reclaim {
|
||||
self.dismissed = true
|
||||
let targetPosition = CGPoint(x: layout.size.width / 2.0, y: layout.size.height - layout.intrinsicInsets.bottom - 268.0 / 2.0)
|
||||
if self.isSlidOffscreen {
|
||||
self.isSlidOffscreen = false
|
||||
@ -139,6 +145,13 @@ public final class VoiceChatOverlayController: ViewController {
|
||||
actionButton.update(snap: false, animated: false)
|
||||
actionButton.position = CGPoint(x: targetPosition.x, y: 268.0 / 2.0)
|
||||
completion(true)
|
||||
} else if self.isButtonHidden {
|
||||
actionButton.isHidden = false
|
||||
actionButton.layer.removeAllAnimations()
|
||||
actionButton.layer.sublayerTransform = CATransform3DIdentity
|
||||
actionButton.update(snap: false, animated: false)
|
||||
actionButton.position = CGPoint(x: targetPosition.x, y: 268.0 / 2.0)
|
||||
completion(true)
|
||||
} else {
|
||||
self.animating = true
|
||||
let sourcePoint = actionButton.position
|
||||
@ -193,17 +206,18 @@ public final class VoiceChatOverlayController: ViewController {
|
||||
return nil
|
||||
}
|
||||
|
||||
private var didAnimateIn = false
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = layout
|
||||
|
||||
if let actionButton = self.controller?.actionButton, !self.animating {
|
||||
if let actionButton = self.controller?.actionButton, !self.animating && !self.dismissed {
|
||||
let convertedRect = actionButton.view.convert(actionButton.bounds, to: self.view)
|
||||
let insets = layout.insets(options: [.input])
|
||||
transition.updatePosition(node: actionButton, position: CGPoint(x: layout.size.width - layout.safeInsets.right - 21.0, y: layout.size.height - insets.bottom - 22.0))
|
||||
|
||||
if actionButton.supernode !== self {
|
||||
if actionButton.supernode !== self && !self.didAnimateIn {
|
||||
self.didAnimateIn = true
|
||||
self.addSubnode(actionButton)
|
||||
|
||||
self.animateIn(from: convertedRect)
|
||||
}
|
||||
}
|
||||
@ -224,15 +238,18 @@ public final class VoiceChatOverlayController: ViewController {
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
self.additionalSideInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 75.0)
|
||||
|
||||
if case .active(.cantSpeak) = actionButton.stateValue {
|
||||
} else {
|
||||
self.additionalSideInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 75.0)
|
||||
}
|
||||
if let navigationController = navigationController {
|
||||
let controllers: Signal<[UIViewController], NoError> = .single([])
|
||||
|> then(navigationController.viewControllersSignal)
|
||||
let overlayControllers: Signal<[UIViewController], NoError> = .single([])
|
||||
|> then(navigationController.overlayControllersSignal)
|
||||
|
||||
self.disposable = (combineLatest(queue: Queue.mainQueue(), controllers, overlayControllers)).start(next: { [weak self] controllers, overlayControllers in
|
||||
self.disposable = (combineLatest(queue: Queue.mainQueue(), controllers, overlayControllers, actionButton.state)).start(next: { [weak self] controllers, overlayControllers, state in
|
||||
if let strongSelf = self {
|
||||
var hasVoiceChatController = false
|
||||
var overlayControllersCount = 0
|
||||
@ -248,19 +265,37 @@ public final class VoiceChatOverlayController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
var slide = true
|
||||
var hidden = true
|
||||
var animated = true
|
||||
if controllers.count == 1 || controllers.last is ChatController {
|
||||
hidden = false
|
||||
if let chatController = controllers.last as? ChatController, chatController.isSendButtonVisible {
|
||||
slide = false
|
||||
animated = false
|
||||
} else {
|
||||
hidden = false
|
||||
}
|
||||
}
|
||||
if overlayControllersCount > 0 {
|
||||
hidden = true
|
||||
}
|
||||
|
||||
if case .active(.cantSpeak) = state {
|
||||
hidden = true
|
||||
}
|
||||
if hasVoiceChatController {
|
||||
hidden = false
|
||||
animated = false
|
||||
}
|
||||
strongSelf.controllerNode.update(hidden: hidden, slide: true, animated: animated)
|
||||
|
||||
strongSelf.controllerNode.update(hidden: hidden, slide: slide, animated: animated)
|
||||
|
||||
let previousInsets = strongSelf.additionalSideInsets
|
||||
strongSelf.additionalSideInsets = hidden ? UIEdgeInsets() : UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 75.0)
|
||||
|
||||
if previousInsets != strongSelf.additionalSideInsets {
|
||||
navigationController.requestLayout(transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -405,13 +405,14 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
}
|
||||
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
|
||||
case let .groupPhoneCall(_, _, duration):
|
||||
let titleString: String
|
||||
if let duration = duration {
|
||||
titleString = strings.Notification_VoiceChatEnded(callDurationString(strings: strings, value: duration)).0
|
||||
let titleString = strings.Notification_VoiceChatEnded(callDurationString(strings: strings, value: duration)).0
|
||||
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
|
||||
} else {
|
||||
titleString = strings.Notification_VoiceChatStarted
|
||||
var attributePeerIds: [(Int, PeerId?)] = [(0, message.author?.id)]
|
||||
let titleString = strings.Notification_VoiceChatStarted(authorName)
|
||||
attributedString = addAttributesToStringWithRanges(titleString, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
|
||||
}
|
||||
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
|
||||
case let .customText(text, entities):
|
||||
attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false)
|
||||
case let .botDomainAccessGranted(domain):
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Call/CallHeadphonesButton.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Call/CallHeadphonesButton.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_call_headphones.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Call/CallHeadphonesButton.imageset/ic_call_headphones.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Call/CallHeadphonesButton.imageset/ic_call_headphones.pdf
vendored
Normal file
Binary file not shown.
Binary file not shown.
@ -6221,9 +6221,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
let shouldBeActive = combineLatest(self.context.sharedContext.mediaManager.audioSession.isPlaybackActive() |> deliverOnMainQueue, self.chatDisplayNode.historyNode.hasVisiblePlayableItemNodes)
|
||||
|> mapToSignal { [weak self] isPlaybackActive, hasVisiblePlayableItemNodes -> Signal<Bool, NoError> in
|
||||
if hasVisiblePlayableItemNodes && !isPlaybackActive {
|
||||
let hasActiveCalls: Signal<Bool, NoError>
|
||||
if let callManager = self.context.sharedContext.callManager as? PresentationCallManagerImpl {
|
||||
hasActiveCalls = callManager.hasActiveCalls
|
||||
} else {
|
||||
hasActiveCalls = .single(false)
|
||||
}
|
||||
|
||||
let shouldBeActive = combineLatest(self.context.sharedContext.mediaManager.audioSession.isPlaybackActive() |> deliverOnMainQueue, self.chatDisplayNode.historyNode.hasVisiblePlayableItemNodes, hasActiveCalls)
|
||||
|> mapToSignal { [weak self] isPlaybackActive, hasVisiblePlayableItemNodes, hasActiveCalls -> Signal<Bool, NoError> in
|
||||
if hasVisiblePlayableItemNodes && !isPlaybackActive && !hasActiveCalls {
|
||||
return Signal<Bool, NoError> { [weak self] subscriber in
|
||||
guard let strongSelf = self else {
|
||||
subscriber.putCompletion()
|
||||
@ -7060,7 +7067,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.saveInterfaceState(includeScrollState: false)
|
||||
}
|
||||
|
||||
if let navigationController = self.navigationController as? NavigationController {
|
||||
if let navigationController = self.navigationController as? NavigationController, self.traceVisibility() && isTopmostChatController(self) {
|
||||
var voiceChatOverlayController: VoiceChatOverlayController?
|
||||
for controller in navigationController.globalOverlayControllers {
|
||||
if let controller = controller as? VoiceChatOverlayController {
|
||||
@ -7070,11 +7077,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
if let controller = voiceChatOverlayController {
|
||||
var hidden = false
|
||||
if self.presentationInterfaceState.interfaceState.editMessage != nil || self.presentationInterfaceState.interfaceState.forwardMessageIds != nil || self.presentationInterfaceState.interfaceState.composeInputState.inputText.string.count > 0 {
|
||||
hidden = true
|
||||
}
|
||||
controller.update(hidden: hidden, slide: false, animated: true)
|
||||
controller.update(hidden: self.isSendButtonVisible, slide: false, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7669,11 +7672,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
}, openCamera: { [weak self] cameraView, menuController in
|
||||
if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
|
||||
if let callManager = strongSelf.context.sharedContext.callManager as? PresentationCallManagerImpl, callManager.hasActiveGroupCall {
|
||||
return
|
||||
var photoOnly = false
|
||||
if let callManager = strongSelf.context.sharedContext.callManager as? PresentationCallManagerImpl, callManager.hasActiveCall {
|
||||
photoOnly = true
|
||||
}
|
||||
|
||||
presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: settings.storeEditedPhotos, mediaGrouping: true, initialCaption: inputText.string, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
|
||||
presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: settings.storeEditedPhotos, mediaGrouping: true, initialCaption: inputText.string, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, photoOnly: photoOnly, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
|
||||
if let strongSelf = self {
|
||||
if editMediaOptions != nil {
|
||||
strongSelf.editMessageMediaWithLegacySignals(signals!)
|
||||
@ -11307,6 +11311,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
//return self.chatDisplayNode.acceptEmbeddedTitlePeekContent(content: content)
|
||||
return false
|
||||
}
|
||||
|
||||
public var isSendButtonVisible: Bool {
|
||||
if self.presentationInterfaceState.interfaceState.editMessage != nil || self.presentationInterfaceState.interfaceState.forwardMessageIds != nil || self.presentationInterfaceState.interfaceState.composeInputState.inputText.string.count > 0 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
||||
|
@ -1236,7 +1236,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
|
||||
let searchLayoutClearButtonSize = CGSize(width: 44.0, height: minimalHeight)
|
||||
var textFieldInsets = self.textFieldInsets(metrics: metrics)
|
||||
if additionalSideInsets.right > 0.0 && self.text.isEmpty {
|
||||
if additionalSideInsets.right > 0.0 {
|
||||
textFieldInsets.right += additionalSideInsets.right / 3.0
|
||||
}
|
||||
self.actionButtons.micButton.isHidden = additionalSideInsets.right > 0.0
|
||||
|
@ -11,7 +11,7 @@ import ShareController
|
||||
import LegacyUI
|
||||
import LegacyMediaPickerUI
|
||||
|
||||
func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocation: ChatLocation, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: String, hasSchedule: Bool, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?) {
|
||||
func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocation: ChatLocation, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: String, hasSchedule: Bool, photoOnly: Bool, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?) {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
|
||||
legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)
|
||||
@ -23,7 +23,7 @@ func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocation: Ch
|
||||
|
||||
let controller: TGCameraController
|
||||
if let cameraView = cameraView, let previewView = cameraView.previewView() {
|
||||
controller = TGCameraController(context: legacyController.context, saveEditedPhotos: saveCapturedPhotos && !isSecretChat, saveCapturedMedia: saveCapturedPhotos && !isSecretChat, camera: previewView.camera, previewView: previewView, intent: TGCameraControllerGenericIntent)
|
||||
controller = TGCameraController(context: legacyController.context, saveEditedPhotos: saveCapturedPhotos && !isSecretChat, saveCapturedMedia: saveCapturedPhotos && !isSecretChat, camera: previewView.camera, previewView: previewView, intent: photoOnly ? TGCameraControllerGenericPhotoOnlyIntent : TGCameraControllerGenericIntent)
|
||||
controller.inhibitMultipleCapture = editingMedia
|
||||
} else {
|
||||
controller = TGCameraController()
|
||||
|
@ -109,6 +109,7 @@ public final class NotificationContainerController: ViewController {
|
||||
let toAlpha: CGFloat = value ? 0.0 : 1.0
|
||||
self.controllerNode.alpha = toAlpha
|
||||
self.controllerNode.layer.animateAlpha(from: fromAlpha, to: toAlpha, duration: 0.2)
|
||||
self.controllerNode.isUserInteractionEnabled = !value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user