mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit 'c187f16728100557af7bf1e444259e0a80b3f6e9'
This commit is contained in:
commit
f675add6f9
@ -40,8 +40,9 @@ public final class PeerSelectionControllerParams {
|
|||||||
public let createNewGroup: (() -> Void)?
|
public let createNewGroup: (() -> Void)?
|
||||||
public let pretendPresentedInModal: Bool
|
public let pretendPresentedInModal: Bool
|
||||||
public let multipleSelection: Bool
|
public let multipleSelection: Bool
|
||||||
|
public let forwardedMessagesCount: Int
|
||||||
|
|
||||||
public init(context: AccountContext, filter: ChatListNodePeersFilter = [.onlyWriteable], hasChatListSelector: Bool = true, hasContactSelector: Bool = true, hasGlobalSearch: Bool = true, title: String? = nil, attemptSelection: ((Peer) -> Void)? = nil, createNewGroup: (() -> Void)? = nil, pretendPresentedInModal: Bool = false, multipleSelection: Bool = false) {
|
public init(context: AccountContext, filter: ChatListNodePeersFilter = [.onlyWriteable], hasChatListSelector: Bool = true, hasContactSelector: Bool = true, hasGlobalSearch: Bool = true, title: String? = nil, attemptSelection: ((Peer) -> Void)? = nil, createNewGroup: (() -> Void)? = nil, pretendPresentedInModal: Bool = false, multipleSelection: Bool = false, forwardedMessagesCount: Int = 0) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.filter = filter
|
self.filter = filter
|
||||||
self.hasChatListSelector = hasChatListSelector
|
self.hasChatListSelector = hasChatListSelector
|
||||||
@ -52,6 +53,7 @@ public final class PeerSelectionControllerParams {
|
|||||||
self.createNewGroup = createNewGroup
|
self.createNewGroup = createNewGroup
|
||||||
self.pretendPresentedInModal = pretendPresentedInModal
|
self.pretendPresentedInModal = pretendPresentedInModal
|
||||||
self.multipleSelection = multipleSelection
|
self.multipleSelection = multipleSelection
|
||||||
|
self.forwardedMessagesCount = forwardedMessagesCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1727,32 +1727,28 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
|
|
||||||
let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context, displayBackground: true)
|
let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context, displayBackground: true)
|
||||||
mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo
|
mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo
|
||||||
|
mediaAccessoryPanel.getController = { [weak self] in
|
||||||
|
return self?.navigationController?.topViewController as? ViewController
|
||||||
|
}
|
||||||
|
mediaAccessoryPanel.presentInGlobalOverlay = { [weak self] c in
|
||||||
|
(self?.navigationController?.topViewController as? ViewController)?.presentInGlobalOverlay(c)
|
||||||
|
}
|
||||||
mediaAccessoryPanel.close = { [weak self] in
|
mediaAccessoryPanel.close = { [weak self] in
|
||||||
if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType {
|
if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType {
|
||||||
strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause))
|
strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mediaAccessoryPanel.toggleRate = {
|
mediaAccessoryPanel.setRate = { [weak self] rate in
|
||||||
[weak self] in
|
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> AudioPlaybackRate in
|
let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> AudioPlaybackRate in
|
||||||
let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings) as? MusicPlaybackSettings ?? MusicPlaybackSettings.defaultSettings
|
let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings) as? MusicPlaybackSettings ?? MusicPlaybackSettings.defaultSettings
|
||||||
|
|
||||||
let nextRate: AudioPlaybackRate
|
|
||||||
switch settings.voicePlaybackRate {
|
|
||||||
case .x1:
|
|
||||||
nextRate = .x2
|
|
||||||
case .x2:
|
|
||||||
nextRate = .x1
|
|
||||||
default:
|
|
||||||
nextRate = .x1
|
|
||||||
}
|
|
||||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in
|
||||||
return settings.withUpdatedVoicePlaybackRate(nextRate)
|
return settings.withUpdatedVoicePlaybackRate(rate)
|
||||||
})
|
})
|
||||||
return nextRate
|
return rate
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { baseRate in
|
|> deliverOnMainQueue).start(next: { baseRate in
|
||||||
guard let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType else {
|
guard let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType else {
|
||||||
@ -1771,22 +1767,31 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
})
|
})
|
||||||
|
|
||||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let slowdown = baseRate == .x1
|
let slowdown: Bool?
|
||||||
controller.present(
|
if baseRate == .x1 {
|
||||||
UndoOverlayController(
|
slowdown = true
|
||||||
presentationData: presentationData,
|
} else if baseRate == .x2 {
|
||||||
content: .audioRate(
|
slowdown = false
|
||||||
slowdown: slowdown,
|
} else {
|
||||||
text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp
|
slowdown = nil
|
||||||
|
}
|
||||||
|
if let slowdown = slowdown {
|
||||||
|
controller.present(
|
||||||
|
UndoOverlayController(
|
||||||
|
presentationData: presentationData,
|
||||||
|
content: .audioRate(
|
||||||
|
slowdown: slowdown,
|
||||||
|
text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp
|
||||||
|
),
|
||||||
|
elevatedLayout: false,
|
||||||
|
animateInAsReplacement: hasTooltip,
|
||||||
|
action: { action in
|
||||||
|
return true
|
||||||
|
}
|
||||||
),
|
),
|
||||||
elevatedLayout: false,
|
in: .current
|
||||||
animateInAsReplacement: hasTooltip,
|
)
|
||||||
action: { action in
|
}
|
||||||
return true
|
|
||||||
}
|
|
||||||
),
|
|
||||||
in: .current
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -524,6 +524,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
private let isInteractingPromise = ValuePromise<Bool>(false, ignoreRepeated: true)
|
private let isInteractingPromise = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
private let controlsVisiblePromise = ValuePromise<Bool>(true, ignoreRepeated: true)
|
private let controlsVisiblePromise = ValuePromise<Bool>(true, ignoreRepeated: true)
|
||||||
private let isShowingContextMenuPromise = ValuePromise<Bool>(false, ignoreRepeated: true)
|
private let isShowingContextMenuPromise = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
|
private let hasExpandedCaptionPromise = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
private var hideControlsDisposable: Disposable?
|
private var hideControlsDisposable: Disposable?
|
||||||
|
|
||||||
var playbackCompleted: (() -> Void)?
|
var playbackCompleted: (() -> Void)?
|
||||||
@ -701,12 +702,12 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
self.titleContentView = GalleryTitleView(frame: CGRect())
|
self.titleContentView = GalleryTitleView(frame: CGRect())
|
||||||
self._titleView.set(.single(self.titleContentView))
|
self._titleView.set(.single(self.titleContentView))
|
||||||
|
|
||||||
let shouldHideControlsSignal: Signal<Void, NoError> = combineLatest(self.isPlayingPromise.get(), self.isInteractingPromise.get(), self.controlsVisiblePromise.get(), self.isShowingContextMenuPromise.get())
|
let shouldHideControlsSignal: Signal<Void, NoError> = combineLatest(self.isPlayingPromise.get(), self.isInteractingPromise.get(), self.controlsVisiblePromise.get(), self.isShowingContextMenuPromise.get(), self.hasExpandedCaptionPromise.get())
|
||||||
|> mapToSignal { isPlaying, isIntracting, controlsVisible, isShowingContextMenu -> Signal<Void, NoError> in
|
|> mapToSignal { isPlaying, isInteracting, controlsVisible, isShowingContextMenu, hasExpandedCaptionPromise -> Signal<Void, NoError> in
|
||||||
if isShowingContextMenu {
|
if isShowingContextMenu || hasExpandedCaptionPromise {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
if isPlaying && !isIntracting && controlsVisible {
|
if isPlaying && !isInteracting && controlsVisible {
|
||||||
return .single(Void())
|
return .single(Void())
|
||||||
|> delay(4.0, queue: Queue.mainQueue())
|
|> delay(4.0, queue: Queue.mainQueue())
|
||||||
} else {
|
} else {
|
||||||
|
@ -76,6 +76,6 @@ typedef enum {
|
|||||||
|
|
||||||
+ (UIInterfaceOrientation)_interfaceOrientationForDeviceOrientation:(UIDeviceOrientation)orientation;
|
+ (UIInterfaceOrientation)_interfaceOrientationForDeviceOrientation:(UIDeviceOrientation)orientation;
|
||||||
|
|
||||||
+ (bool)useLegacyCamera;
|
+ (UIImage *)startImage;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#import <LegacyComponents/TGMenuSheetView.h>
|
#import <LegacyComponents/TGMenuSheetView.h>
|
||||||
#import "TGAttachmentMenuCell.h"
|
#import "TGAttachmentMenuCell.h"
|
||||||
|
#import "TGCameraController.h"
|
||||||
|
|
||||||
#import <LegacyComponents/PGCamera.h>
|
#import <LegacyComponents/PGCamera.h>
|
||||||
#import <LegacyComponents/TGCameraPreviewView.h>
|
#import <LegacyComponents/TGCameraPreviewView.h>
|
||||||
@ -46,6 +47,8 @@
|
|||||||
_camera = camera;
|
_camera = camera;
|
||||||
|
|
||||||
_previewView = [[TGCameraPreviewView alloc] initWithFrame:CGRectMake(0, 0, 84.0f, 84.0f)];
|
_previewView = [[TGCameraPreviewView alloc] initWithFrame:CGRectMake(0, 0, 84.0f, 84.0f)];
|
||||||
|
[_previewView fadeInAnimated:false];
|
||||||
|
[_previewView beginTransitionWithSnapshotImage:[TGCameraController startImage] animated:false];
|
||||||
[_wrapperView addSubview:_previewView];
|
[_wrapperView addSubview:_previewView];
|
||||||
[camera attachPreviewView:_previewView];
|
[camera attachPreviewView:_previewView];
|
||||||
|
|
||||||
|
@ -2251,6 +2251,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
|
|||||||
_dismissing = true;
|
_dismissing = true;
|
||||||
self.view.userInteractionEnabled = false;
|
self.view.userInteractionEnabled = false;
|
||||||
|
|
||||||
|
|
||||||
_focusControl.active = false;
|
_focusControl.active = false;
|
||||||
_rectangleView.hidden = true;
|
_rectangleView.hidden = true;
|
||||||
|
|
||||||
@ -2267,6 +2268,15 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
|
|||||||
referenceFrame = self.beginTransitionOut();
|
referenceFrame = self.beginTransitionOut();
|
||||||
|
|
||||||
__weak TGCameraController *weakSelf = self;
|
__weak TGCameraController *weakSelf = self;
|
||||||
|
[_camera captureNextFrameCompletion:^(UIImage *frameImage) {
|
||||||
|
CGFloat minSize = MIN(frameImage.size.width, frameImage.size.height);
|
||||||
|
CGFloat maxSize = MAX(frameImage.size.width, frameImage.size.height);
|
||||||
|
|
||||||
|
UIImage *image = TGPhotoEditorCrop(frameImage, nil, UIImageOrientationUp, 0.0f, CGRectMake((maxSize - minSize) / 2.0f, 0.0f, minSize, minSize), false, CGSizeMake(240.0f, 240.0f), frameImage.size, true);
|
||||||
|
|
||||||
|
UIImage *startImage = TGSecretBlurredAttachmentImage(image, image.size, NULL, false, 0);
|
||||||
|
[TGCameraController saveStartImage:startImage];
|
||||||
|
}];
|
||||||
if (_standalone)
|
if (_standalone)
|
||||||
{
|
{
|
||||||
[self simpleTransitionOutWithVelocity:velocity completion:^
|
[self simpleTransitionOutWithVelocity:velocity completion:^
|
||||||
@ -2757,11 +2767,6 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (bool)useLegacyCamera
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (NSArray *)resultSignalsForSelectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext currentItem:(id<TGMediaSelectableItem>)currentItem storeAssets:(bool)storeAssets saveEditedPhotos:(bool)saveEditedPhotos descriptionGenerator:(id (^)(id, NSString *, NSArray *, NSString *))descriptionGenerator
|
+ (NSArray *)resultSignalsForSelectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext currentItem:(id<TGMediaSelectableItem>)currentItem storeAssets:(bool)storeAssets saveEditedPhotos:(bool)saveEditedPhotos descriptionGenerator:(id (^)(id, NSString *, NSArray *, NSString *))descriptionGenerator
|
||||||
{
|
{
|
||||||
NSMutableArray *signals = [[NSMutableArray alloc] init];
|
NSMutableArray *signals = [[NSMutableArray alloc] init];
|
||||||
@ -3144,4 +3149,24 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Start Image
|
||||||
|
|
||||||
|
static UIImage *startImage = nil;
|
||||||
|
|
||||||
|
+ (UIImage *)startImage
|
||||||
|
{
|
||||||
|
if (startImage == nil)
|
||||||
|
startImage = TGComponentsImageNamed (@"VideoMessagePlaceholder.jpg");
|
||||||
|
|
||||||
|
return startImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)saveStartImage:(UIImage *)image
|
||||||
|
{
|
||||||
|
if (image == nil)
|
||||||
|
return;
|
||||||
|
|
||||||
|
startImage = image;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -129,10 +129,15 @@
|
|||||||
if (strongSelf == nil)
|
if (strongSelf == nil)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (resume)
|
if (resume) {
|
||||||
[strongSelf endResetTransitionAnimated:true];
|
[strongSelf endResetTransitionAnimated:true];
|
||||||
else
|
} else {
|
||||||
[strongSelf fadeInAnimated:true];
|
if (strongSelf->_snapshotView != nil) {
|
||||||
|
[strongSelf endTransitionAnimated:true];
|
||||||
|
} else {
|
||||||
|
[strongSelf fadeInAnimated:true];
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
camera.captureStopped = ^(bool pause)
|
camera.captureStopped = ^(bool pause)
|
||||||
@ -262,6 +267,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (bool)hasTransitionSnapshot {
|
||||||
|
return _snapshotView != nil;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)beginResetTransitionAnimated:(bool)animated
|
- (void)beginResetTransitionAnimated:(bool)animated
|
||||||
{
|
{
|
||||||
if (iosMajorVersion() < 7)
|
if (iosMajorVersion() < 7)
|
||||||
@ -319,7 +328,12 @@
|
|||||||
|
|
||||||
if (_snapshotView != nil)
|
if (_snapshotView != nil)
|
||||||
{
|
{
|
||||||
CGSize size = TGScaleToFill(_snapshotView.frame.size, _wrapperView.frame.size);
|
CGSize imageSize = _snapshotView.frame.size;
|
||||||
|
if ([_snapshotView isKindOfClass:[UIImageView class]]) {
|
||||||
|
imageSize = ((UIImageView *)_snapshotView).image.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGSize size = TGScaleToFill(imageSize, _wrapperView.frame.size);
|
||||||
_snapshotView.frame = CGRectMake(floor((self.frame.size.width - size.width) / 2.0f), floor((self.frame.size.height - size.height) / 2.0f), size.width, size.height);
|
_snapshotView.frame = CGRectMake(floor((self.frame.size.width - size.width) / 2.0f), floor((self.frame.size.height - size.height) / 2.0f), size.width, size.height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
_hasSearchButton = hasSearchButton;
|
_hasSearchButton = hasSearchButton;
|
||||||
_hasDeleteButton = hasDeleteButton;
|
_hasDeleteButton = hasDeleteButton;
|
||||||
_hasViewButton = hasViewButton;
|
_hasViewButton = hasViewButton;
|
||||||
_personalPhoto = ![TGCameraController useLegacyCamera] ? personalPhoto : false;
|
_personalPhoto = personalPhoto;
|
||||||
_isVideo = isVideo;
|
_isVideo = isVideo;
|
||||||
_signup = signup;
|
_signup = signup;
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ private final class LegacyComponentsGlobalsProviderImpl: NSObject, LegacyCompone
|
|||||||
let convertedType: ManagedAudioSessionType
|
let convertedType: ManagedAudioSessionType
|
||||||
switch type {
|
switch type {
|
||||||
case TGAudioSessionTypePlayAndRecord, TGAudioSessionTypePlayAndRecordHeadphones:
|
case TGAudioSessionTypePlayAndRecord, TGAudioSessionTypePlayAndRecordHeadphones:
|
||||||
convertedType = .recordWithOthers
|
convertedType = .record(speaker: false)
|
||||||
default:
|
default:
|
||||||
convertedType = .play
|
convertedType = .play
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ swift_library(
|
|||||||
"//submodules/AccountContext:AccountContext",
|
"//submodules/AccountContext:AccountContext",
|
||||||
"//submodules/SegmentedControlNode:SegmentedControlNode",
|
"//submodules/SegmentedControlNode:SegmentedControlNode",
|
||||||
"//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode",
|
"//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode",
|
||||||
|
"//submodules/ShimmerEffect:ShimmerEffect",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -10,6 +10,7 @@ import TelegramStringFormatting
|
|||||||
import SelectablePeerNode
|
import SelectablePeerNode
|
||||||
import PeerPresenceStatusManager
|
import PeerPresenceStatusManager
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
import ShimmerEffect
|
||||||
|
|
||||||
final class ShareControllerInteraction {
|
final class ShareControllerInteraction {
|
||||||
var foundPeers: [RenderedPeer] = []
|
var foundPeers: [RenderedPeer] = []
|
||||||
@ -89,14 +90,14 @@ final class ShareControllerPeerGridItem: GridItem {
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
let peer: RenderedPeer
|
let peer: RenderedPeer?
|
||||||
let presence: PeerPresence?
|
let presence: PeerPresence?
|
||||||
let controllerInteraction: ShareControllerInteraction
|
let controllerInteraction: ShareControllerInteraction
|
||||||
let search: Bool
|
let search: Bool
|
||||||
|
|
||||||
let section: GridSection?
|
let section: GridSection?
|
||||||
|
|
||||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer, presence: PeerPresence?, controllerInteraction: ShareControllerInteraction, sectionTitle: String? = nil, search: Bool = false) {
|
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer?, presence: PeerPresence?, controllerInteraction: ShareControllerInteraction, sectionTitle: String? = nil, search: Bool = false) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
@ -130,12 +131,15 @@ final class ShareControllerPeerGridItem: GridItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class ShareControllerPeerGridItemNode: GridItemNode {
|
final class ShareControllerPeerGridItemNode: GridItemNode {
|
||||||
private var currentState: (AccountContext, PresentationTheme, PresentationStrings, RenderedPeer, Bool, PeerPresence?)?
|
private var currentState: (AccountContext, PresentationTheme, PresentationStrings, RenderedPeer?, Bool, PeerPresence?)?
|
||||||
private let peerNode: SelectablePeerNode
|
private let peerNode: SelectablePeerNode
|
||||||
private var presenceManager: PeerPresenceStatusManager?
|
private var presenceManager: PeerPresenceStatusManager?
|
||||||
|
|
||||||
var controllerInteraction: ShareControllerInteraction?
|
var controllerInteraction: ShareControllerInteraction?
|
||||||
|
|
||||||
|
private var placeholderNode: ShimmerEffectNode?
|
||||||
|
private var absoluteLocation: (CGRect, CGSize)?
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
self.peerNode = SelectablePeerNode()
|
self.peerNode = SelectablePeerNode()
|
||||||
|
|
||||||
@ -143,7 +147,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
self.peerNode.toggleSelection = { [weak self] in
|
self.peerNode.toggleSelection = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if let (_, _, _, peer, search, _) = strongSelf.currentState {
|
if let (_, _, _, maybePeer, search, _) = strongSelf.currentState, let peer = maybePeer {
|
||||||
if let _ = peer.peers[peer.peerId] {
|
if let _ = peer.peers[peer.peerId] {
|
||||||
strongSelf.controllerInteraction?.togglePeer(peer, search)
|
strongSelf.controllerInteraction?.togglePeer(peer, search)
|
||||||
}
|
}
|
||||||
@ -159,13 +163,21 @@ final class ShareControllerPeerGridItemNode: GridItemNode {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer, presence: PeerPresence?, search: Bool, synchronousLoad: Bool, force: Bool) {
|
override func updateAbsoluteRect(_ absoluteRect: CGRect, within containerSize: CGSize) {
|
||||||
|
var rect = absoluteRect
|
||||||
|
self.absoluteLocation = (rect, containerSize)
|
||||||
|
if let shimmerNode = self.placeholderNode {
|
||||||
|
shimmerNode.updateAbsoluteRect(rect, within: containerSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setup(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer?, presence: PeerPresence?, search: Bool, synchronousLoad: Bool, force: Bool) {
|
||||||
if force || self.currentState == nil || self.currentState!.0 !== context || self.currentState!.3 != peer || !arePeerPresencesEqual(self.currentState!.5, presence) {
|
if force || self.currentState == nil || self.currentState!.0 !== context || self.currentState!.3 != peer || !arePeerPresencesEqual(self.currentState!.5, presence) {
|
||||||
let itemTheme = SelectablePeerNodeTheme(textColor: theme.actionSheet.primaryTextColor, secretTextColor: theme.chatList.secretTitleColor, selectedTextColor: theme.actionSheet.controlAccentColor, checkBackgroundColor: theme.actionSheet.opaqueItemBackgroundColor, checkFillColor: theme.actionSheet.controlAccentColor, checkColor: theme.actionSheet.checkContentColor, avatarPlaceholderColor: theme.list.mediaPlaceholderColor)
|
let itemTheme = SelectablePeerNodeTheme(textColor: theme.actionSheet.primaryTextColor, secretTextColor: theme.chatList.secretTitleColor, selectedTextColor: theme.actionSheet.controlAccentColor, checkBackgroundColor: theme.actionSheet.opaqueItemBackgroundColor, checkFillColor: theme.actionSheet.controlAccentColor, checkColor: theme.actionSheet.checkContentColor, avatarPlaceholderColor: theme.list.mediaPlaceholderColor)
|
||||||
|
|
||||||
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||||
var online = false
|
var online = false
|
||||||
if let peer = peer.peer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != context.account.peerId {
|
if let peer = peer?.peer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != context.account.peerId {
|
||||||
let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: timestamp)
|
let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: timestamp)
|
||||||
if case .online = relativeStatus {
|
if case .online = relativeStatus {
|
||||||
online = true
|
online = true
|
||||||
@ -173,7 +185,39 @@ final class ShareControllerPeerGridItemNode: GridItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.peerNode.theme = itemTheme
|
self.peerNode.theme = itemTheme
|
||||||
self.peerNode.setup(context: context, theme: theme, strings: strings, peer: EngineRenderedPeer(peer), online: online, synchronousLoad: synchronousLoad)
|
if let peer = peer {
|
||||||
|
self.peerNode.setup(context: context, theme: theme, strings: strings, peer: EngineRenderedPeer(peer), online: online, synchronousLoad: synchronousLoad)
|
||||||
|
if let shimmerNode = self.placeholderNode {
|
||||||
|
self.placeholderNode = nil
|
||||||
|
shimmerNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let shimmerNode: ShimmerEffectNode
|
||||||
|
if let current = self.placeholderNode {
|
||||||
|
shimmerNode = current
|
||||||
|
} else {
|
||||||
|
shimmerNode = ShimmerEffectNode()
|
||||||
|
self.placeholderNode = shimmerNode
|
||||||
|
self.addSubnode(shimmerNode)
|
||||||
|
}
|
||||||
|
shimmerNode.frame = self.bounds
|
||||||
|
if let (rect, size) = self.absoluteLocation {
|
||||||
|
shimmerNode.updateAbsoluteRect(rect, within: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
var shapes: [ShimmerEffectNode.Shape] = []
|
||||||
|
|
||||||
|
let titleLineWidth: CGFloat = 56.0
|
||||||
|
let lineDiameter: CGFloat = 10.0
|
||||||
|
|
||||||
|
let iconFrame = CGRect(x: 13.0, y: 4.0, width: 60.0, height: 60.0)
|
||||||
|
shapes.append(.circle(iconFrame))
|
||||||
|
|
||||||
|
let titleFrame = CGRect(x: 15.0, y: 70.0, width: 56.0, height: 10.0)
|
||||||
|
shapes.append(.roundedRectLine(startPoint: CGPoint(x: titleFrame.minX, y: titleFrame.minY + floor((titleFrame.height - lineDiameter) / 2.0)), width: titleLineWidth, diameter: lineDiameter))
|
||||||
|
|
||||||
|
shimmerNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, horizontal: true, size: self.bounds.size)
|
||||||
|
}
|
||||||
self.currentState = (context, theme, strings, peer, search, presence)
|
self.currentState = (context, theme, strings, peer, search, presence)
|
||||||
self.setNeedsLayout()
|
self.setNeedsLayout()
|
||||||
if let presence = presence as? TelegramUserPresence {
|
if let presence = presence as? TelegramUserPresence {
|
||||||
@ -185,7 +229,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
func updateSelection(animated: Bool) {
|
func updateSelection(animated: Bool) {
|
||||||
var selected = false
|
var selected = false
|
||||||
if let controllerInteraction = self.controllerInteraction, let (_, _, _, peer, _, _) = self.currentState {
|
if let controllerInteraction = self.controllerInteraction, let (_, _, _, maybePeer, _, _) = self.currentState, let peer = maybePeer {
|
||||||
selected = controllerInteraction.selectedPeerIds.contains(peer.peerId)
|
selected = controllerInteraction.selectedPeerIds.contains(peer.peerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,5 +241,21 @@ final class ShareControllerPeerGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
let bounds = self.bounds
|
let bounds = self.bounds
|
||||||
self.peerNode.frame = bounds
|
self.peerNode.frame = bounds
|
||||||
|
self.placeholderNode?.frame = bounds
|
||||||
|
|
||||||
|
if let (_, theme, _, _, _, _) = self.currentState, let shimmerNode = self.placeholderNode {
|
||||||
|
var shapes: [ShimmerEffectNode.Shape] = []
|
||||||
|
|
||||||
|
let titleLineWidth: CGFloat = 56.0
|
||||||
|
let lineDiameter: CGFloat = 10.0
|
||||||
|
|
||||||
|
let iconFrame = CGRect(x: (bounds.width - 60.0) / 2.0, y: 4.0, width: 60.0, height: 60.0)
|
||||||
|
shapes.append(.circle(iconFrame))
|
||||||
|
|
||||||
|
let titleFrame = CGRect(x: (bounds.width - titleLineWidth) / 2.0, y: 70.0, width: titleLineWidth, height: 10.0)
|
||||||
|
shapes.append(.roundedRectLine(startPoint: CGPoint(x: titleFrame.minX, y: titleFrame.minY + floor((titleFrame.height - lineDiameter) / 2.0)), width: titleLineWidth, diameter: lineDiameter))
|
||||||
|
|
||||||
|
shimmerNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, horizontal: true, size: self.bounds.size)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,13 +110,17 @@ private enum ShareSearchRecentEntry: Comparable, Identifiable {
|
|||||||
|
|
||||||
private struct ShareSearchPeerEntry: Comparable, Identifiable {
|
private struct ShareSearchPeerEntry: Comparable, Identifiable {
|
||||||
let index: Int32
|
let index: Int32
|
||||||
let peer: RenderedPeer
|
let peer: RenderedPeer?
|
||||||
let presence: PeerPresence?
|
let presence: PeerPresence?
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
|
|
||||||
var stableId: Int64 {
|
var stableId: Int64 {
|
||||||
return self.peer.peerId.toInt64()
|
if let peer = self.peer {
|
||||||
|
return peer.peerId.toInt64()
|
||||||
|
} else {
|
||||||
|
return Int64(index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: ShareSearchPeerEntry, rhs: ShareSearchPeerEntry) -> Bool {
|
static func ==(lhs: ShareSearchPeerEntry, rhs: ShareSearchPeerEntry) -> Bool {
|
||||||
@ -134,7 +138,7 @@ private struct ShareSearchPeerEntry: Comparable, Identifiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func item(context: AccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem {
|
func item(context: AccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem {
|
||||||
return ShareControllerPeerGridItem(context: context, theme: self.theme, strings: self.strings, peer: peer, presence: self.presence, controllerInteraction: interfaceInteraction, search: true)
|
return ShareControllerPeerGridItem(context: context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, controllerInteraction: interfaceInteraction, search: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,10 +250,13 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode {
|
|||||||
if !query.isEmpty {
|
if !query.isEmpty {
|
||||||
let accountPeer = context.account.postbox.loadedPeerWithId(context.account.peerId) |> take(1)
|
let accountPeer = context.account.postbox.loadedPeerWithId(context.account.peerId) |> take(1)
|
||||||
let foundLocalPeers = context.account.postbox.searchPeers(query: query.lowercased())
|
let foundLocalPeers = context.account.postbox.searchPeers(query: query.lowercased())
|
||||||
let foundRemotePeers: Signal<([FoundPeer], [FoundPeer]), NoError> = .single(([], []))
|
let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError> = .single(([], [], true))
|
||||||
|> then(
|
|> then(
|
||||||
context.engine.peers.searchPeers(query: query)
|
context.engine.peers.searchPeers(query: query)
|
||||||
|> delay(0.2, queue: Queue.concurrentDefaultQueue())
|
|> delay(0.2, queue: Queue.concurrentDefaultQueue())
|
||||||
|
|> map { a, b -> ([FoundPeer], [FoundPeer], Bool) in
|
||||||
|
return (a, b, false)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return combineLatest(accountPeer, foundLocalPeers, foundRemotePeers)
|
return combineLatest(accountPeer, foundLocalPeers, foundRemotePeers)
|
||||||
@ -278,21 +285,28 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for foundPeer in foundRemotePeers.0 {
|
if foundRemotePeers.2 {
|
||||||
let peer = foundPeer.peer
|
for _ in 0 ..< 4 {
|
||||||
if !existingPeerIds.contains(peer.id) && canSendMessagesToPeer(peer) {
|
entries.append(ShareSearchPeerEntry(index: index, peer: nil, presence: nil, theme: theme, strings: strings))
|
||||||
existingPeerIds.insert(peer.id)
|
|
||||||
entries.append(ShareSearchPeerEntry(index: index, peer: RenderedPeer(peer: foundPeer.peer), presence: nil, theme: theme, strings: strings))
|
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
for foundPeer in foundRemotePeers.0 {
|
||||||
for foundPeer in foundRemotePeers.1 {
|
let peer = foundPeer.peer
|
||||||
let peer = foundPeer.peer
|
if !existingPeerIds.contains(peer.id) && canSendMessagesToPeer(peer) {
|
||||||
if !existingPeerIds.contains(peer.id) && canSendMessagesToPeer(peer) {
|
existingPeerIds.insert(peer.id)
|
||||||
existingPeerIds.insert(peer.id)
|
entries.append(ShareSearchPeerEntry(index: index, peer: RenderedPeer(peer: foundPeer.peer), presence: nil, theme: theme, strings: strings))
|
||||||
entries.append(ShareSearchPeerEntry(index: index, peer: RenderedPeer(peer: peer), presence: nil, theme: theme, strings: strings))
|
index += 1
|
||||||
index += 1
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for foundPeer in foundRemotePeers.1 {
|
||||||
|
let peer = foundPeer.peer
|
||||||
|
if !existingPeerIds.contains(peer.id) && canSendMessagesToPeer(peer) {
|
||||||
|
existingPeerIds.insert(peer.id)
|
||||||
|
entries.append(ShareSearchPeerEntry(index: index, peer: RenderedPeer(peer: peer), presence: nil, theme: theme, strings: strings))
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,7 +450,7 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode {
|
|||||||
var scrollToItem: GridNodeScrollToItem?
|
var scrollToItem: GridNodeScrollToItem?
|
||||||
if !self.contentGridNode.isHidden, let ensurePeerVisibleOnLayout = self.ensurePeerVisibleOnLayout {
|
if !self.contentGridNode.isHidden, let ensurePeerVisibleOnLayout = self.ensurePeerVisibleOnLayout {
|
||||||
self.ensurePeerVisibleOnLayout = nil
|
self.ensurePeerVisibleOnLayout = nil
|
||||||
if let index = self.entries.firstIndex(where: { $0.peer.peerId == ensurePeerVisibleOnLayout }) {
|
if let index = self.entries.firstIndex(where: { $0.peer?.peerId == ensurePeerVisibleOnLayout }) {
|
||||||
scrollToItem = GridNodeScrollToItem(index: index, position: .visible, transition: transition, directionHint: .up, adjustForSection: false)
|
scrollToItem = GridNodeScrollToItem(index: index, position: .visible, transition: transition, directionHint: .up, adjustForSection: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ private func nativeCategoryForType(_ type: ManagedAudioSessionType, headphones:
|
|||||||
return .ambient
|
return .ambient
|
||||||
case .play:
|
case .play:
|
||||||
return .playback
|
return .playback
|
||||||
case .record, .recordWithOthers, .voiceCall, .videoCall:
|
case .record, .recordWithOthers, .voiceCall, .videoCall:
|
||||||
return .playAndRecord
|
return .playAndRecord
|
||||||
case .playWithPossiblePortOverride:
|
case .playWithPossiblePortOverride:
|
||||||
if headphones {
|
if headphones {
|
||||||
@ -568,7 +568,7 @@ public final class ManagedAudioSession {
|
|||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastIsRecordWithOthers = self.holders.last?.audioSessionType == .recordWithOthers
|
let lastIsRecordWithOthers = false // self.holders.last?.audioSessionType == .recordWithOthers
|
||||||
if !deactivating {
|
if !deactivating {
|
||||||
if let activeIndex = activeIndex {
|
if let activeIndex = activeIndex {
|
||||||
var deactivate = false
|
var deactivate = false
|
||||||
@ -745,9 +745,9 @@ public final class ManagedAudioSession {
|
|||||||
case .videoCall:
|
case .videoCall:
|
||||||
mode = .videoChat
|
mode = .videoChat
|
||||||
options.insert(.mixWithOthers)
|
options.insert(.mixWithOthers)
|
||||||
case .recordWithOthers:
|
// case .recordWithOthers:
|
||||||
mode = .videoRecording
|
// mode = .videoRecording
|
||||||
options.insert(.mixWithOthers)
|
// options.insert(.mixWithOthers)
|
||||||
default:
|
default:
|
||||||
mode = .default
|
mode = .default
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ public final class MediaNavigationAccessoryContainerNode: ASDisplayNode, UIGestu
|
|||||||
|
|
||||||
self.backgroundNode = ASDisplayNode()
|
self.backgroundNode = ASDisplayNode()
|
||||||
self.separatorNode = ASDisplayNode()
|
self.separatorNode = ASDisplayNode()
|
||||||
self.headerNode = MediaNavigationAccessoryHeaderNode(presentationData: self.presentationData)
|
self.headerNode = MediaNavigationAccessoryHeaderNode(context: context)
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import UniversalMediaPlayer
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
import TelegramStringFormatting
|
import TelegramStringFormatting
|
||||||
import ManagedAnimationNode
|
import ManagedAnimationNode
|
||||||
|
import ContextUI
|
||||||
|
|
||||||
private let titleFont = Font.regular(12.0)
|
private let titleFont = Font.regular(12.0)
|
||||||
private let subtitleFont = Font.regular(10.0)
|
private let subtitleFont = Font.regular(10.0)
|
||||||
@ -130,6 +131,7 @@ private func generateMaskImage(color: UIColor) -> UIImage? {
|
|||||||
public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollViewDelegate {
|
public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollViewDelegate {
|
||||||
public static let minimizedHeight: CGFloat = 37.0
|
public static let minimizedHeight: CGFloat = 37.0
|
||||||
|
|
||||||
|
private let context: AccountContext
|
||||||
private var theme: PresentationTheme
|
private var theme: PresentationTheme
|
||||||
private var strings: PresentationStrings
|
private var strings: PresentationStrings
|
||||||
private var dateTimeFormat: PresentationDateTimeFormat
|
private var dateTimeFormat: PresentationDateTimeFormat
|
||||||
@ -148,7 +150,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
|
|||||||
private let closeButton: HighlightableButtonNode
|
private let closeButton: HighlightableButtonNode
|
||||||
private let actionButton: HighlightTrackingButtonNode
|
private let actionButton: HighlightTrackingButtonNode
|
||||||
private let playPauseIconNode: PlayPauseIconNode
|
private let playPauseIconNode: PlayPauseIconNode
|
||||||
private let rateButton: HighlightableButtonNode
|
private let rateButton: RateButton
|
||||||
private let accessibilityAreaNode: AccessibilityAreaNode
|
private let accessibilityAreaNode: AccessibilityAreaNode
|
||||||
|
|
||||||
private let scrubbingNode: MediaPlayerScrubbingNode
|
private let scrubbingNode: MediaPlayerScrubbingNode
|
||||||
@ -167,24 +169,31 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
|
|||||||
|
|
||||||
public var tapAction: (() -> Void)?
|
public var tapAction: (() -> Void)?
|
||||||
public var close: (() -> Void)?
|
public var close: (() -> Void)?
|
||||||
public var toggleRate: (() -> Void)?
|
public var setRate: ((AudioPlaybackRate) -> Void)?
|
||||||
public var togglePlayPause: (() -> Void)?
|
public var togglePlayPause: (() -> Void)?
|
||||||
public var playPrevious: (() -> Void)?
|
public var playPrevious: (() -> Void)?
|
||||||
public var playNext: (() -> Void)?
|
public var playNext: (() -> Void)?
|
||||||
|
|
||||||
|
public var getController: (() -> ViewController?)?
|
||||||
|
public var presentInGlobalOverlay: ((ViewController) -> Void)?
|
||||||
|
|
||||||
public var playbackBaseRate: AudioPlaybackRate? = nil {
|
public var playbackBaseRate: AudioPlaybackRate? = nil {
|
||||||
didSet {
|
didSet {
|
||||||
guard self.playbackBaseRate != oldValue, let playbackBaseRate = self.playbackBaseRate else {
|
guard self.playbackBaseRate != oldValue, let playbackBaseRate = self.playbackBaseRate else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch playbackBaseRate {
|
switch playbackBaseRate {
|
||||||
|
case .x0_5:
|
||||||
|
self.rateButton.setContent(.image(optionsRateImage(rate: "0.5x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor)))
|
||||||
case .x1:
|
case .x1:
|
||||||
self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateInactiveIcon(self.theme), for: [])
|
self.rateButton.setContent(.image(optionsRateImage(rate: "1x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor)))
|
||||||
self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate
|
self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate
|
||||||
self.rateButton.accessibilityValue = self.strings.VoiceOver_Media_PlaybackRateNormal
|
self.rateButton.accessibilityValue = self.strings.VoiceOver_Media_PlaybackRateNormal
|
||||||
self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange
|
self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange
|
||||||
|
case .x1_5:
|
||||||
|
self.rateButton.setContent(.image(optionsRateImage(rate: "1.5x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor)))
|
||||||
case .x2:
|
case .x2:
|
||||||
self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateActiveIcon(self.theme), for: [])
|
self.rateButton.setContent(.image(optionsRateImage(rate: "2x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor)))
|
||||||
self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate
|
self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate
|
||||||
self.rateButton.accessibilityValue = self.strings.VoiceOver_Media_PlaybackRateFast
|
self.rateButton.accessibilityValue = self.strings.VoiceOver_Media_PlaybackRateFast
|
||||||
self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange
|
self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange
|
||||||
@ -208,7 +217,9 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(presentationData: PresentationData) {
|
public init(context: AccountContext) {
|
||||||
|
self.context = context
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
self.theme = presentationData.theme
|
self.theme = presentationData.theme
|
||||||
self.strings = presentationData.strings
|
self.strings = presentationData.strings
|
||||||
self.dateTimeFormat = presentationData.dateTimeFormat
|
self.dateTimeFormat = presentationData.dateTimeFormat
|
||||||
@ -236,7 +247,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
|
|||||||
self.closeButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 2.0)
|
self.closeButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 2.0)
|
||||||
self.closeButton.displaysAsynchronously = false
|
self.closeButton.displaysAsynchronously = false
|
||||||
|
|
||||||
self.rateButton = HighlightableButtonNode()
|
self.rateButton = RateButton()
|
||||||
|
|
||||||
self.rateButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -4.0, bottom: -8.0, right: -4.0)
|
self.rateButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -4.0, bottom: -8.0, right: -4.0)
|
||||||
self.rateButton.displaysAsynchronously = false
|
self.rateButton.displaysAsynchronously = false
|
||||||
@ -265,9 +276,6 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
|
|||||||
self.scrollNode.addSubnode(self.previousItemNode)
|
self.scrollNode.addSubnode(self.previousItemNode)
|
||||||
self.scrollNode.addSubnode(self.nextItemNode)
|
self.scrollNode.addSubnode(self.nextItemNode)
|
||||||
|
|
||||||
//self.addSubnode(self.leftMaskNode)
|
|
||||||
//self.addSubnode(self.rightMaskNode)
|
|
||||||
|
|
||||||
self.addSubnode(self.closeButton)
|
self.addSubnode(self.closeButton)
|
||||||
self.addSubnode(self.rateButton)
|
self.addSubnode(self.rateButton)
|
||||||
self.addSubnode(self.accessibilityAreaNode)
|
self.addSubnode(self.accessibilityAreaNode)
|
||||||
@ -276,9 +284,13 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
|
|||||||
self.addSubnode(self.actionButton)
|
self.addSubnode(self.actionButton)
|
||||||
|
|
||||||
self.closeButton.addTarget(self, action: #selector(self.closeButtonPressed), forControlEvents: .touchUpInside)
|
self.closeButton.addTarget(self, action: #selector(self.closeButtonPressed), forControlEvents: .touchUpInside)
|
||||||
self.rateButton.addTarget(self, action: #selector(self.rateButtonPressed), forControlEvents: .touchUpInside)
|
|
||||||
self.actionButton.addTarget(self, action: #selector(self.actionButtonPressed), forControlEvents: .touchUpInside)
|
self.actionButton.addTarget(self, action: #selector(self.actionButtonPressed), forControlEvents: .touchUpInside)
|
||||||
|
|
||||||
|
self.rateButton.addTarget(self, action: #selector(self.rateButtonPressed), forControlEvents: .touchUpInside)
|
||||||
|
self.rateButton.contextAction = { [weak self] sourceNode, gesture in
|
||||||
|
self?.openRateMenu(sourceNode: sourceNode, gesture: gesture)
|
||||||
|
}
|
||||||
|
|
||||||
self.addSubnode(self.scrubbingNode)
|
self.addSubnode(self.scrubbingNode)
|
||||||
|
|
||||||
self.addSubnode(self.separatorNode)
|
self.addSubnode(self.separatorNode)
|
||||||
@ -300,13 +312,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let status = status {
|
if let status = status {
|
||||||
let baseRate: AudioPlaybackRate
|
strongSelf.playbackBaseRate = AudioPlaybackRate(status.baseRate)
|
||||||
if status.baseRate.isEqual(to: 1.0) {
|
|
||||||
baseRate = .x1
|
|
||||||
} else {
|
|
||||||
baseRate = .x2
|
|
||||||
}
|
|
||||||
strongSelf.playbackBaseRate = baseRate
|
|
||||||
} else {
|
} else {
|
||||||
strongSelf.playbackBaseRate = .x1
|
strongSelf.playbackBaseRate = .x1
|
||||||
}
|
}
|
||||||
@ -365,10 +371,14 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
|
|||||||
|
|
||||||
if let playbackBaseRate = self.playbackBaseRate {
|
if let playbackBaseRate = self.playbackBaseRate {
|
||||||
switch playbackBaseRate {
|
switch playbackBaseRate {
|
||||||
|
case .x0_5:
|
||||||
|
self.rateButton.setContent(.image(optionsRateImage(rate: "0.5x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor)))
|
||||||
case .x1:
|
case .x1:
|
||||||
self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateInactiveIcon(self.theme), for: [])
|
self.rateButton.setContent(.image(optionsRateImage(rate: "1x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor)))
|
||||||
|
case .x1_5:
|
||||||
|
self.rateButton.setContent(.image(optionsRateImage(rate: "1.5x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor)))
|
||||||
case .x2:
|
case .x2:
|
||||||
self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateActiveIcon(self.theme), for: [])
|
self.rateButton.setContent(.image(optionsRateImage(rate: "2x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor)))
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -475,8 +485,8 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
|
|||||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0))
|
let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0))
|
||||||
transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 44.0 - rightInset, y: 0.0), size: CGSize(width: 44.0, height: minHeight)))
|
transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 44.0 - rightInset, y: 0.0), size: CGSize(width: 44.0, height: minHeight)))
|
||||||
let rateButtonSize = CGSize(width: 24.0, height: minHeight)
|
let rateButtonSize = CGSize(width: 30.0, height: minHeight)
|
||||||
transition.updateFrame(node: self.rateButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 18.0 - closeButtonSize.width - 17.0 - rateButtonSize.width - rightInset, y: 0.0), size: rateButtonSize))
|
transition.updateFrame(node: self.rateButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 20.0 - closeButtonSize.width - rateButtonSize.width - rightInset, y: -4.0), size: rateButtonSize))
|
||||||
transition.updateFrame(node: self.playPauseIconNode, frame: CGRect(origin: CGPoint(x: 6.0, y: 4.0 + UIScreenPixel), size: CGSize(width: 28.0, height: 28.0)))
|
transition.updateFrame(node: self.playPauseIconNode, frame: CGRect(origin: CGPoint(x: 6.0, y: 4.0 + UIScreenPixel), size: CGSize(width: 28.0, height: 28.0)))
|
||||||
transition.updateFrame(node: self.actionButton, frame: CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: 40.0, height: 37.0)))
|
transition.updateFrame(node: self.actionButton, frame: CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: 40.0, height: 37.0)))
|
||||||
transition.updateFrame(node: self.scrubbingNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 37.0 - 2.0), size: CGSize(width: size.width, height: 2.0)))
|
transition.updateFrame(node: self.scrubbingNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 37.0 - 2.0), size: CGSize(width: size.width, height: 2.0)))
|
||||||
@ -491,7 +501,48 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc public func rateButtonPressed() {
|
@objc public func rateButtonPressed() {
|
||||||
self.toggleRate?()
|
self.rateButton.contextAction?(self.rateButton.containerNode, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func speedList(strings: PresentationStrings) -> [(String, String, AudioPlaybackRate)] {
|
||||||
|
let speedList: [(String, String, AudioPlaybackRate)] = [
|
||||||
|
("0.5x", "0.5x", .x0_5),
|
||||||
|
(strings.PlaybackSpeed_Normal, "1x", .x1),
|
||||||
|
("1.5x", "1.5x", .x1_5),
|
||||||
|
("2x", "2x", .x2)
|
||||||
|
]
|
||||||
|
return speedList
|
||||||
|
}
|
||||||
|
|
||||||
|
private func contextMenuSpeedItems() -> Signal<[ContextMenuItem], NoError> {
|
||||||
|
var items: [ContextMenuItem] = []
|
||||||
|
|
||||||
|
for (text, _, rate) in self.speedList(strings: self.strings) {
|
||||||
|
let isSelected = self.playbackBaseRate == rate
|
||||||
|
items.append(.action(ContextMenuActionItem(text: text, icon: { theme in
|
||||||
|
if isSelected {
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}, action: { [weak self] _, f in
|
||||||
|
f(.default)
|
||||||
|
|
||||||
|
self?.setRate?(rate)
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
return .single(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func openRateMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) {
|
||||||
|
guard let controller = self.getController?() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let items: Signal<[ContextMenuItem], NoError> = self.contextMenuSpeedItems()
|
||||||
|
let contextController = ContextController(account: self.context.account, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode)), items: items, reactionItems: [], gesture: gesture)
|
||||||
|
self.presentInGlobalOverlay?(contextController)
|
||||||
|
// controller.presentInGlobalOverlay(contextController)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public func actionButtonPressed() {
|
@objc public func actionButtonPressed() {
|
||||||
@ -554,3 +605,159 @@ private final class PlayPauseIconNode: ManagedAnimationNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func optionsRateImage(rate: String, isLarge: Bool, color: UIColor = .white) -> UIImage? {
|
||||||
|
return generateImage(isLarge ? CGSize(width: 30.0, height: 30.0) : CGSize(width: 24.0, height: 24.0), rotatedContext: { size, context in
|
||||||
|
UIGraphicsPushContext(context)
|
||||||
|
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
if let image = generateTintedImage(image: UIImage(bundleImageName: isLarge ? "Chat/Context Menu/Playspeed30" : "Chat/Context Menu/Playspeed24"), color: color) {
|
||||||
|
image.draw(at: CGPoint(x: 0.0, y: 0.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
let string = NSMutableAttributedString(string: rate, font: Font.with(size: 11.0, design: .round, weight: .semibold), textColor: color)
|
||||||
|
|
||||||
|
var offset = CGPoint(x: 1.0, y: 0.0)
|
||||||
|
if rate.count >= 3 {
|
||||||
|
if rate == "0.5x" {
|
||||||
|
string.addAttribute(.kern, value: -0.8 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string))
|
||||||
|
offset.x += -0.5
|
||||||
|
} else {
|
||||||
|
string.addAttribute(.kern, value: -0.5 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string))
|
||||||
|
offset.x += -0.3
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
offset.x += -0.3
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isLarge {
|
||||||
|
offset.x *= 0.5
|
||||||
|
offset.y *= 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
let boundingRect = string.boundingRect(with: size, options: [], context: nil)
|
||||||
|
string.draw(at: CGPoint(x: offset.x + floor((size.width - boundingRect.width) / 2.0), y: offset.y + floor((size.height - boundingRect.height) / 2.0)))
|
||||||
|
|
||||||
|
UIGraphicsPopContext()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class RateButton: HighlightableButtonNode {
|
||||||
|
enum Content {
|
||||||
|
case image(UIImage?)
|
||||||
|
}
|
||||||
|
|
||||||
|
let referenceNode: ContextReferenceContentNode
|
||||||
|
let containerNode: ContextControllerSourceNode
|
||||||
|
private let iconNode: ASImageNode
|
||||||
|
|
||||||
|
var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
|
|
||||||
|
private let wide: Bool
|
||||||
|
|
||||||
|
init(wide: Bool = false) {
|
||||||
|
self.wide = wide
|
||||||
|
|
||||||
|
self.referenceNode = ContextReferenceContentNode()
|
||||||
|
self.containerNode = ContextControllerSourceNode()
|
||||||
|
self.containerNode.animateScale = false
|
||||||
|
self.iconNode = ASImageNode()
|
||||||
|
self.iconNode.displaysAsynchronously = false
|
||||||
|
self.iconNode.displayWithoutProcessing = true
|
||||||
|
self.iconNode.contentMode = .scaleToFill
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.containerNode.addSubnode(self.referenceNode)
|
||||||
|
self.referenceNode.addSubnode(self.iconNode)
|
||||||
|
self.addSubnode(self.containerNode)
|
||||||
|
|
||||||
|
self.containerNode.shouldBegin = { [weak self] location in
|
||||||
|
guard let strongSelf = self, let _ = strongSelf.contextAction else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
self.containerNode.activated = { [weak self] gesture, _ in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.contextAction?(strongSelf.containerNode, gesture)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 26.0, height: 44.0))
|
||||||
|
self.referenceNode.frame = self.containerNode.bounds
|
||||||
|
|
||||||
|
if let image = self.iconNode.image {
|
||||||
|
self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.hitTestSlop = UIEdgeInsets(top: 0.0, left: -4.0, bottom: 0.0, right: -4.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var content: Content?
|
||||||
|
func setContent(_ content: Content, animated: Bool = false) {
|
||||||
|
if animated {
|
||||||
|
if let snapshotView = self.referenceNode.view.snapshotContentTree() {
|
||||||
|
snapshotView.frame = self.referenceNode.frame
|
||||||
|
self.view.addSubview(snapshotView)
|
||||||
|
|
||||||
|
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||||
|
snapshotView?.removeFromSuperview()
|
||||||
|
})
|
||||||
|
snapshotView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.3, removeOnCompletion: false)
|
||||||
|
|
||||||
|
self.iconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
|
self.iconNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch content {
|
||||||
|
case let .image(image):
|
||||||
|
if let image = image {
|
||||||
|
self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.iconNode.image = image
|
||||||
|
self.iconNode.isHidden = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.content = content
|
||||||
|
switch content {
|
||||||
|
case let .image(image):
|
||||||
|
if let image = image {
|
||||||
|
self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.iconNode.image = image
|
||||||
|
self.iconNode.isHidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didLoad() {
|
||||||
|
super.didLoad()
|
||||||
|
self.view.isOpaque = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||||
|
return CGSize(width: wide ? 32.0 : 22.0, height: 44.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func onLayout() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
|
||||||
|
private let controller: ViewController
|
||||||
|
private let sourceNode: ContextReferenceContentNode
|
||||||
|
|
||||||
|
init(controller: ViewController, sourceNode: ContextReferenceContentNode) {
|
||||||
|
self.controller = controller
|
||||||
|
self.sourceNode = sourceNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func transitionInfo() -> ContextControllerReferenceViewInfo? {
|
||||||
|
return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,17 +4,21 @@ import Display
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
import TelegramUIPreferences
|
||||||
|
|
||||||
public final class MediaNavigationAccessoryPanel: ASDisplayNode {
|
public final class MediaNavigationAccessoryPanel: ASDisplayNode {
|
||||||
public let containerNode: MediaNavigationAccessoryContainerNode
|
public let containerNode: MediaNavigationAccessoryContainerNode
|
||||||
|
|
||||||
public var close: (() -> Void)?
|
public var close: (() -> Void)?
|
||||||
public var toggleRate: (() -> Void)?
|
public var setRate: ((AudioPlaybackRate) -> Void)?
|
||||||
public var togglePlayPause: (() -> Void)?
|
public var togglePlayPause: (() -> Void)?
|
||||||
public var tapAction: (() -> Void)?
|
public var tapAction: (() -> Void)?
|
||||||
public var playPrevious: (() -> Void)?
|
public var playPrevious: (() -> Void)?
|
||||||
public var playNext: (() -> Void)?
|
public var playNext: (() -> Void)?
|
||||||
|
|
||||||
|
public var getController: (() -> ViewController?)?
|
||||||
|
public var presentInGlobalOverlay: ((ViewController) -> Void)?
|
||||||
|
|
||||||
public init(context: AccountContext, displayBackground: Bool = false) {
|
public init(context: AccountContext, displayBackground: Bool = false) {
|
||||||
self.containerNode = MediaNavigationAccessoryContainerNode(context: context, displayBackground: displayBackground)
|
self.containerNode = MediaNavigationAccessoryContainerNode(context: context, displayBackground: displayBackground)
|
||||||
|
|
||||||
@ -27,8 +31,8 @@ public final class MediaNavigationAccessoryPanel: ASDisplayNode {
|
|||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.containerNode.headerNode.toggleRate = { [weak self] in
|
self.containerNode.headerNode.setRate = { [weak self] rate in
|
||||||
self?.toggleRate?()
|
self?.setRate?(rate)
|
||||||
}
|
}
|
||||||
self.containerNode.headerNode.togglePlayPause = { [weak self] in
|
self.containerNode.headerNode.togglePlayPause = { [weak self] in
|
||||||
if let strongSelf = self, let togglePlayPause = strongSelf.togglePlayPause {
|
if let strongSelf = self, let togglePlayPause = strongSelf.togglePlayPause {
|
||||||
@ -50,6 +54,20 @@ public final class MediaNavigationAccessoryPanel: ASDisplayNode {
|
|||||||
playNext()
|
playNext()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.containerNode.headerNode.getController = { [weak self] in
|
||||||
|
if let strongSelf = self, let getController = strongSelf.getController {
|
||||||
|
return getController()
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.containerNode.headerNode.presentInGlobalOverlay = { [weak self] c in
|
||||||
|
if let strongSelf = self, let presentInGlobalOverlay = strongSelf.presentInGlobalOverlay {
|
||||||
|
presentInGlobalOverlay(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
|
@ -645,32 +645,28 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
|||||||
|
|
||||||
let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context)
|
let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context)
|
||||||
mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo
|
mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo
|
||||||
|
mediaAccessoryPanel.getController = { [weak self] in
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
mediaAccessoryPanel.presentInGlobalOverlay = { [weak self] c in
|
||||||
|
self?.presentInGlobalOverlay(c)
|
||||||
|
}
|
||||||
mediaAccessoryPanel.close = { [weak self] in
|
mediaAccessoryPanel.close = { [weak self] in
|
||||||
if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType {
|
if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType {
|
||||||
strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause))
|
strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mediaAccessoryPanel.toggleRate = {
|
mediaAccessoryPanel.setRate = { [weak self] rate in
|
||||||
[weak self] in
|
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> AudioPlaybackRate in
|
let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> AudioPlaybackRate in
|
||||||
let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings) as? MusicPlaybackSettings ?? MusicPlaybackSettings.defaultSettings
|
let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings) as? MusicPlaybackSettings ?? MusicPlaybackSettings.defaultSettings
|
||||||
|
|
||||||
let nextRate: AudioPlaybackRate
|
|
||||||
switch settings.voicePlaybackRate {
|
|
||||||
case .x1:
|
|
||||||
nextRate = .x2
|
|
||||||
case .x2:
|
|
||||||
nextRate = .x1
|
|
||||||
default:
|
|
||||||
nextRate = .x1
|
|
||||||
}
|
|
||||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in
|
||||||
return settings.withUpdatedVoicePlaybackRate(nextRate)
|
return settings.withUpdatedVoicePlaybackRate(rate)
|
||||||
})
|
})
|
||||||
return nextRate
|
return rate
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { baseRate in
|
|> deliverOnMainQueue).start(next: { baseRate in
|
||||||
guard let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType else {
|
guard let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType else {
|
||||||
@ -688,22 +684,31 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
|||||||
})
|
})
|
||||||
|
|
||||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let slowdown = baseRate == .x1
|
let slowdown: Bool?
|
||||||
strongSelf.present(
|
if baseRate == .x1 {
|
||||||
UndoOverlayController(
|
slowdown = true
|
||||||
presentationData: presentationData,
|
} else if baseRate == .x2 {
|
||||||
content: .audioRate(
|
slowdown = false
|
||||||
slowdown: slowdown,
|
} else {
|
||||||
text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp
|
slowdown = nil
|
||||||
|
}
|
||||||
|
if let slowdown = slowdown {
|
||||||
|
strongSelf.present(
|
||||||
|
UndoOverlayController(
|
||||||
|
presentationData: presentationData,
|
||||||
|
content: .audioRate(
|
||||||
|
slowdown: slowdown,
|
||||||
|
text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp
|
||||||
|
),
|
||||||
|
elevatedLayout: false,
|
||||||
|
animateInAsReplacement: hasTooltip,
|
||||||
|
action: { action in
|
||||||
|
return true
|
||||||
|
}
|
||||||
),
|
),
|
||||||
elevatedLayout: false,
|
in: .current
|
||||||
animateInAsReplacement: hasTooltip,
|
)
|
||||||
action: { action in
|
}
|
||||||
return true
|
|
||||||
}
|
|
||||||
),
|
|
||||||
in: .current
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
mediaAccessoryPanel.togglePlayPause = { [weak self] in
|
mediaAccessoryPanel.togglePlayPause = { [weak self] in
|
||||||
|
@ -4800,7 +4800,6 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
let speakingPeersUpdated = self.currentSpeakingPeers != speakingPeers
|
let speakingPeersUpdated = self.currentSpeakingPeers != speakingPeers
|
||||||
self.currentCallMembers = callMembers
|
self.currentCallMembers = callMembers
|
||||||
self.currentSpeakingPeers = speakingPeers
|
|
||||||
self.currentInvitedPeers = invitedPeers
|
self.currentInvitedPeers = invitedPeers
|
||||||
|
|
||||||
var entries: [ListEntry] = []
|
var entries: [ListEntry] = []
|
||||||
@ -5105,9 +5104,10 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.updateMainVideo(waitForFullSize: true, entries: fullscreenEntries, force: true)
|
self.updateMainVideo(waitForFullSize: true, entries: fullscreenEntries, force: true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateRequestedVideoChannels()
|
self.updateRequestedVideoChannels()
|
||||||
|
|
||||||
|
self.currentSpeakingPeers = speakingPeers
|
||||||
self.peerIdToEndpointId = peerIdToEndpointId
|
self.peerIdToEndpointId = peerIdToEndpointId
|
||||||
|
|
||||||
var updateLayout = false
|
var updateLayout = false
|
||||||
|
@ -11010,7 +11010,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
var attemptSelectionImpl: ((Peer) -> Void)?
|
var attemptSelectionImpl: ((Peer) -> Void)?
|
||||||
let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: filter, attemptSelection: { peer in
|
let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: filter, attemptSelection: { peer in
|
||||||
attemptSelectionImpl?(peer)
|
attemptSelectionImpl?(peer)
|
||||||
}, multipleSelection: true))
|
}, multipleSelection: true, forwardedMessagesCount: messages.count))
|
||||||
let context = self.context
|
let context = self.context
|
||||||
attemptSelectionImpl = { [weak controller] peer in
|
attemptSelectionImpl = { [weak controller] peer in
|
||||||
guard let controller = controller else {
|
guard let controller = controller else {
|
||||||
|
@ -1080,7 +1080,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
let viaBotNode = viaBotApply()
|
let viaBotNode = viaBotApply()
|
||||||
if strongSelf.viaBotNode == nil {
|
if strongSelf.viaBotNode == nil {
|
||||||
strongSelf.viaBotNode = viaBotNode
|
strongSelf.viaBotNode = viaBotNode
|
||||||
strongSelf.addSubnode(viaBotNode)
|
strongSelf.contextSourceNode.contentNode.addSubnode(viaBotNode)
|
||||||
}
|
}
|
||||||
let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 15.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 14.0)), y: 8.0), size: viaBotLayout.size)
|
let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 15.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 14.0)), y: 8.0), size: viaBotLayout.size)
|
||||||
viaBotNode.frame = viaBotFrame
|
viaBotNode.frame = viaBotFrame
|
||||||
@ -1161,7 +1161,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
} else {
|
} else {
|
||||||
let forwardBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper))
|
let forwardBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper))
|
||||||
strongSelf.forwardBackgroundNode = forwardBackgroundNode
|
strongSelf.forwardBackgroundNode = forwardBackgroundNode
|
||||||
strongSelf.addSubnode(forwardBackgroundNode)
|
strongSelf.contextSourceNode.contentNode.addSubnode(forwardBackgroundNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1169,7 +1169,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
let forwardInfoNode = forwardInfoApply(forwardInfoSize.width)
|
let forwardInfoNode = forwardInfoApply(forwardInfoSize.width)
|
||||||
if strongSelf.forwardInfoNode == nil {
|
if strongSelf.forwardInfoNode == nil {
|
||||||
strongSelf.forwardInfoNode = forwardInfoNode
|
strongSelf.forwardInfoNode = forwardInfoNode
|
||||||
strongSelf.addSubnode(forwardInfoNode)
|
strongSelf.contextSourceNode.contentNode.addSubnode(forwardInfoNode)
|
||||||
}
|
}
|
||||||
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - forwardInfoSize.width - layoutConstants.bubble.edgeInset - 12.0)), y: 8.0), size: forwardInfoSize)
|
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - forwardInfoSize.width - layoutConstants.bubble.edgeInset - 12.0)), y: 8.0), size: forwardInfoSize)
|
||||||
forwardInfoNode.frame = forwardInfoFrame
|
forwardInfoNode.frame = forwardInfoFrame
|
||||||
|
@ -43,11 +43,11 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var forwardInfoNode: ChatMessageForwardInfoNode?
|
private var forwardInfoNode: ChatMessageForwardInfoNode?
|
||||||
private var forwardBackgroundNode: ASImageNode?
|
private var forwardBackgroundNode: NavigationBackgroundNode?
|
||||||
|
|
||||||
private var viaBotNode: TextNode?
|
private var viaBotNode: TextNode?
|
||||||
private var replyInfoNode: ChatMessageReplyInfoNode?
|
private var replyInfoNode: ChatMessageReplyInfoNode?
|
||||||
private var replyBackgroundNode: ASImageNode?
|
private var replyBackgroundNode: NavigationBackgroundNode?
|
||||||
|
|
||||||
private var actionButtonsNode: ChatMessageActionButtonsNode?
|
private var actionButtonsNode: ChatMessageActionButtonsNode?
|
||||||
|
|
||||||
@ -389,7 +389,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
|
|
||||||
var viaBotApply: (TextNodeLayout, () -> TextNode)?
|
var viaBotApply: (TextNodeLayout, () -> TextNode)?
|
||||||
var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)?
|
var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)?
|
||||||
var updatedReplyBackgroundNode: ASImageNode?
|
var updatedReplyBackgroundNode: NavigationBackgroundNode?
|
||||||
var replyBackgroundImage: UIImage?
|
var replyBackgroundImage: UIImage?
|
||||||
var replyMarkup: ReplyMarkupMessageAttribute?
|
var replyMarkup: ReplyMarkupMessageAttribute?
|
||||||
|
|
||||||
@ -432,6 +432,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
let botString = addAttributesToStringWithRanges(item.presentationData.strings.Conversation_MessageViaUser("@\(inlineBotNameString)")._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
|
let botString = addAttributesToStringWithRanges(item.presentationData.strings.Conversation_MessageViaUser("@\(inlineBotNameString)")._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
|
||||||
|
|
||||||
viaBotApply = viaBotLayout(TextNodeLayoutArguments(attributedString: botString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0, availableWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
viaBotApply = viaBotLayout(TextNodeLayoutArguments(attributedString: botString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0, availableWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
|
ignoreForward = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,11 +466,10 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
if let currentReplyBackgroundNode = currentReplyBackgroundNode {
|
if let currentReplyBackgroundNode = currentReplyBackgroundNode {
|
||||||
updatedReplyBackgroundNode = currentReplyBackgroundNode
|
updatedReplyBackgroundNode = currentReplyBackgroundNode
|
||||||
} else {
|
} else {
|
||||||
updatedReplyBackgroundNode = ASImageNode()
|
updatedReplyBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper))
|
||||||
}
|
}
|
||||||
|
|
||||||
let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners)
|
updatedReplyBackgroundNode?.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate)
|
||||||
replyBackgroundImage = graphics.chatFreeformContentAdditionalInfoBackgroundImage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var updatedShareButtonNode: ChatMessageShareButton?
|
var updatedShareButtonNode: ChatMessageShareButton?
|
||||||
@ -487,7 +488,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
var forwardAuthorSignature: String?
|
var forwardAuthorSignature: String?
|
||||||
|
|
||||||
var forwardInfoSizeApply: (CGSize, (CGFloat) -> ChatMessageForwardInfoNode)?
|
var forwardInfoSizeApply: (CGSize, (CGFloat) -> ChatMessageForwardInfoNode)?
|
||||||
var updatedForwardBackgroundNode: ASImageNode?
|
var updatedForwardBackgroundNode: NavigationBackgroundNode?
|
||||||
var forwardBackgroundImage: UIImage?
|
var forwardBackgroundImage: UIImage?
|
||||||
|
|
||||||
if !ignoreForward, let forwardInfo = item.message.forwardInfo {
|
if !ignoreForward, let forwardInfo = item.message.forwardInfo {
|
||||||
@ -517,11 +518,10 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
if let currentForwardBackgroundNode = currentForwardBackgroundNode {
|
if let currentForwardBackgroundNode = currentForwardBackgroundNode {
|
||||||
updatedForwardBackgroundNode = currentForwardBackgroundNode
|
updatedForwardBackgroundNode = currentForwardBackgroundNode
|
||||||
} else {
|
} else {
|
||||||
updatedForwardBackgroundNode = ASImageNode()
|
updatedForwardBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper))
|
||||||
}
|
}
|
||||||
|
|
||||||
let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners)
|
updatedForwardBackgroundNode?.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate)
|
||||||
forwardBackgroundImage = graphics.chatServiceBubbleFillImage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var maxContentWidth = normalDisplaySize.width
|
var maxContentWidth = normalDisplaySize.width
|
||||||
@ -613,10 +613,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
if let updatedReplyBackgroundNode = updatedReplyBackgroundNode {
|
if let updatedReplyBackgroundNode = updatedReplyBackgroundNode {
|
||||||
if strongSelf.replyBackgroundNode == nil {
|
if strongSelf.replyBackgroundNode == nil {
|
||||||
strongSelf.replyBackgroundNode = updatedReplyBackgroundNode
|
strongSelf.replyBackgroundNode = updatedReplyBackgroundNode
|
||||||
strongSelf.addSubnode(updatedReplyBackgroundNode)
|
strongSelf.contextSourceNode.contentNode.addSubnode(updatedReplyBackgroundNode)
|
||||||
updatedReplyBackgroundNode.image = replyBackgroundImage
|
|
||||||
} else {
|
|
||||||
strongSelf.replyBackgroundNode?.image = replyBackgroundImage
|
|
||||||
}
|
}
|
||||||
} else if let replyBackgroundNode = strongSelf.replyBackgroundNode {
|
} else if let replyBackgroundNode = strongSelf.replyBackgroundNode {
|
||||||
replyBackgroundNode.removeFromSupernode()
|
replyBackgroundNode.removeFromSupernode()
|
||||||
@ -627,11 +624,13 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
let viaBotNode = viaBotApply()
|
let viaBotNode = viaBotApply()
|
||||||
if strongSelf.viaBotNode == nil {
|
if strongSelf.viaBotNode == nil {
|
||||||
strongSelf.viaBotNode = viaBotNode
|
strongSelf.viaBotNode = viaBotNode
|
||||||
strongSelf.addSubnode(viaBotNode)
|
strongSelf.contextSourceNode.contentNode.addSubnode(viaBotNode)
|
||||||
}
|
}
|
||||||
let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0), size: viaBotLayout.size)
|
let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0), size: viaBotLayout.size)
|
||||||
viaBotNode.frame = viaBotFrame
|
viaBotNode.frame = viaBotFrame
|
||||||
strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: viaBotFrame.minX - 4.0, y: viaBotFrame.minY - 2.0), size: CGSize(width: viaBotFrame.size.width + 8.0, height: viaBotFrame.size.height + 5.0))
|
let replyBackgroundFrame = CGRect(origin: CGPoint(x: viaBotFrame.minX - 4.0, y: viaBotFrame.minY - 2.0), size: CGSize(width: viaBotFrame.size.width + 8.0, height: viaBotFrame.size.height + 5.0))
|
||||||
|
strongSelf.replyBackgroundNode?.frame = replyBackgroundFrame
|
||||||
|
strongSelf.replyBackgroundNode?.update(size: replyBackgroundFrame.size, cornerRadius: 8.0, transition: .immediate)
|
||||||
} else if let viaBotNode = strongSelf.viaBotNode {
|
} else if let viaBotNode = strongSelf.viaBotNode {
|
||||||
viaBotNode.removeFromSupernode()
|
viaBotNode.removeFromSupernode()
|
||||||
strongSelf.viaBotNode = nil
|
strongSelf.viaBotNode = nil
|
||||||
@ -641,7 +640,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
let replyInfoNode = replyInfoApply()
|
let replyInfoNode = replyInfoApply()
|
||||||
if strongSelf.replyInfoNode == nil {
|
if strongSelf.replyInfoNode == nil {
|
||||||
strongSelf.replyInfoNode = replyInfoNode
|
strongSelf.replyInfoNode = replyInfoNode
|
||||||
strongSelf.addSubnode(replyInfoNode)
|
strongSelf.contextSourceNode.contentNode.addSubnode(replyInfoNode)
|
||||||
}
|
}
|
||||||
var viaBotSize = CGSize()
|
var viaBotSize = CGSize()
|
||||||
if let viaBotNode = strongSelf.viaBotNode {
|
if let viaBotNode = strongSelf.viaBotNode {
|
||||||
@ -654,7 +653,9 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
replyInfoNode.frame = replyInfoFrame
|
replyInfoNode.frame = replyInfoFrame
|
||||||
strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - viaBotSize.height - 2.0), size: CGSize(width: max(replyInfoFrame.size.width, viaBotSize.width) + 8.0, height: replyInfoFrame.size.height + viaBotSize.height + 5.0))
|
let replyBackgroundFrame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - viaBotSize.height - 2.0), size: CGSize(width: max(replyInfoFrame.size.width, viaBotSize.width) + 8.0, height: replyInfoFrame.size.height + viaBotSize.height + 5.0))
|
||||||
|
strongSelf.replyBackgroundNode?.frame = replyBackgroundFrame
|
||||||
|
strongSelf.replyBackgroundNode?.update(size: replyBackgroundFrame.size, cornerRadius: 8.0, transition: .immediate)
|
||||||
} else if let replyInfoNode = strongSelf.replyInfoNode {
|
} else if let replyInfoNode = strongSelf.replyInfoNode {
|
||||||
replyInfoNode.removeFromSupernode()
|
replyInfoNode.removeFromSupernode()
|
||||||
strongSelf.replyInfoNode = nil
|
strongSelf.replyInfoNode = nil
|
||||||
@ -694,8 +695,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
if let updatedForwardBackgroundNode = updatedForwardBackgroundNode {
|
if let updatedForwardBackgroundNode = updatedForwardBackgroundNode {
|
||||||
if strongSelf.forwardBackgroundNode == nil {
|
if strongSelf.forwardBackgroundNode == nil {
|
||||||
strongSelf.forwardBackgroundNode = updatedForwardBackgroundNode
|
strongSelf.forwardBackgroundNode = updatedForwardBackgroundNode
|
||||||
strongSelf.addSubnode(updatedForwardBackgroundNode)
|
strongSelf.contextSourceNode.contentNode.addSubnode(updatedForwardBackgroundNode)
|
||||||
updatedForwardBackgroundNode.image = forwardBackgroundImage
|
|
||||||
}
|
}
|
||||||
} else if let forwardBackgroundNode = strongSelf.forwardBackgroundNode {
|
} else if let forwardBackgroundNode = strongSelf.forwardBackgroundNode {
|
||||||
forwardBackgroundNode.removeFromSupernode()
|
forwardBackgroundNode.removeFromSupernode()
|
||||||
@ -706,7 +706,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
let forwardInfoNode = forwardInfoApply(forwardInfoSize.width)
|
let forwardInfoNode = forwardInfoApply(forwardInfoSize.width)
|
||||||
if strongSelf.forwardInfoNode == nil {
|
if strongSelf.forwardInfoNode == nil {
|
||||||
strongSelf.forwardInfoNode = forwardInfoNode
|
strongSelf.forwardInfoNode = forwardInfoNode
|
||||||
strongSelf.addSubnode(forwardInfoNode)
|
strongSelf.contextSourceNode.contentNode.addSubnode(forwardInfoNode)
|
||||||
forwardInfoNode.openPsa = { [weak strongSelf] type, sourceNode in
|
forwardInfoNode.openPsa = { [weak strongSelf] type, sourceNode in
|
||||||
guard let strongSelf = strongSelf, let item = strongSelf.item else {
|
guard let strongSelf = strongSelf, let item = strongSelf.item else {
|
||||||
return
|
return
|
||||||
@ -716,7 +716,9 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
}
|
}
|
||||||
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - forwardInfoSize.width - layoutConstants.bubble.edgeInset - 12.0)), y: 8.0), size: forwardInfoSize)
|
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - forwardInfoSize.width - layoutConstants.bubble.edgeInset - 12.0)), y: 8.0), size: forwardInfoSize)
|
||||||
forwardInfoNode.frame = forwardInfoFrame
|
forwardInfoNode.frame = forwardInfoFrame
|
||||||
strongSelf.forwardBackgroundNode?.frame = CGRect(origin: CGPoint(x: forwardInfoFrame.minX - 6.0, y: forwardInfoFrame.minY - 2.0), size: CGSize(width: forwardInfoFrame.size.width + 10.0, height: forwardInfoFrame.size.height + 4.0))
|
let forwardBackgroundFrame = CGRect(origin: CGPoint(x: forwardInfoFrame.minX - 6.0, y: forwardInfoFrame.minY - 2.0), size: CGSize(width: forwardInfoFrame.size.width + 10.0, height: forwardInfoFrame.size.height + 4.0))
|
||||||
|
strongSelf.forwardBackgroundNode?.frame = forwardBackgroundFrame
|
||||||
|
strongSelf.forwardBackgroundNode?.update(size: forwardBackgroundFrame.size, cornerRadius: 8.0, transition: .immediate)
|
||||||
} else if let forwardInfoNode = strongSelf.forwardInfoNode {
|
} else if let forwardInfoNode = strongSelf.forwardInfoNode {
|
||||||
forwardInfoNode.removeFromSupernode()
|
forwardInfoNode.removeFromSupernode()
|
||||||
strongSelf.forwardInfoNode = nil
|
strongSelf.forwardInfoNode = nil
|
||||||
|
@ -719,7 +719,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
let viaBotNode = viaBotApply()
|
let viaBotNode = viaBotApply()
|
||||||
if strongSelf.viaBotNode == nil {
|
if strongSelf.viaBotNode == nil {
|
||||||
strongSelf.viaBotNode = viaBotNode
|
strongSelf.viaBotNode = viaBotNode
|
||||||
strongSelf.addSubnode(viaBotNode)
|
strongSelf.contextSourceNode.contentNode.addSubnode(viaBotNode)
|
||||||
}
|
}
|
||||||
viaBotNode.frame = viaBotFrame
|
viaBotNode.frame = viaBotFrame
|
||||||
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
|
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
|
||||||
|
@ -11,13 +11,15 @@ private final class InstantVideoRadialStatusNodeParameters: NSObject {
|
|||||||
let progress: CGFloat
|
let progress: CGFloat
|
||||||
let dimProgress: CGFloat
|
let dimProgress: CGFloat
|
||||||
let playProgress: CGFloat
|
let playProgress: CGFloat
|
||||||
|
let blinkProgress: CGFloat
|
||||||
let hasSeek: Bool
|
let hasSeek: Bool
|
||||||
|
|
||||||
init(color: UIColor, progress: CGFloat, dimProgress: CGFloat, playProgress: CGFloat, hasSeek: Bool) {
|
init(color: UIColor, progress: CGFloat, dimProgress: CGFloat, playProgress: CGFloat, blinkProgress: CGFloat, hasSeek: Bool) {
|
||||||
self.color = color
|
self.color = color
|
||||||
self.progress = progress
|
self.progress = progress
|
||||||
self.dimProgress = dimProgress
|
self.dimProgress = dimProgress
|
||||||
self.playProgress = playProgress
|
self.playProgress = playProgress
|
||||||
|
self.blinkProgress = blinkProgress
|
||||||
self.hasSeek = hasSeek
|
self.hasSeek = hasSeek
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,6 +66,12 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var effectiveBlinkProgress: CGFloat = 0.0 {
|
||||||
|
didSet {
|
||||||
|
self.setNeedsDisplay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var _statusValue: MediaPlayerStatus?
|
private var _statusValue: MediaPlayerStatus?
|
||||||
private var statusValue: MediaPlayerStatus? {
|
private var statusValue: MediaPlayerStatus? {
|
||||||
get {
|
get {
|
||||||
@ -196,8 +204,38 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
if let seekingProgress = self.seekingProgress {
|
if let seekingProgress = self.seekingProgress {
|
||||||
if seekingProgress > 0.98 && fraction > 0.0 && fraction < 0.05 {
|
if seekingProgress > 0.98 && fraction > 0.0 && fraction < 0.05 {
|
||||||
self.hapticFeedback.impact(.light)
|
self.hapticFeedback.impact(.light)
|
||||||
|
|
||||||
|
let blinkAnimation = POPBasicAnimation()
|
||||||
|
blinkAnimation.property = POPAnimatableProperty.property(withName: "blinkProgress", initializer: { property in
|
||||||
|
property?.readBlock = { node, values in
|
||||||
|
values?.pointee = (node as! InstantVideoRadialStatusNode).effectiveBlinkProgress
|
||||||
|
}
|
||||||
|
property?.writeBlock = { node, values in
|
||||||
|
(node as! InstantVideoRadialStatusNode).effectiveBlinkProgress = values!.pointee
|
||||||
|
}
|
||||||
|
property?.threshold = 0.01
|
||||||
|
}) as? POPAnimatableProperty
|
||||||
|
blinkAnimation.fromValue = 1.0 as NSNumber
|
||||||
|
blinkAnimation.toValue = 0.0 as NSNumber
|
||||||
|
blinkAnimation.duration = 0.5
|
||||||
|
self.pop_add(blinkAnimation, forKey: "blinkProgress")
|
||||||
} else if seekingProgress > 0.0 && seekingProgress < 0.05 && fraction > 0.98 {
|
} else if seekingProgress > 0.0 && seekingProgress < 0.05 && fraction > 0.98 {
|
||||||
self.hapticFeedback.impact(.light)
|
self.hapticFeedback.impact(.light)
|
||||||
|
|
||||||
|
let blinkAnimation = POPBasicAnimation()
|
||||||
|
blinkAnimation.property = POPAnimatableProperty.property(withName: "blinkProgress", initializer: { property in
|
||||||
|
property?.readBlock = { node, values in
|
||||||
|
values?.pointee = (node as! InstantVideoRadialStatusNode).effectiveBlinkProgress
|
||||||
|
}
|
||||||
|
property?.writeBlock = { node, values in
|
||||||
|
(node as! InstantVideoRadialStatusNode).effectiveBlinkProgress = values!.pointee
|
||||||
|
}
|
||||||
|
property?.threshold = 0.01
|
||||||
|
}) as? POPAnimatableProperty
|
||||||
|
blinkAnimation.fromValue = -1.0 as NSNumber
|
||||||
|
blinkAnimation.toValue = 0.0 as NSNumber
|
||||||
|
blinkAnimation.duration = 0.5
|
||||||
|
self.pop_add(blinkAnimation, forKey: "blinkProgress")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let newProgress = min(0.99, fraction)
|
let newProgress = min(0.99, fraction)
|
||||||
@ -216,7 +254,7 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
|
override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
|
||||||
return InstantVideoRadialStatusNodeParameters(color: self.color, progress: self.effectiveProgress, dimProgress: self.effectiveDimProgress, playProgress: self.effectivePlayProgress, hasSeek: self.hasSeek)
|
return InstantVideoRadialStatusNodeParameters(color: self.color, progress: self.effectiveProgress, dimProgress: self.effectiveDimProgress, playProgress: self.effectivePlayProgress, blinkProgress: self.effectiveBlinkProgress, hasSeek: self.hasSeek)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
|
@objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
|
||||||
@ -231,9 +269,23 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
if let parameters = parameters as? InstantVideoRadialStatusNodeParameters {
|
if let parameters = parameters as? InstantVideoRadialStatusNodeParameters {
|
||||||
context.setStrokeColor(parameters.color.cgColor)
|
context.setStrokeColor(parameters.color.cgColor)
|
||||||
|
|
||||||
|
context.addEllipse(in: bounds)
|
||||||
|
context.clip()
|
||||||
|
|
||||||
if !parameters.dimProgress.isZero {
|
if !parameters.dimProgress.isZero {
|
||||||
context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.35 * min(1.0, parameters.dimProgress)).cgColor)
|
if parameters.playProgress == 1.0 {
|
||||||
context.fillEllipse(in: bounds)
|
context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.35 * min(1.0, parameters.dimProgress)).cgColor)
|
||||||
|
context.fillEllipse(in: bounds)
|
||||||
|
} else {
|
||||||
|
var locations: [CGFloat] = [0.0, 0.8, 1.0]
|
||||||
|
let alpha: CGFloat = 0.2 + 0.15 * parameters.playProgress
|
||||||
|
let colors: [CGColor] = [UIColor(rgb: 0x000000, alpha: alpha * min(1.0, parameters.dimProgress * parameters.playProgress)).cgColor, UIColor(rgb: 0x000000, alpha: alpha * min(1.0, parameters.dimProgress * parameters.playProgress)).cgColor, UIColor(rgb: 0x000000, alpha: alpha * min(1.0, parameters.dimProgress)).cgColor]
|
||||||
|
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||||
|
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
|
||||||
|
|
||||||
|
let center = bounds.center
|
||||||
|
context.drawRadialGradient(gradient, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: bounds.width / 2.0, options: .drawsAfterEndLocation)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.setBlendMode(.normal)
|
context.setBlendMode(.normal)
|
||||||
@ -255,9 +307,19 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !parameters.dimProgress.isZero {
|
if !parameters.dimProgress.isZero {
|
||||||
|
context.setLineWidth(lineWidth)
|
||||||
|
|
||||||
|
if parameters.blinkProgress > 0.0 {
|
||||||
|
context.setStrokeColor(parameters.color.withAlphaComponent(0.2 * parameters.blinkProgress).cgColor)
|
||||||
|
context.strokeEllipse(in: CGRect(x: (bounds.size.width - pathDiameter) / 2.0 , y: (bounds.size.height - pathDiameter) / 2.0, width: pathDiameter, height: pathDiameter))
|
||||||
|
}
|
||||||
|
|
||||||
if parameters.hasSeek {
|
if parameters.hasSeek {
|
||||||
context.setStrokeColor(parameters.color.withAlphaComponent(0.2 * parameters.dimProgress).cgColor)
|
var progress = parameters.dimProgress
|
||||||
context.setLineWidth(lineWidth)
|
if parameters.blinkProgress < 0.0 {
|
||||||
|
progress = parameters.dimProgress + parameters.blinkProgress
|
||||||
|
}
|
||||||
|
context.setStrokeColor(parameters.color.withAlphaComponent(0.2 * progress).cgColor)
|
||||||
context.strokeEllipse(in: CGRect(x: (bounds.size.width - pathDiameter) / 2.0 , y: (bounds.size.height - pathDiameter) / 2.0, width: pathDiameter, height: pathDiameter))
|
context.strokeEllipse(in: CGRect(x: (bounds.size.width - pathDiameter) / 2.0 , y: (bounds.size.height - pathDiameter) / 2.0, width: pathDiameter, height: pathDiameter))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,32 +206,28 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
|||||||
|
|
||||||
let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context, displayBackground: true)
|
let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context, displayBackground: true)
|
||||||
mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo
|
mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo
|
||||||
|
mediaAccessoryPanel.getController = { [weak self] in
|
||||||
|
return self?.parentController
|
||||||
|
}
|
||||||
|
mediaAccessoryPanel.presentInGlobalOverlay = { [weak self] c in
|
||||||
|
self?.parentController?.presentInGlobalOverlay(c)
|
||||||
|
}
|
||||||
mediaAccessoryPanel.close = { [weak self] in
|
mediaAccessoryPanel.close = { [weak self] in
|
||||||
if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType {
|
if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType {
|
||||||
strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause))
|
strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mediaAccessoryPanel.toggleRate = {
|
mediaAccessoryPanel.setRate = { [weak self] rate in
|
||||||
[weak self] in
|
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> AudioPlaybackRate in
|
let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> AudioPlaybackRate in
|
||||||
let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings) as? MusicPlaybackSettings ?? MusicPlaybackSettings.defaultSettings
|
let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings) as? MusicPlaybackSettings ?? MusicPlaybackSettings.defaultSettings
|
||||||
|
|
||||||
let nextRate: AudioPlaybackRate
|
|
||||||
switch settings.voicePlaybackRate {
|
|
||||||
case .x1:
|
|
||||||
nextRate = .x2
|
|
||||||
case .x2:
|
|
||||||
nextRate = .x1
|
|
||||||
default:
|
|
||||||
nextRate = .x1
|
|
||||||
}
|
|
||||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in
|
||||||
return settings.withUpdatedVoicePlaybackRate(nextRate)
|
return settings.withUpdatedVoicePlaybackRate(rate)
|
||||||
})
|
})
|
||||||
return nextRate
|
return rate
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { baseRate in
|
|> deliverOnMainQueue).start(next: { baseRate in
|
||||||
guard let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType else {
|
guard let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType else {
|
||||||
@ -250,22 +246,31 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
|||||||
})
|
})
|
||||||
|
|
||||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let slowdown = baseRate == .x1
|
let slowdown: Bool?
|
||||||
controller.present(
|
if baseRate == .x1 {
|
||||||
UndoOverlayController(
|
slowdown = true
|
||||||
presentationData: presentationData,
|
} else if baseRate == .x2 {
|
||||||
content: .audioRate(
|
slowdown = false
|
||||||
slowdown: slowdown,
|
} else {
|
||||||
text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp
|
slowdown = nil
|
||||||
|
}
|
||||||
|
if let slowdown = slowdown {
|
||||||
|
controller.present(
|
||||||
|
UndoOverlayController(
|
||||||
|
presentationData: presentationData,
|
||||||
|
content: .audioRate(
|
||||||
|
slowdown: slowdown,
|
||||||
|
text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp
|
||||||
|
),
|
||||||
|
elevatedLayout: false,
|
||||||
|
animateInAsReplacement: hasTooltip,
|
||||||
|
action: { action in
|
||||||
|
return true
|
||||||
|
}
|
||||||
),
|
),
|
||||||
elevatedLayout: false,
|
in: .current
|
||||||
animateInAsReplacement: hasTooltip,
|
)
|
||||||
action: { action in
|
}
|
||||||
return true
|
|
||||||
}
|
|
||||||
),
|
|
||||||
in: .current
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
|||||||
private let hasContactSelector: Bool
|
private let hasContactSelector: Bool
|
||||||
private let hasGlobalSearch: Bool
|
private let hasGlobalSearch: Bool
|
||||||
private let pretendPresentedInModal: Bool
|
private let pretendPresentedInModal: Bool
|
||||||
|
private let forwardedMessagesCount: Int
|
||||||
|
|
||||||
override public var _presentedInModal: Bool {
|
override public var _presentedInModal: Bool {
|
||||||
get {
|
get {
|
||||||
@ -85,6 +86,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
|||||||
self.attemptSelection = params.attemptSelection
|
self.attemptSelection = params.attemptSelection
|
||||||
self.createNewGroup = params.createNewGroup
|
self.createNewGroup = params.createNewGroup
|
||||||
self.pretendPresentedInModal = params.pretendPresentedInModal
|
self.pretendPresentedInModal = params.pretendPresentedInModal
|
||||||
|
self.forwardedMessagesCount = params.forwardedMessagesCount
|
||||||
|
|
||||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||||
|
|
||||||
@ -148,7 +150,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
self.displayNode = PeerSelectionControllerNode(context: self.context, filter: self.filter, hasChatListSelector: self.hasChatListSelector, hasContactSelector: self.hasContactSelector, hasGlobalSearch: self.hasGlobalSearch, createNewGroup: self.createNewGroup, present: { [weak self] c, a in
|
self.displayNode = PeerSelectionControllerNode(context: self.context, filter: self.filter, hasChatListSelector: self.hasChatListSelector, hasContactSelector: self.hasContactSelector, hasGlobalSearch: self.hasGlobalSearch, forwardedMessagesCount: self.forwardedMessagesCount, createNewGroup: self.createNewGroup, present: { [weak self] c, a in
|
||||||
self?.present(c, in: .window(.root), with: a)
|
self?.present(c, in: .window(.root), with: a)
|
||||||
}, presentInGlobalOverlay: { [weak self] c, a in
|
}, presentInGlobalOverlay: { [weak self] c, a in
|
||||||
self?.presentInGlobalOverlay(c, with: a)
|
self?.presentInGlobalOverlay(c, with: a)
|
||||||
|
@ -66,7 +66,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
|||||||
return self.readyValue.get()
|
return self.readyValue.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
init(context: AccountContext, filter: ChatListNodePeersFilter, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) {
|
init(context: AccountContext, filter: ChatListNodePeersFilter, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessagesCount: Int, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.present = present
|
self.present = present
|
||||||
self.presentInGlobalOverlay = presentInGlobalOverlay
|
self.presentInGlobalOverlay = presentInGlobalOverlay
|
||||||
@ -79,6 +79,16 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(PeerId(0)), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil)
|
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(PeerId(0)), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil)
|
||||||
|
|
||||||
|
var mockMessageIds: [MessageId]?
|
||||||
|
if forwardedMessagesCount > 0 {
|
||||||
|
var messageIds: [MessageId] = []
|
||||||
|
for _ in 0 ..< forwardedMessagesCount {
|
||||||
|
messageIds.append(MessageId(peerId: PeerId(0), namespace: Namespaces.Message.Local, id: Int32.random(in: 0 ..< Int32.max)))
|
||||||
|
}
|
||||||
|
mockMessageIds = messageIds
|
||||||
|
}
|
||||||
|
self.presentationInterfaceState = self.presentationInterfaceState.updatedInterfaceState { $0.withUpdatedForwardMessageIds(mockMessageIds) }
|
||||||
|
|
||||||
if hasChatListSelector && hasContactSelector {
|
if hasChatListSelector && hasContactSelector {
|
||||||
self.toolbarBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.rootController.navigationBar.blurredBackgroundColor)
|
self.toolbarBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.rootController.navigationBar.blurredBackgroundColor)
|
||||||
|
|
||||||
@ -276,13 +286,6 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
|||||||
guard let textInputNode = textInputPanelNode.textInputNode else {
|
guard let textInputNode = textInputPanelNode.textInputNode else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// let previousSupportedOrientations = strongSelf.supportedOrientations
|
|
||||||
// if layout.size.width > layout.size.height {
|
|
||||||
// strongSelf.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .landscape)
|
|
||||||
// } else {
|
|
||||||
// strongSelf.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
|
||||||
// }
|
|
||||||
|
|
||||||
let controller = ChatSendMessageActionSheetController(context: strongSelf.context, interfaceState: strongSelf.presentationInterfaceState, gesture: gesture, sourceSendButton: node, textInputNode: textInputNode, completion: { [weak self] in
|
let controller = ChatSendMessageActionSheetController(context: strongSelf.context, interfaceState: strongSelf.presentationInterfaceState, gesture: gesture, sourceSendButton: node, textInputNode: textInputNode, completion: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
// strongSelf.supportedOrientations = previousSupportedOrientations
|
// strongSelf.supportedOrientations = previousSupportedOrientations
|
||||||
@ -292,7 +295,6 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
|||||||
}, schedule: { [weak textInputPanelNode] in
|
}, schedule: { [weak textInputPanelNode] in
|
||||||
textInputPanelNode?.sendMessage(.schedule)
|
textInputPanelNode?.sendMessage(.schedule)
|
||||||
})
|
})
|
||||||
// strongSelf.sendMessageActionsController = controller
|
|
||||||
strongSelf.presentInGlobalOverlay(controller, nil)
|
strongSelf.presentInGlobalOverlay(controller, nil)
|
||||||
}, openScheduledMessages: {
|
}, openScheduledMessages: {
|
||||||
}, openPeersNearby: {
|
}, openPeersNearby: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user