mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit 'f041078fe401812660b1c7be8dc5265e88ba35a8'
This commit is contained in:
commit
6c94c37ae0
@ -5962,7 +5962,7 @@ Sorry for the inconvenience.";
|
|||||||
"VoiceChat.InvitedPeerText" = "You invited %@ to the voice chat";
|
"VoiceChat.InvitedPeerText" = "You invited %@ to the voice chat";
|
||||||
"VoiceChat.RemovedPeerText" = "You removed %@ from this group";
|
"VoiceChat.RemovedPeerText" = "You removed %@ from this group";
|
||||||
|
|
||||||
"Notification.VoiceChatStarted" = "%@ started a voice chat";
|
"Notification.VoiceChatStarted" = "%1$@ started a voice chat";
|
||||||
"Notification.VoiceChatEnded" = "Voice chat ended (%@)";
|
"Notification.VoiceChatEnded" = "Voice chat ended (%@)";
|
||||||
|
|
||||||
"VoiceChat.Panel.TapToJoin" = "Tap to join";
|
"VoiceChat.Panel.TapToJoin" = "Tap to join";
|
||||||
|
@ -474,6 +474,8 @@ public protocol ChatController: ViewController {
|
|||||||
func updatePresentationMode(_ mode: ChatControllerPresentationMode)
|
func updatePresentationMode(_ mode: ChatControllerPresentationMode)
|
||||||
func beginMessageSearch(_ query: String)
|
func beginMessageSearch(_ query: String)
|
||||||
func displayPromoAnnouncement(text: String)
|
func displayPromoAnnouncement(text: String)
|
||||||
|
|
||||||
|
var isSendButtonVisible: Bool { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol ChatMessagePreviewItemNode: class {
|
public protocol ChatMessagePreviewItemNode: class {
|
||||||
|
@ -19,7 +19,8 @@ typedef enum {
|
|||||||
TGCameraControllerPassportIdIntent,
|
TGCameraControllerPassportIdIntent,
|
||||||
TGCameraControllerPassportMultipleIntent,
|
TGCameraControllerPassportMultipleIntent,
|
||||||
TGCameraControllerAvatarIntent,
|
TGCameraControllerAvatarIntent,
|
||||||
TGCameraControllerSignupAvatarIntent
|
TGCameraControllerSignupAvatarIntent,
|
||||||
|
TGCameraControllerGenericPhotoOnlyIntent
|
||||||
} TGCameraControllerIntent;
|
} TGCameraControllerIntent;
|
||||||
|
|
||||||
@interface TGCameraControllerWindow : TGOverlayControllerWindow
|
@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];
|
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.inhibitMute = self.inhibitMute;
|
||||||
model.controller = galleryController;
|
model.controller = galleryController;
|
||||||
|
@ -37,6 +37,7 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
|
|||||||
case speaker
|
case speaker
|
||||||
case airpods
|
case airpods
|
||||||
case airpodsPro
|
case airpodsPro
|
||||||
|
case headphones
|
||||||
case accept
|
case accept
|
||||||
case end
|
case end
|
||||||
}
|
}
|
||||||
@ -221,6 +222,8 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
|
|||||||
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAirpodsButton"), color: imageColor)
|
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAirpodsButton"), color: imageColor)
|
||||||
case .airpodsPro:
|
case .airpodsPro:
|
||||||
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAirpodsProButton"), color: imageColor)
|
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAirpodsProButton"), color: imageColor)
|
||||||
|
case .headphones:
|
||||||
|
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallHeadphonesButton"), color: imageColor)
|
||||||
case .accept:
|
case .accept:
|
||||||
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAcceptButton"), color: imageColor)
|
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAcceptButton"), color: imageColor)
|
||||||
case .end:
|
case .end:
|
||||||
|
@ -51,6 +51,7 @@ private enum ButtonDescription: Equatable {
|
|||||||
case bluetooth
|
case bluetooth
|
||||||
case airpods
|
case airpods
|
||||||
case airpodsPro
|
case airpodsPro
|
||||||
|
case headphones
|
||||||
}
|
}
|
||||||
|
|
||||||
enum EndType {
|
enum EndType {
|
||||||
@ -205,7 +206,7 @@ final class CallControllerButtonsNode: ASDisplayNode {
|
|||||||
case .speaker:
|
case .speaker:
|
||||||
soundOutput = .speaker
|
soundOutput = .speaker
|
||||||
case .headphones:
|
case .headphones:
|
||||||
soundOutput = .bluetooth
|
soundOutput = .headphones
|
||||||
case let .bluetooth(type):
|
case let .bluetooth(type):
|
||||||
switch type {
|
switch type {
|
||||||
case .generic:
|
case .generic:
|
||||||
@ -296,7 +297,7 @@ final class CallControllerButtonsNode: ASDisplayNode {
|
|||||||
case .speaker:
|
case .speaker:
|
||||||
soundOutput = .speaker
|
soundOutput = .speaker
|
||||||
case .headphones:
|
case .headphones:
|
||||||
soundOutput = .builtin
|
soundOutput = .headphones
|
||||||
case let .bluetooth(type):
|
case let .bluetooth(type):
|
||||||
switch type {
|
switch type {
|
||||||
case .generic:
|
case .generic:
|
||||||
@ -467,6 +468,9 @@ final class CallControllerButtonsNode: ASDisplayNode {
|
|||||||
case .airpodsPro:
|
case .airpodsPro:
|
||||||
image = .airpodsPro
|
image = .airpodsPro
|
||||||
title = strings.Call_Audio
|
title = strings.Call_Audio
|
||||||
|
case .headphones:
|
||||||
|
image = .headphones
|
||||||
|
title = strings.Call_Audio
|
||||||
}
|
}
|
||||||
buttonContent = CallControllerButtonItemNode.Content(
|
buttonContent = CallControllerButtonItemNode.Content(
|
||||||
appearance: .blurred(isFilled: isFilled),
|
appearance: .blurred(isFilled: isFilled),
|
||||||
|
@ -296,7 +296,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
|||||||
membersText = self.strings.VoiceChat_Panel_Members(Int32(data.participantCount))
|
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)
|
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.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 {
|
if let (size, leftInset, rightInset) = strongSelf.validLayout {
|
||||||
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate)
|
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.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
|
updateAudioLevels = true
|
||||||
}
|
}
|
||||||
|
@ -82,8 +82,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
public var hasActiveGroupCall: Bool {
|
public var hasActiveCall: Bool {
|
||||||
return self.currentGroupCall != nil
|
return self.currentCall != nil || self.currentGroupCall != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private let currentCallPromise = Promise<PresentationCall?>(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)
|
private let blobSize = CGSize(width: 244.0, height: 244.0)
|
||||||
|
|
||||||
final class VoiceChatActionButton: HighlightTrackingButtonNode {
|
final class VoiceChatActionButton: HighlightTrackingButtonNode {
|
||||||
enum State {
|
enum State: Equatable {
|
||||||
enum ActiveState {
|
enum ActiveState: Equatable {
|
||||||
case cantSpeak
|
case cantSpeak
|
||||||
case muted
|
case muted
|
||||||
case on
|
case on
|
||||||
@ -31,6 +31,14 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
|
|||||||
case active(state: ActiveState)
|
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 containerNode: ASDisplayNode
|
||||||
private let backgroundNode: VoiceChatActionButtonBackgroundNode
|
private let backgroundNode: VoiceChatActionButtonBackgroundNode
|
||||||
private let iconNode: VoiceChatMicrophoneNode
|
private let iconNode: VoiceChatMicrophoneNode
|
||||||
@ -167,7 +175,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
|
|||||||
let subtitleSize = self.subtitleLabel.updateLayout(CGSize(width: size.width, height: .greatestFiniteMagnitude))
|
let subtitleSize = self.subtitleLabel.updateLayout(CGSize(width: size.width, height: .greatestFiniteMagnitude))
|
||||||
let totalHeight = titleSize.height + subtitleSize.height + 1.0
|
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.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)
|
self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||||
@ -209,7 +217,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func applyIconParams() {
|
private func applyIconParams() {
|
||||||
guard let (size, _, state, _, small, title, subtitle, snap) = self.currentParams else {
|
guard let (_, _, state, _, _, _, _, snap) = self.currentParams else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,6 +258,8 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
|
|||||||
let previousState = previous?.state
|
let previousState = previous?.state
|
||||||
self.currentParams = (size, buttonSize, state, dark, small, title, subtitle, previous?.snap ?? false)
|
self.currentParams = (size, buttonSize, state, dark, small, title, subtitle, previous?.snap ?? false)
|
||||||
|
|
||||||
|
self.statePromise.set(state)
|
||||||
|
|
||||||
var backgroundState: VoiceChatActionButtonBackgroundNode.State
|
var backgroundState: VoiceChatActionButtonBackgroundNode.State
|
||||||
switch state {
|
switch state {
|
||||||
case let .active(state):
|
case let .active(state):
|
||||||
@ -392,6 +402,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
|||||||
|
|
||||||
private var state: State
|
private var state: State
|
||||||
private var hasState = false
|
private var hasState = false
|
||||||
|
|
||||||
private var transition: State?
|
private var transition: State?
|
||||||
|
|
||||||
var audioLevel: CGFloat = 0.0 {
|
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.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))
|
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 titleSize = self.titleNode.measure(constrainedSize)
|
||||||
let infoSize = self.infoNode.measure(constrainedSize)
|
let infoSize = self.infoNode.measure(constrainedSize)
|
||||||
let titleInfoSpacing: CGFloat = 0.0
|
let titleInfoSpacing: CGFloat = 0.0
|
||||||
@ -128,7 +128,6 @@ public final class VoiceChatController: ViewController {
|
|||||||
let isEmpty: Bool
|
let isEmpty: Bool
|
||||||
let crossFade: Bool
|
let crossFade: Bool
|
||||||
let count: Int
|
let count: Int
|
||||||
let isExpanded: Bool
|
|
||||||
let animated: 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 (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||||
|
|
||||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
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 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) }
|
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?
|
private weak var controller: VoiceChatController?
|
||||||
@ -414,7 +413,6 @@ public final class VoiceChatController: ViewController {
|
|||||||
private var currentCallMembers: [GroupCallParticipantsContext.Participant]?
|
private var currentCallMembers: [GroupCallParticipantsContext.Participant]?
|
||||||
private var currentInvitedPeers: [Peer]?
|
private var currentInvitedPeers: [Peer]?
|
||||||
private var currentSpeakingPeers: Set<PeerId>?
|
private var currentSpeakingPeers: Set<PeerId>?
|
||||||
private var currentIsExpanded: Bool = false
|
|
||||||
private var currentContentOffset: CGFloat?
|
private var currentContentOffset: CGFloat?
|
||||||
private var ignoreScrolling = false
|
private var ignoreScrolling = false
|
||||||
private var accountPeer: Peer?
|
private var accountPeer: Peer?
|
||||||
@ -476,6 +474,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.listNode = ListView()
|
self.listNode = ListView()
|
||||||
self.listNode.verticalScrollIndicatorColor = UIColor(white: 1.0, alpha: 0.3)
|
self.listNode.verticalScrollIndicatorColor = UIColor(white: 1.0, alpha: 0.3)
|
||||||
self.listNode.clipsToBounds = true
|
self.listNode.clipsToBounds = true
|
||||||
|
self.listNode.scroller.bounces = false
|
||||||
|
|
||||||
self.topPanelNode = ASDisplayNode()
|
self.topPanelNode = ASDisplayNode()
|
||||||
self.topPanelNode.clipsToBounds = false
|
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)))
|
let subtitle = strongSelf.presentationData.strings.VoiceChat_Panel_Members(Int32(max(1, callMembers?.totalCount ?? 0)))
|
||||||
strongSelf.currentSubtitle = subtitle
|
strongSelf.currentSubtitle = subtitle
|
||||||
@ -907,7 +906,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
if !strongSelf.didSetDataReady {
|
if !strongSelf.didSetDataReady {
|
||||||
strongSelf.accountPeer = accountPeer
|
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 {
|
if let peer = peerViewMainPeer(view), let channel = peer as? TelegramChannel {
|
||||||
let addressName = channel.addressName ?? ""
|
let addressName = channel.addressName ?? ""
|
||||||
@ -1100,40 +1099,11 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.listNode.updateFloatingHeaderOffset = { [weak self] offset, transition in
|
self.listNode.updateFloatingHeaderOffset = { [weak self] offset, transition in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.currentContentOffset = offset
|
strongSelf.currentContentOffset = offset
|
||||||
strongSelf.updateFloatingHeaderOffset(offset: offset, transition: transition)
|
if strongSelf.animation == nil && !strongSelf.animatingExpansion {
|
||||||
}
|
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
deinit {
|
||||||
@ -1223,7 +1193,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring))
|
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) {
|
@objc private func actionButtonPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer) {
|
||||||
@ -1268,7 +1238,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
if let (layout, navigationHeight) = self.validLayout {
|
if let (layout, navigationHeight) = self.validLayout {
|
||||||
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring))
|
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:
|
default:
|
||||||
break
|
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 {
|
guard let (layout, _) = self.validLayout else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1352,9 +1322,22 @@ public final class VoiceChatController: ViewController {
|
|||||||
let bottomAreaHeight: CGFloat = 268.0
|
let bottomAreaHeight: CGFloat = 268.0
|
||||||
let bottomPanelHeight = bottomAreaHeight + layout.intrinsicInsets.bottom
|
let bottomPanelHeight = bottomAreaHeight + layout.intrinsicInsets.bottom
|
||||||
let listSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset - bottomPanelHeight)
|
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
|
self.floatingHeaderOffset = offset
|
||||||
|
|
||||||
let rawPanelOffset = offset + listTopInset - topPanelHeight
|
let rawPanelOffset = offset + listTopInset - topPanelHeight
|
||||||
@ -1374,7 +1357,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
if !topPanelFrame.equalTo(previousTopPanelFrame) {
|
if !topPanelFrame.equalTo(previousTopPanelFrame) {
|
||||||
self.topPanelNode.frame = topPanelFrame
|
self.topPanelNode.frame = topPanelFrame
|
||||||
let positionDelta = CGPoint(x: 0.0, y: topPanelFrame.minY - previousTopPanelFrame.minY)
|
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
|
self.backgroundNode.frame = backgroundFrame
|
||||||
let backgroundPositionDelta = CGPoint(x: 0.0, y: previousBackgroundFrame.minY - backgroundFrame.minY)
|
let backgroundPositionDelta = CGPoint(x: 0.0, y: previousBackgroundFrame.minY - backgroundFrame.minY)
|
||||||
@ -1387,6 +1370,8 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.rightBorderNode.frame = rightBorderFrame
|
self.rightBorderNode.frame = rightBorderFrame
|
||||||
let rightBorderPositionDelta = CGPoint(x: 0.0, y: previousRightBorderFrame.minY - rightBorderFrame.minY)
|
let rightBorderPositionDelta = CGPoint(x: 0.0, y: previousRightBorderFrame.minY - rightBorderFrame.minY)
|
||||||
transition.animatePositionAdditive(node: self.rightBorderNode, offset: rightBorderPositionDelta)
|
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)
|
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
|
var isFullscreen = false
|
||||||
func updateColors(fullscreen: Bool) {
|
func updateIsFullscreen(_ isFullscreen: Bool) {
|
||||||
guard self.isFullscreen != fullscreen, let (layout, _) = self.validLayout else {
|
guard self.isFullscreen != isFullscreen, let (layout, _) = self.validLayout else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.isFullscreen = fullscreen
|
self.isFullscreen = isFullscreen
|
||||||
|
|
||||||
self.controller?.statusBar.statusBarStyle = fullscreen ? .White : .Ignore
|
|
||||||
|
|
||||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .linear)
|
|
||||||
|
|
||||||
|
self.controller?.statusBar.statusBarStyle = isFullscreen ? .White : .Ignore
|
||||||
|
|
||||||
let topPanelHeight: CGFloat = 63.0
|
let topPanelHeight: CGFloat = 63.0
|
||||||
let topEdgeFrame: CGRect
|
let topEdgeFrame: CGRect
|
||||||
if self.isFullscreen {
|
if isFullscreen {
|
||||||
let offset: CGFloat
|
let offset: CGFloat
|
||||||
if let statusBarHeight = layout.statusBarHeight {
|
if let statusBarHeight = layout.statusBarHeight {
|
||||||
offset = statusBarHeight
|
offset = statusBarHeight
|
||||||
@ -1445,16 +1428,17 @@ public final class VoiceChatController: ViewController {
|
|||||||
} else {
|
} else {
|
||||||
topEdgeFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: topPanelHeight)
|
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.updateFrame(node: self.topPanelEdgeNode, frame: topEdgeFrame)
|
||||||
transition.updateCornerRadius(node: self.topPanelEdgeNode, cornerRadius: fullscreen ? layout.deviceMetrics.screenCornerRadius - 0.5 : 12.0)
|
transition.updateCornerRadius(node: self.topPanelEdgeNode, cornerRadius: isFullscreen ? layout.deviceMetrics.screenCornerRadius - 0.5 : 12.0)
|
||||||
// transition.updateBackgroundColor(node: self.dimNode, color: fullscreen ? fullscreenBackgroundColor : dimColor)
|
transition.updateBackgroundColor(node: self.topPanelBackgroundNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||||
transition.updateBackgroundColor(node: self.topPanelBackgroundNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
transition.updateBackgroundColor(node: self.topPanelEdgeNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||||
transition.updateBackgroundColor(node: self.topPanelEdgeNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
transition.updateBackgroundColor(node: self.backgroundNode, color: isFullscreen ? panelBackgroundColor : secondaryPanelBackgroundColor)
|
||||||
transition.updateBackgroundColor(node: self.backgroundNode, color: fullscreen ? panelBackgroundColor : secondaryPanelBackgroundColor)
|
transition.updateBackgroundColor(node: self.bottomPanelBackgroundNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||||
transition.updateBackgroundColor(node: self.bottomPanelBackgroundNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
transition.updateBackgroundColor(node: self.leftBorderNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||||
transition.updateBackgroundColor(node: self.leftBorderNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
transition.updateBackgroundColor(node: self.rightBorderNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||||
transition.updateBackgroundColor(node: self.rightBorderNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
transition.updateBackgroundColor(node: self.rightBorderNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
||||||
transition.updateBackgroundColor(node: self.rightBorderNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
|
|
||||||
|
|
||||||
if let snapshotView = self.topCornersNode.view.snapshotContentTree() {
|
if let snapshotView = self.topCornersNode.view.snapshotContentTree() {
|
||||||
snapshotView.frame = self.topCornersNode.frame
|
snapshotView.frame = self.topCornersNode.frame
|
||||||
@ -1464,7 +1448,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
snapshotView?.removeFromSuperview()
|
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() {
|
if let snapshotView = self.bottomCornersNode.view.snapshotContentTree() {
|
||||||
snapshotView.frame = self.bottomCornersNode.bounds
|
snapshotView.frame = self.bottomCornersNode.bounds
|
||||||
@ -1474,10 +1458,10 @@ public final class VoiceChatController: ViewController {
|
|||||||
snapshotView?.removeFromSuperview()
|
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.optionsButton.setImage(optionsButtonImage(dark: isFullscreen), animated: transition.isAnimated)
|
||||||
self.closeButton.setImage(closeButtonImage(dark: fullscreen), animated: transition.isAnimated)
|
self.closeButton.setImage(closeButtonImage(dark: isFullscreen), animated: transition.isAnimated)
|
||||||
|
|
||||||
self.updateTitle(transition: transition)
|
self.updateTitle(transition: transition)
|
||||||
}
|
}
|
||||||
@ -1536,6 +1520,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
let soundImage: CallControllerButtonItemNode.Content.Image
|
let soundImage: CallControllerButtonItemNode.Content.Image
|
||||||
var soundAppearance: CallControllerButtonItemNode.Content.Appearance = audioButtonAppearance
|
var soundAppearance: CallControllerButtonItemNode.Content.Appearance = audioButtonAppearance
|
||||||
|
var soundTitle: String = self.presentationData.strings.Call_Speaker
|
||||||
switch audioMode {
|
switch audioMode {
|
||||||
case .none, .builtin:
|
case .none, .builtin:
|
||||||
soundImage = .speaker
|
soundImage = .speaker
|
||||||
@ -1543,7 +1528,8 @@ public final class VoiceChatController: ViewController {
|
|||||||
soundImage = .speaker
|
soundImage = .speaker
|
||||||
soundAppearance = .blurred(isFilled: true)
|
soundAppearance = .blurred(isFilled: true)
|
||||||
case .headphones:
|
case .headphones:
|
||||||
soundImage = .bluetooth
|
soundImage = .headphones
|
||||||
|
soundTitle = self.presentationData.strings.Call_Audio
|
||||||
case let .bluetooth(type):
|
case let .bluetooth(type):
|
||||||
switch type {
|
switch type {
|
||||||
case .generic:
|
case .generic:
|
||||||
@ -1553,10 +1539,11 @@ public final class VoiceChatController: ViewController {
|
|||||||
case .airpodsPro:
|
case .airpodsPro:
|
||||||
soundImage = .airpodsPro
|
soundImage = .airpodsPro
|
||||||
}
|
}
|
||||||
|
soundTitle = self.presentationData.strings.Call_Audio
|
||||||
}
|
}
|
||||||
|
|
||||||
let sideButtonSize = CGSize(width: 60.0, height: 60.0)
|
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)
|
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
|
insets.right = layout.safeInsets.right + sideInset
|
||||||
|
|
||||||
let topPanelHeight: CGFloat = 63.0
|
let topPanelHeight: CGFloat = 63.0
|
||||||
let topEdgeFrame: CGRect
|
if let _ = self.panGestureArguments {
|
||||||
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 {
|
} 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 bottomPanelHeight = bottomAreaHeight + layout.intrinsicInsets.bottom
|
||||||
let listTopInset = layoutTopInset + topPanelHeight
|
let listTopInset = layoutTopInset + topPanelHeight
|
||||||
let listSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset - bottomPanelHeight)
|
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 (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listSize, insets: insets, duration: duration, curve: curve)
|
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listSize, insets: insets, duration: duration, curve: curve)
|
||||||
@ -1695,7 +1700,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
}, completion: { _ in
|
}, completion: { _ in
|
||||||
if self.actionButton.supernode !== self.bottomPanelNode {
|
if self.actionButton.supernode !== self.bottomPanelNode {
|
||||||
self.bottomPanelNode.addSubnode(self.actionButton)
|
self.bottomPanelNode.addSubnode(self.actionButton)
|
||||||
self.containerLayoutUpdated(layout, navigationHeight:navigationHeight, transition: .immediate)
|
self.containerLayoutUpdated(layout, navigationHeight :navigationHeight, transition: .immediate)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.controller?.currentOverlayController?.dismiss()
|
self.controller?.currentOverlayController?.dismiss()
|
||||||
@ -1778,24 +1783,24 @@ public final class VoiceChatController: ViewController {
|
|||||||
let listTopInset = layoutTopInset + 63.0
|
let listTopInset = layoutTopInset + 63.0
|
||||||
let listSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset - bottomPanelHeight)
|
let listSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset - bottomPanelHeight)
|
||||||
|
|
||||||
let previousIsExpanded = self.currentIsExpanded
|
self.topInset = max(0.0, max(listSize.height - itemsHeight, listSize.height - 46.0 - floor(56.0 * 3.5)))
|
||||||
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)))
|
|
||||||
|
|
||||||
let frameTransition: ContainedViewLayoutTransition
|
if !self.isExpanded {
|
||||||
if previousIsExpanded != self.currentIsExpanded {
|
let targetY = listTopInset + (self.topInset ?? listSize.height)
|
||||||
frameTransition = .animated(duration: 0.4, curve: .spring)
|
if self.listNode.frame.minY != targetY && !self.animatingExpansion && self.panGestureArguments == nil {
|
||||||
} else {
|
self.animation = ListViewAnimation(from: self.listNode.frame.minY, to: targetY, duration: 0.4, curve: listViewAnimationCurveEaseInOut, beginAt: CACurrentMediaTime(), update: { [weak self] _, currentValue in
|
||||||
frameTransition = .animated(duration: 0.4, curve: .easeInOut)
|
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))
|
|
||||||
|
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: nil, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in
|
||||||
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
|
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1804,12 +1809,42 @@ public final class VoiceChatController: ViewController {
|
|||||||
strongSelf.controller?.contentsReady.set(true)
|
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.currentCallMembers = callMembers
|
||||||
self.currentSpeakingPeers = speakingPeers
|
self.currentSpeakingPeers = speakingPeers
|
||||||
self.currentInvitedPeers = invitedPeers
|
self.currentInvitedPeers = invitedPeers
|
||||||
@ -1874,7 +1909,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.currentEntries = entries
|
self.currentEntries = entries
|
||||||
|
|
||||||
let presentationData = self.presentationData.withUpdated(theme: self.darkTheme)
|
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)
|
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 {
|
// if let callState = self.callState, case .connected = callState.networkState, let muteState = callState.muteState, !muteState.canUnmute {
|
||||||
// return false
|
// return false
|
||||||
// }
|
// }
|
||||||
if let recognizer = gestureRecognizer as? UIPanGestureRecognizer {
|
// if let recognizer = gestureRecognizer as? UIPanGestureRecognizer {
|
||||||
let location = recognizer.location(in: self.view)
|
// let location = recognizer.location(in: self.view)
|
||||||
if let view = super.hitTest(location, with: nil) {
|
// if let view = super.hitTest(location, with: nil) {
|
||||||
if let gestureRecognizers = view.gestureRecognizers, view != self.view {
|
// if let gestureRecognizers = view.gestureRecognizers, view != self.view {
|
||||||
for gestureRecognizer in gestureRecognizers {
|
// for gestureRecognizer in gestureRecognizers {
|
||||||
if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer, gestureRecognizer.isEnabled {
|
// if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer, gestureRecognizer.isEnabled {
|
||||||
if panGestureRecognizer.state != .began {
|
// if panGestureRecognizer.state != .began {
|
||||||
panGestureRecognizer.isEnabled = false
|
// panGestureRecognizer.isEnabled = false
|
||||||
panGestureRecognizer.isEnabled = true
|
// panGestureRecognizer.isEnabled = true
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return 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) {
|
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||||
switch recognizer.state {
|
switch recognizer.state {
|
||||||
case .began:
|
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:
|
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)
|
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
|
var bounds = self.contentContainer.bounds
|
||||||
bounds.origin.y = -translation.y
|
bounds.origin.y = -translation.y
|
||||||
bounds.origin.y = min(0.0, bounds.origin.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)
|
let offset: CGFloat
|
||||||
|
if let (inset, panOffset) = self.panGestureArguments {
|
||||||
if (bounds.minY < -60.0 || velocity.y > 300.0) {
|
offset = inset + panOffset
|
||||||
self.controller?.dismiss(closing: false)
|
|
||||||
} else {
|
} else {
|
||||||
var bounds = self.contentContainer.bounds
|
offset = 0.0
|
||||||
let previousBounds = bounds
|
}
|
||||||
bounds.origin.y = 0.0
|
|
||||||
self.contentContainer.bounds = bounds
|
let topInset: CGFloat
|
||||||
self.contentContainer.layer.animateBounds(from: previousBounds, to: self.contentContainer.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
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:
|
case .cancelled:
|
||||||
|
self.panGestureArguments = nil
|
||||||
|
|
||||||
let previousBounds = self.contentContainer.bounds
|
let previousBounds = self.contentContainer.bounds
|
||||||
var bounds = self.contentContainer.bounds
|
var bounds = self.contentContainer.bounds
|
||||||
bounds.origin.y = 0.0
|
bounds.origin.y = 0.0
|
||||||
self.contentContainer.bounds = bounds
|
self.contentContainer.bounds = bounds
|
||||||
self.contentContainer.layer.animateBounds(from: previousBounds, to: self.contentContainer.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
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:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1941,7 +2110,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
let result = super.hitTest(point, with: event)
|
let result = super.hitTest(point, with: event)
|
||||||
|
|
||||||
if result === self.topPanelNode.view {
|
if result === self.topPanelNode.view {
|
||||||
return self.listNode.view
|
return self.view
|
||||||
}
|
}
|
||||||
|
|
||||||
if result === self.bottomPanelNode.view {
|
if result === self.bottomPanelNode.view {
|
||||||
@ -2071,7 +2240,11 @@ public final class VoiceChatController: ViewController {
|
|||||||
if let navigationController = self.navigationController as? NavigationController {
|
if let navigationController = self.navigationController as? NavigationController {
|
||||||
let count = navigationController.viewControllers.count
|
let count = navigationController.viewControllers.count
|
||||||
if count == 2 || navigationController.viewControllers[count - 2] is ChatController {
|
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 {
|
} else {
|
||||||
|
@ -53,6 +53,7 @@ public final class VoiceChatOverlayController: ViewController {
|
|||||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
|
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
|
||||||
if hidden {
|
if hidden {
|
||||||
if slide {
|
if slide {
|
||||||
|
actionButton.isHidden = false
|
||||||
transition.updateSublayerTransformOffset(layer: actionButton.layer, offset: CGPoint(x: slideOffset, y: 0.0))
|
transition.updateSublayerTransformOffset(layer: actionButton.layer, offset: CGPoint(x: slideOffset, y: 0.0))
|
||||||
} else {
|
} else {
|
||||||
actionButton.layer.removeAllAnimations()
|
actionButton.layer.removeAllAnimations()
|
||||||
@ -64,10 +65,10 @@ public final class VoiceChatOverlayController: ViewController {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
actionButton.isHidden = false
|
actionButton.isHidden = false
|
||||||
|
actionButton.layer.removeAllAnimations()
|
||||||
if slide {
|
if slide {
|
||||||
transition.updateSublayerTransformOffset(layer: actionButton.layer, offset: CGPoint())
|
transition.updateSublayerTransformOffset(layer: actionButton.layer, offset: CGPoint())
|
||||||
} else {
|
} else {
|
||||||
actionButton.layer.removeAllAnimations()
|
|
||||||
actionButton.layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
actionButton.update(snap: true, animated: !self.isSlidOffscreen)
|
actionButton.update(snap: true, animated: !self.isSlidOffscreen && !self.isButtonHidden)
|
||||||
if self.isSlidOffscreen {
|
if self.isSlidOffscreen {
|
||||||
actionButton.layer.sublayerTransform = CATransform3DMakeTranslation(slideOffset, 0.0, 0.0)
|
actionButton.layer.sublayerTransform = CATransform3DMakeTranslation(slideOffset, 0.0, 0.0)
|
||||||
return
|
return
|
||||||
|
} else if self.isButtonHidden {
|
||||||
|
actionButton.isHidden = true
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let targetPosition = actionButton.position
|
let targetPosition = actionButton.position
|
||||||
@ -125,12 +129,14 @@ public final class VoiceChatOverlayController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var animating = false
|
private var animating = false
|
||||||
|
private var dismissed = false
|
||||||
func animateOut(reclaim: Bool, completion: @escaping (Bool) -> Void) {
|
func animateOut(reclaim: Bool, completion: @escaping (Bool) -> Void) {
|
||||||
guard let actionButton = self.controller?.actionButton, let layout = self.validLayout else {
|
guard let actionButton = self.controller?.actionButton, let layout = self.validLayout else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if reclaim {
|
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)
|
let targetPosition = CGPoint(x: layout.size.width / 2.0, y: layout.size.height - layout.intrinsicInsets.bottom - 268.0 / 2.0)
|
||||||
if self.isSlidOffscreen {
|
if self.isSlidOffscreen {
|
||||||
self.isSlidOffscreen = false
|
self.isSlidOffscreen = false
|
||||||
@ -139,6 +145,13 @@ public final class VoiceChatOverlayController: ViewController {
|
|||||||
actionButton.update(snap: false, animated: false)
|
actionButton.update(snap: false, animated: false)
|
||||||
actionButton.position = CGPoint(x: targetPosition.x, y: 268.0 / 2.0)
|
actionButton.position = CGPoint(x: targetPosition.x, y: 268.0 / 2.0)
|
||||||
completion(true)
|
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 {
|
} else {
|
||||||
self.animating = true
|
self.animating = true
|
||||||
let sourcePoint = actionButton.position
|
let sourcePoint = actionButton.position
|
||||||
@ -193,17 +206,18 @@ public final class VoiceChatOverlayController: ViewController {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var didAnimateIn = false
|
||||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
self.validLayout = layout
|
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 convertedRect = actionButton.view.convert(actionButton.bounds, to: self.view)
|
||||||
let insets = layout.insets(options: [.input])
|
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))
|
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.addSubnode(actionButton)
|
||||||
|
|
||||||
self.animateIn(from: convertedRect)
|
self.animateIn(from: convertedRect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,15 +238,18 @@ public final class VoiceChatOverlayController: ViewController {
|
|||||||
super.init(navigationBarPresentationData: nil)
|
super.init(navigationBarPresentationData: nil)
|
||||||
|
|
||||||
self.statusBar.statusBarStyle = .Ignore
|
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 {
|
if let navigationController = navigationController {
|
||||||
let controllers: Signal<[UIViewController], NoError> = .single([])
|
let controllers: Signal<[UIViewController], NoError> = .single([])
|
||||||
|> then(navigationController.viewControllersSignal)
|
|> then(navigationController.viewControllersSignal)
|
||||||
let overlayControllers: Signal<[UIViewController], NoError> = .single([])
|
let overlayControllers: Signal<[UIViewController], NoError> = .single([])
|
||||||
|> then(navigationController.overlayControllersSignal)
|
|> 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 {
|
if let strongSelf = self {
|
||||||
var hasVoiceChatController = false
|
var hasVoiceChatController = false
|
||||||
var overlayControllersCount = 0
|
var overlayControllersCount = 0
|
||||||
@ -248,19 +265,37 @@ public final class VoiceChatOverlayController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var slide = true
|
||||||
var hidden = true
|
var hidden = true
|
||||||
var animated = true
|
var animated = true
|
||||||
if controllers.count == 1 || controllers.last is ChatController {
|
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 {
|
if overlayControllersCount > 0 {
|
||||||
hidden = true
|
hidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if case .active(.cantSpeak) = state {
|
||||||
|
hidden = true
|
||||||
|
}
|
||||||
if hasVoiceChatController {
|
if hasVoiceChatController {
|
||||||
hidden = false
|
hidden = false
|
||||||
animated = 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)
|
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
|
||||||
case let .groupPhoneCall(_, _, duration):
|
case let .groupPhoneCall(_, _, duration):
|
||||||
let titleString: String
|
|
||||||
if let duration = duration {
|
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 {
|
} else {
|
||||||
titleString = strings.Notification_VoiceChatStarted(message.author?.compactDisplayTitle ?? "").0
|
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):
|
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)
|
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):
|
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.
@ -6227,9 +6227,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let shouldBeActive = combineLatest(self.context.sharedContext.mediaManager.audioSession.isPlaybackActive() |> deliverOnMainQueue, self.chatDisplayNode.historyNode.hasVisiblePlayableItemNodes)
|
let hasActiveCalls: Signal<Bool, NoError>
|
||||||
|> mapToSignal { [weak self] isPlaybackActive, hasVisiblePlayableItemNodes -> Signal<Bool, NoError> in
|
if let callManager = self.context.sharedContext.callManager as? PresentationCallManagerImpl {
|
||||||
if hasVisiblePlayableItemNodes && !isPlaybackActive {
|
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
|
return Signal<Bool, NoError> { [weak self] subscriber in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
@ -7066,7 +7073,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self.saveInterfaceState(includeScrollState: false)
|
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?
|
var voiceChatOverlayController: VoiceChatOverlayController?
|
||||||
for controller in navigationController.globalOverlayControllers {
|
for controller in navigationController.globalOverlayControllers {
|
||||||
if let controller = controller as? VoiceChatOverlayController {
|
if let controller = controller as? VoiceChatOverlayController {
|
||||||
@ -7076,11 +7083,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let controller = voiceChatOverlayController {
|
if let controller = voiceChatOverlayController {
|
||||||
var hidden = false
|
controller.update(hidden: self.isSendButtonVisible, slide: false, animated: true)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7675,11 +7678,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
})
|
})
|
||||||
}, openCamera: { [weak self] cameraView, menuController in
|
}, openCamera: { [weak self] cameraView, menuController in
|
||||||
if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
|
if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
|
||||||
if let callManager = strongSelf.context.sharedContext.callManager as? PresentationCallManagerImpl, callManager.hasActiveGroupCall {
|
var photoOnly = false
|
||||||
return
|
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 let strongSelf = self {
|
||||||
if editMediaOptions != nil {
|
if editMediaOptions != nil {
|
||||||
strongSelf.editMessageMediaWithLegacySignals(signals!)
|
strongSelf.editMessageMediaWithLegacySignals(signals!)
|
||||||
@ -11313,6 +11317,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
//return self.chatDisplayNode.acceptEmbeddedTitlePeekContent(content: content)
|
//return self.chatDisplayNode.acceptEmbeddedTitlePeekContent(content: content)
|
||||||
return false
|
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 {
|
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
||||||
|
@ -1236,7 +1236,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
|||||||
|
|
||||||
let searchLayoutClearButtonSize = CGSize(width: 44.0, height: minimalHeight)
|
let searchLayoutClearButtonSize = CGSize(width: 44.0, height: minimalHeight)
|
||||||
var textFieldInsets = self.textFieldInsets(metrics: metrics)
|
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
|
textFieldInsets.right += additionalSideInsets.right / 3.0
|
||||||
}
|
}
|
||||||
self.actionButtons.micButton.isHidden = additionalSideInsets.right > 0.0
|
self.actionButtons.micButton.isHidden = additionalSideInsets.right > 0.0
|
||||||
|
@ -11,7 +11,7 @@ import ShareController
|
|||||||
import LegacyUI
|
import LegacyUI
|
||||||
import LegacyMediaPickerUI
|
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 presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
|
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
|
||||||
legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)
|
legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)
|
||||||
@ -23,7 +23,7 @@ func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocation: Ch
|
|||||||
|
|
||||||
let controller: TGCameraController
|
let controller: TGCameraController
|
||||||
if let cameraView = cameraView, let previewView = cameraView.previewView() {
|
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
|
controller.inhibitMultipleCapture = editingMedia
|
||||||
} else {
|
} else {
|
||||||
controller = TGCameraController()
|
controller = TGCameraController()
|
||||||
|
@ -109,6 +109,7 @@ public final class NotificationContainerController: ViewController {
|
|||||||
let toAlpha: CGFloat = value ? 0.0 : 1.0
|
let toAlpha: CGFloat = value ? 0.0 : 1.0
|
||||||
self.controllerNode.alpha = toAlpha
|
self.controllerNode.alpha = toAlpha
|
||||||
self.controllerNode.layer.animateAlpha(from: fromAlpha, to: toAlpha, duration: 0.2)
|
self.controllerNode.layer.animateAlpha(from: fromAlpha, to: toAlpha, duration: 0.2)
|
||||||
|
self.controllerNode.isUserInteractionEnabled = !value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user