Various UI improvements

This commit is contained in:
Ilya Laktyushin 2020-12-25 20:16:12 +04:00
parent 1f23769a5b
commit 5459c357d4
28 changed files with 538 additions and 61 deletions

View File

@ -5668,6 +5668,8 @@ Sorry for the inconvenience.";
"Media.LimitedAccessTitle" = "Limited Access to Media";
"Media.LimitedAccessText" = "You've given Telegram access only to select number of photos.";
"Media.LimitedAccessManage" = "Manage";
"Media.LimitedAccessSelectMore" = "Select More Photos...";
"Media.LimitedAccessChangeSettings" = "Change Settings";
"VoiceChat.StatusSpeaking" = "speaking";
"VoiceChat.StatusListening" = "listening";

View File

@ -466,6 +466,38 @@ public final class ContactSelectionControllerParams {
}
}
public enum ChatListSearchFilter: Equatable {
case chats
case media
case links
case files
case music
case voice
case peer(PeerId, Bool, String, String)
case date(Int32?, Int32, String)
public var id: Int32 {
switch self {
case .chats:
return 0
case .media:
return 1
case .links:
return 2
case .files:
return 3
case .music:
return 4
case .voice:
return 5
case let .peer(peerId, _, _, _):
return peerId.id
case let .date(_, date, _):
return date
}
}
}
#if ENABLE_WALLET
public enum OpenWalletContext {
case generic
@ -526,6 +558,7 @@ public protocol SharedAccountContext: class {
func updateNotificationTokensRegistration()
func setAccountUserInterfaceInUse(_ id: AccountRecordId) -> Disposable
func handleTextLinkAction(context: AccountContext, peerId: PeerId?, navigateDisposable: MetaDisposable, controller: ViewController, action: TextLinkItemActionType, itemLink: TextLinkItem)
func openSearch(filter: ChatListSearchFilter, query: String?)
func navigateToChat(accountId: AccountRecordId, peerId: PeerId, messageId: MessageId?)
func openChatMessage(_ params: OpenChatMessageParams) -> Bool
func messageFromPreloadedChatHistoryViewForLocation(id: MessageId, location: ChatHistoryLocationInput, context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, tagMask: MessageTags?) -> Signal<(MessageIndex?, Bool), NoError>

View File

@ -8,7 +8,7 @@ public protocol ChatListController: ViewController {
var lockViewFrame: CGRect? { get }
var isSearchActive: Bool { get }
func activateSearch()
func activateSearch(filter: ChatListSearchFilter, query: String?)
func deactivateSearch(animated: Bool)
func activateCompose()
func maybeAskForPeerChatRemoval(peer: RenderedPeer, joined: Bool, deleteGloballyIfPossible: Bool, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void)

View File

@ -1679,7 +1679,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
public private(set) var isSearchActive: Bool = false
public func activateSearch() {
public func activateSearch(filter: ChatListSearchFilter = .chats, query: String? = nil) {
if self.displayNavigationBar {
let _ = (combineLatest(self.chatListDisplayNode.containerNode.currentItemNode.contentsReady |> take(1), self.context.account.postbox.tailChatListView(groupId: .root, count: 16, summaryComponents: ChatListEntrySummaryComponents(tagSummary: nil, actionsSummary: nil)) |> take(1))
|> deliverOnMainQueue).start(next: { [weak self] _, chatListView in
@ -1703,7 +1703,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
if let searchContentNode = strongSelf.searchContentNode {
if let filterContainerNodeAndActivate = strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, displaySearchFilters: displaySearchFilters, navigationController: strongSelf.navigationController as? NavigationController) {
if let filterContainerNodeAndActivate = strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, displaySearchFilters: displaySearchFilters, initialFilter: filter, navigationController: strongSelf.navigationController as? NavigationController) {
let (filterContainerNode, activate) = filterContainerNodeAndActivate
if displaySearchFilters {
strongSelf.navigationBar?.setSecondaryContentNode(filterContainerNode, animated: false)
@ -1714,6 +1714,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
activate()
if let searchContentNode = strongSelf.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode {
searchContentNode.search(filter: filter, query: query)
}
if !tabsIsEmpty {
Queue.mainQueue().after(0.01) {
filterContainerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 38.0), to: CGPoint(), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
@ -1739,6 +1743,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
}
}
} else if self.isSearchActive {
if let searchContentNode = self.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode {
searchContentNode.search(filter: filter, query: query)
}
}
}

View File

@ -1146,7 +1146,7 @@ final class ChatListControllerNode: ASDisplayNode {
}
}
func activateSearch(placeholderNode: SearchBarPlaceholderNode, displaySearchFilters: Bool, navigationController: NavigationController?) -> (ASDisplayNode, () -> Void)? {
func activateSearch(placeholderNode: SearchBarPlaceholderNode, displaySearchFilters: Bool, initialFilter: ChatListSearchFilter, navigationController: NavigationController?) -> (ASDisplayNode, () -> Void)? {
guard let (containerLayout, _, _, cleanNavigationBarHeight) = self.containerLayout, let navigationBar = self.navigationBar, self.searchDisplayController == nil else {
return nil
}
@ -1156,7 +1156,7 @@ final class ChatListControllerNode: ASDisplayNode {
filter.insert(.excludeRecent)
}
let contentNode = ChatListSearchContainerNode(context: self.context, filter: filter, groupId: self.groupId, displaySearchFilters: displaySearchFilters, openPeer: { [weak self] peer, dismissSearch in
let contentNode = ChatListSearchContainerNode(context: self.context, filter: filter, groupId: self.groupId, displaySearchFilters: displaySearchFilters, initialFilter: initialFilter, openPeer: { [weak self] peer, dismissSearch in
self?.requestOpenPeerFromSearch?(peer, dismissSearch)
}, openDisabledPeer: { _ in
}, openRecentPeerOptions: { [weak self] peer in

View File

@ -109,8 +109,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
private var stateValue = ChatListSearchContainerNodeSearchState()
private let statePromise = ValuePromise<ChatListSearchContainerNodeSearchState>()
private var selectedFilterKey: ChatListSearchFilterEntryId? = .filter(ChatListSearchFilter.chats.id)
private var selectedFilterKeyPromise = Promise<ChatListSearchFilterEntryId?>(.filter(ChatListSearchFilter.chats.id))
private var selectedFilterKey: ChatListSearchFilterEntryId?
private var selectedFilterKeyPromise = Promise<ChatListSearchFilterEntryId?>()
private var transitionFraction: CGFloat = 0.0
private var didSetReady: Bool = false
@ -121,7 +121,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
private var validLayout: (ContainerViewLayout, CGFloat)?
public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, displaySearchFilters: Bool, openPeer originalOpenPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) {
public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, displaySearchFilters: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) {
self.context = context
self.peersFilter = filter
self.groupId = groupId
@ -129,6 +129,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
self.navigationController = navigationController
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.selectedFilterKey = .filter(initialFilter.id)
self.selectedFilterKeyPromise.set(.single(self.selectedFilterKey))
self.openMessage = originalOpenMessage
self.present = present
self.presentInGlobalOverlay = presentInGlobalOverlay
@ -293,6 +296,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
}
self.filterContainerNode.filterPressed?(initialFilter)
let suggestedPeers = self.searchQuery.get()
|> mapToSignal { query -> Signal<[Peer], NoError> in
if let query = query {
@ -490,6 +495,28 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
self.suggestedDates.set(.single(suggestDates(for: text, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat)))
}
public func search(filter: ChatListSearchFilter, query: String?) {
let key: ChatListSearchPaneKey
switch filter {
case .media:
key = .media
case .links:
key = .links
case .files:
key = .files
case .music:
key = .music
case .voice:
key = .voice
default:
key = .chats
}
self.paneContainerNode.requestSelectPane(key)
self.updateSearchOptions(nil)
self.searchTextUpdated(text: query ?? "")
self.setQuery?(nil, [], query ?? "")
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)

View File

@ -6,38 +6,7 @@ import SyncCore
import Postbox
import TelegramCore
import TelegramPresentationData
enum ChatListSearchFilter: Equatable {
case chats
case media
case links
case files
case music
case voice
case peer(PeerId, Bool, String, String)
case date(Int32?, Int32, String)
var id: Int32 {
switch self {
case .chats:
return 0
case .media:
return 1
case .links:
return 2
case .files:
return 3
case .music:
return 4
case .voice:
return 5
case let .peer(peerId, _, _, _):
return peerId.id
case let .date(_, date, _):
return date
}
}
}
import AccountContext
private final class ItemNode: ASDisplayNode {
private let pressed: () -> Void

View File

@ -1730,6 +1730,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
nextRate = .x2
case .x2:
nextRate = .x1
default:
nextRate = .x1
}
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in
return settings.withUpdatedVoicePlaybackRate(nextRate)

View File

@ -316,7 +316,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, UIGestureRecognizerD
self.currentPaneKey = nil
self.pendingSwitchToPaneKey = nil
}
} else if self.currentPaneKey == nil {
} else if self.currentPaneKey == nil && self.pendingSwitchToPaneKey == nil {
self.pendingSwitchToPaneKey = availablePanes.first
}

View File

@ -46,6 +46,7 @@ objc_library(
],
weak_sdk_frameworks = [
"Vision",
"PhotosUI",
],
visibility = [
"//visibility:public",

View File

@ -32,6 +32,7 @@ typedef enum
@property (nonatomic, readonly) UIColor *textColor;
@property (nonatomic, readonly) UIColor *secondaryTextColor;
@property (nonatomic, readonly) UIColor *accentColor;
@property (nonatomic, readonly) UIColor *destructiveColor;
@property (nonatomic, readonly) UIColor *barBackgroundColor;
@property (nonatomic, readonly) UIColor *barSeparatorColor;
@property (nonatomic, readonly) UIColor *navigationTitleColor;
@ -42,7 +43,7 @@ typedef enum
@property (nonatomic, readonly) UIColor *maybeAccentColor;
+ (instancetype)palleteWithDark:(bool)dark backgroundColor:(UIColor *)backgroundColor selectionColor:(UIColor *)selectionColor separatorColor:(UIColor *)separatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor barBackgroundColor:(UIColor *)barBackgroundColor barSeparatorColor:(UIColor *)barSeparatorColor navigationTitleColor:(UIColor *)navigationTitleColor badge:(UIImage *)badge badgeTextColor:(UIColor *)badgeTextColor sendIconImage:(UIImage *)sendIconImage doneIconImage:(UIImage *)doneIconImage maybeAccentColor:(UIColor *)maybeAccentColor;
+ (instancetype)palleteWithDark:(bool)dark backgroundColor:(UIColor *)backgroundColor selectionColor:(UIColor *)selectionColor separatorColor:(UIColor *)separatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor destructiveColor:(UIColor *)destructiveColor barBackgroundColor:(UIColor *)barBackgroundColor barSeparatorColor:(UIColor *)barSeparatorColor navigationTitleColor:(UIColor *)navigationTitleColor badge:(UIImage *)badge badgeTextColor:(UIColor *)badgeTextColor sendIconImage:(UIImage *)sendIconImage doneIconImage:(UIImage *)doneIconImage maybeAccentColor:(UIColor *)maybeAccentColor;
@end

View File

@ -36,6 +36,8 @@
@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t));
@property (nonatomic, copy) void (^presentTimerController)(void (^)(int32_t));
@property (nonatomic, assign) CGFloat topInset;
@property (nonatomic, strong) TGMediaAssetsPallete *pallete;
@property (nonatomic, readonly) TGMediaSelectionContext *selectionContext;

View File

@ -1,5 +1,7 @@
#import "TGMediaAssetsPickerController.h"
#import <Photos/Photos.h>
#import <PhotosUI/PhotosUI.h>
#import "LegacyComponentsInternal.h"
#import "TGMediaGroupsController.h"
@ -26,14 +28,145 @@
#import <LegacyComponents/TGVideoEditAdjustments.h>
#import <LegacyComponents/TGPaintingData.h>
#import "TGModernButton.h"
#import "PGPhotoEditor.h"
@interface TGMediaPickerAccessView: UIView
{
TGMediaAssetsPallete *_pallete;
UIView *_backgroundView;
UIView *_separatorView;
UIView *_bottomSeparatorView;
UIImageView *_iconView;
UILabel *_labelView;
UILabel *_titleView;
UILabel *_textView;
TGModernButton * _buttonView;
CGSize _titleSize;
CGSize _textSize;
}
@property (nonatomic, assign) UIEdgeInsets safeAreaInset;
@property (nonatomic, copy) void (^pressed)(void);
@end
@implementation TGMediaPickerAccessView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self != nil) {
_backgroundView = [[UIView alloc] init];
_separatorView = [[UIView alloc] init];
_bottomSeparatorView = [[UIView alloc] init];
_iconView = [[UIImageView alloc] init];
_labelView = [[UILabel alloc] init];
_titleView = [[UILabel alloc] init];
_textView = [[UILabel alloc] init];
_labelView.font = TGSystemFontOfSize(14.0);
_labelView.text = @"!";
_labelView.textAlignment = NSTextAlignmentCenter;
_titleView.font = TGSemiboldSystemFontOfSize(17.0);
_titleView.text = TGLocalized(@"Media.LimitedAccessTitle");
_titleView.numberOfLines = 1;
_textView.font = TGSystemFontOfSize(14.0);
_textView.text = TGLocalized(@"Media.LimitedAccessText");
_textView.numberOfLines = 3;
_buttonView = [[TGModernButton alloc] init];
_buttonView.adjustsImageWhenHighlighted = false;
_buttonView.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
_buttonView.contentEdgeInsets = UIEdgeInsetsMake(0.0, 15.0, 0.0, 0.0);
_buttonView.titleLabel.font = TGSystemFontOfSize(17.0f);
_buttonView.highlightBackgroundColor = UIColorRGB(0xebebeb);
[_buttonView setTitle:TGLocalized(@"Media.LimitedAccessManage") forState:UIControlStateNormal];
[_buttonView addTarget:self action:@selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_backgroundView];
[self addSubview:_separatorView];
[self addSubview:_bottomSeparatorView];
[self addSubview:_iconView];
[self addSubview:_labelView];
[self addSubview:_titleView];
[self addSubview:_textView];
[self addSubview:_buttonView];
}
return self;
}
- (void)buttonPressed {
self.pressed();
}
- (void)setPallete:(TGMediaAssetsPallete *)pallete {
_pallete = pallete;
_backgroundView.backgroundColor = pallete.backgroundColor;
_separatorView.backgroundColor = pallete.separatorColor;
_bottomSeparatorView.backgroundColor = pallete.separatorColor;
_titleView.textColor = pallete.textColor;
_textView.textColor = pallete.textColor;
_iconView.image = TGCircleImage(20.0, pallete.destructiveColor);
_labelView.textColor = pallete.badgeTextColor;
_buttonView.highlightBackgroundColor = pallete.selectionColor;
[_buttonView setTitleColor:pallete.accentColor];
}
- (CGSize)sizeThatFits:(CGSize)size {
CGSize result = CGSizeMake(size.width, 0.0);
CGSize constrainedSize = CGSizeMake(size.width - 30.0, size.height);
CGSize titleSize = [_titleView sizeThatFits:constrainedSize];
CGSize textSize = [_textView sizeThatFits:constrainedSize];
result.height += titleSize.height;
result.height += textSize.height;
result.height += 45.0;
result.height += 39.0;
result.height = CGFloor(result.height);
_titleSize = titleSize;
_textSize = textSize;
return result;
}
- (void)setSafeAreaInset:(UIEdgeInsets)safeAreaInset {
_safeAreaInset = safeAreaInset;
[self layoutSubviews];
}
- (void)layoutSubviews {
_backgroundView.frame = self.bounds;
_iconView.frame = CGRectMake(self.safeAreaInset.left + 15.0, 16.0, 20.0, 20.0);
_labelView.frame = _iconView.frame;
_buttonView.contentEdgeInsets = UIEdgeInsetsMake(0.0, self.safeAreaInset.left + 15.0, 0.0, 0.0);
_titleView.frame = CGRectMake(self.safeAreaInset.left + 42.0, 15.0, _titleSize.width, _titleSize.height);
_textView.frame = CGRectMake(self.safeAreaInset.left + 15.0, 46.0, _textSize.width, _textSize.height);
_separatorView.frame = CGRectMake(self.safeAreaInset.left + 15.0, self.bounds.size.height - 46.0, self.bounds.size.width, TGScreenPixel);
_buttonView.frame = CGRectMake(0.0, self.bounds.size.height - 46.0, self.bounds.size.width, 46.0);
_bottomSeparatorView.frame = CGRectMake(0.0, self.bounds.size.height - TGScreenPixel, self.bounds.size.width, TGScreenPixel);
}
@end
@interface TGMediaAssetsController () <UINavigationControllerDelegate, ASWatcher>
{
TGMediaAssetsControllerIntent _intent;
TGMediaPickerToolbarView *_toolbarView;
TGMediaPickerAccessView *_accessView;
SMetaDisposable *_groupingChangedDisposable;
SMetaDisposable *_selectionChangedDisposable;
SMetaDisposable *_timersChangedDisposable;
@ -456,6 +589,8 @@
_toolbarView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
if ((_intent != TGMediaAssetsControllerSendFileIntent && _intent != TGMediaAssetsControllerSendMediaIntent && _intent != TGMediaAssetsControllerPassportMultipleIntent) || _selectionContext == nil)
[_toolbarView setRightButtonHidden:true];
__weak TGMediaAssetsController *weakSelf = self;
if (_selectionContext.allowGrouping)
{
[_toolbarView setCenterButtonImage:TGTintedImage(TGComponentsImageNamed(@"MediaPickerGroupPhotosIcon"), _pallete != nil ? _pallete.secondaryTextColor : UIColorRGB(0x858e99))];
@ -463,7 +598,6 @@
[_toolbarView setCenterButtonHidden:true animated:false];
[_toolbarView setCenterButtonSelected:_selectionContext.grouping];
__weak TGMediaAssetsController *weakSelf = self;
_toolbarView.centerPressed = ^
{
__strong TGMediaAssetsController *strongSelf = weakSelf;
@ -472,6 +606,73 @@
};
}
[self.view addSubview:_toolbarView];
if (iosMajorVersion() >= 14 && [PHPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelReadWrite] == PHAuthorizationStatusLimited) {
_accessView = [[TGMediaPickerAccessView alloc] init];
_accessView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_accessView.safeAreaInset = [TGViewController safeAreaInsetForOrientation:self.interfaceOrientation hasOnScreenNavigation:hasOnScreenNavigation];
[_accessView setPallete:_pallete];
_accessView.pressed = ^{
__strong TGMediaAssetsController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf manageAccess];
}
};
[self.view addSubview:_accessView];
}
}
- (void)manageAccess
{
if (iosMajorVersion() < 14) {
return;
}
__weak TGMediaAssetsController *weakSelf = self;
TGMenuSheetController *controller = [[TGMenuSheetController alloc] initWithContext:_context dark:false];
controller.dismissesByOutsideTap = true;
controller.narrowInLandscape = true;
__weak TGMenuSheetController *weakController = controller;
NSArray *items = @
[
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Media.LimitedAccessSelectMore") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
{
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
__strong TGMediaAssetsController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongController dismissAnimated:true manual:false completion:nil];
[[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:strongSelf];
}],
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Media.LimitedAccessChangeSettings") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
{
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
__strong TGMediaAssetsController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongController dismissAnimated:true manual:false completion:nil];
[[[LegacyComponentsGlobals provider] applicationInstance] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}],
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") type:TGMenuSheetButtonTypeCancel fontSize:20.0 action:^
{
__strong TGMenuSheetController *strongController = weakController;
if (strongController != nil)
[strongController dismissAnimated:true];
}]
];
[controller setItemViews:items];
[controller presentInViewController:self sourceView:self.view animated:true];
}
- (void)viewDidLoad
@ -509,6 +710,12 @@
[_editingContext clearPaintingData];
}
- (void)setPallete:(TGMediaAssetsPallete *)pallete {
_pallete = pallete;
[_accessView setPallete:pallete];
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
@ -522,7 +729,20 @@
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON;
_toolbarView.safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation];
_accessView.safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation];
if (_accessView != nil) {
CGSize accessSize = [_accessView sizeThatFits:self.view.frame.size];
_accessView.frame = CGRectMake(0.0, self.navigationBar.frame.size.height, self.view.frame.size.width, accessSize.height);
for (UIViewController *controller in self.viewControllers) {
if ([controller isKindOfClass:[TGMediaGroupsController class]]) {
((TGMediaGroupsController *)controller).topInset = accessSize.height;
} else if ([controller isKindOfClass:[TGMediaPickerController class]]) {
((TGMediaPickerController *)controller).topInset = accessSize.height;
}
}
}
if (_searchController == nil)
return;
@ -1384,7 +1604,7 @@
@implementation TGMediaAssetsPallete
+ (instancetype)palleteWithDark:(bool)dark backgroundColor:(UIColor *)backgroundColor selectionColor:(UIColor *)selectionColor separatorColor:(UIColor *)separatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor barBackgroundColor:(UIColor *)barBackgroundColor barSeparatorColor:(UIColor *)barSeparatorColor navigationTitleColor:(UIColor *)navigationTitleColor badge:(UIImage *)badge badgeTextColor:(UIColor *)badgeTextColor sendIconImage:(UIImage *)sendIconImage doneIconImage:(UIImage *)doneIconImage maybeAccentColor:(UIColor *)maybeAccentColor
+ (instancetype)palleteWithDark:(bool)dark backgroundColor:(UIColor *)backgroundColor selectionColor:(UIColor *)selectionColor separatorColor:(UIColor *)separatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor destructiveColor:(UIColor *)destructiveColor barBackgroundColor:(UIColor *)barBackgroundColor barSeparatorColor:(UIColor *)barSeparatorColor navigationTitleColor:(UIColor *)navigationTitleColor badge:(UIImage *)badge badgeTextColor:(UIColor *)badgeTextColor sendIconImage:(UIImage *)sendIconImage doneIconImage:(UIImage *)doneIconImage maybeAccentColor:(UIColor *)maybeAccentColor
{
TGMediaAssetsPallete *pallete = [[TGMediaAssetsPallete alloc] init];
pallete->_isDark = dark;
@ -1394,6 +1614,7 @@
pallete->_textColor = textColor;
pallete->_secondaryTextColor = secondaryTextColor;
pallete->_accentColor = accentColor;
pallete->_destructiveColor = destructiveColor;
pallete->_barBackgroundColor = barBackgroundColor;
pallete->_barSeparatorColor = barSeparatorColor;
pallete->_navigationTitleColor = navigationTitleColor;

View File

@ -8,6 +8,7 @@
@property (nonatomic, assign) bool localMediaCacheEnabled;
@property (nonatomic, assign) bool liveVideoUploadEnabled;
@property (nonatomic, assign) bool captionsEnabled;
@property (nonatomic, assign) CGFloat topInset;
@property (nonatomic, strong) TGMediaAssetsPallete *pallete;

View File

@ -53,7 +53,6 @@
if (iosMajorVersion() >= 11)
_tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
_tableView.alwaysBounceVertical = true;
_tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_tableView.backgroundColor = self.view.backgroundColor;
_tableView.delaysContentTouches = true;
_tableView.canCancelContentTouches = true;
@ -71,6 +70,17 @@
[self controllerInsetUpdated:UIEdgeInsetsZero];
}
- (void)setTopInset:(CGFloat)topInset {
_topInset = topInset;
[self viewDidLayoutSubviews];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
_tableView.frame = CGRectMake(0.0, _topInset, self.view.bounds.size.width, self.view.bounds.size.height - _topInset);
}
- (void)loadViewIfNeeded
{
if (iosMajorVersion() >= 9)

View File

@ -110,7 +110,7 @@
UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation];
CGSize frameSize = self.view.frame.size;
CGRect collectionViewFrame = CGRectMake(safeAreaInset.left, 0.0f, frameSize.width - safeAreaInset.left - safeAreaInset.right, frameSize.height);
CGRect collectionViewFrame = CGRectMake(safeAreaInset.left, _topInset, frameSize.width - safeAreaInset.left - safeAreaInset.right, frameSize.height - _topInset);
_collectionViewWidth = collectionViewFrame.size.width;
_collectionView.frame = collectionViewFrame;
}
@ -280,6 +280,11 @@
[_collectionView setContentOffset:contentOffset animated:false];
}
- (void)setTopInset:(CGFloat)topInset {
_topInset = topInset;
[self layoutControllerForSize:self.view.frame.size duration:0.0];
}
- (void)layoutControllerForSize:(CGSize)size duration:(NSTimeInterval)duration
{
[super layoutControllerForSize:size duration:duration];
@ -308,7 +313,7 @@
UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation];
CGRect frame = CGRectMake(safeAreaInset.left, 0, size.width - safeAreaInset.left - safeAreaInset.right, size.height);
CGRect frame = CGRectMake(safeAreaInset.left, _topInset, size.width - safeAreaInset.left - safeAreaInset.right, size.height - _topInset);
_collectionViewWidth = frame.size.width;
_collectionView.frame = frame;

View File

@ -288,7 +288,7 @@ private final class LegacyComponentsGlobalsProviderImpl: NSObject, LegacyCompone
let navigationBar = presentationTheme.rootController.navigationBar
let tabBar = presentationTheme.rootController.tabBar
return TGMediaAssetsPallete(dark: presentationTheme.overallDarkAppearance, backgroundColor: theme.plainBackgroundColor, selectionColor: theme.itemHighlightedBackgroundColor, separatorColor: theme.itemPlainSeparatorColor, textColor: theme.itemPrimaryTextColor, secondaryTextColor: theme.controlSecondaryColor, accentColor: theme.itemAccentColor, barBackgroundColor: tabBar.backgroundColor, barSeparatorColor: tabBar.separatorColor, navigationTitleColor: navigationBar.primaryTextColor, badge: generateStretchableFilledCircleImage(diameter: 22.0, color: navigationBar.accentTextColor), badgeTextColor: navigationBar.backgroundColor, sendIconImage: PresentationResourcesChat.chatInputPanelSendButtonImage(presentationTheme), doneIconImage: PresentationResourcesChat.chatInputPanelApplyButtonImage(presentationTheme), maybeAccentColor: navigationBar.accentTextColor)
return TGMediaAssetsPallete(dark: presentationTheme.overallDarkAppearance, backgroundColor: theme.plainBackgroundColor, selectionColor: theme.itemHighlightedBackgroundColor, separatorColor: theme.itemPlainSeparatorColor, textColor: theme.itemPrimaryTextColor, secondaryTextColor: theme.controlSecondaryColor, accentColor: theme.itemAccentColor, destructiveColor: theme.itemDestructiveColor, barBackgroundColor: tabBar.backgroundColor, barSeparatorColor: tabBar.separatorColor, navigationTitleColor: navigationBar.primaryTextColor, badge: generateStretchableFilledCircleImage(diameter: 22.0, color: navigationBar.accentTextColor), badgeTextColor: navigationBar.backgroundColor, sendIconImage: PresentationResourcesChat.chatInputPanelSendButtonImage(presentationTheme), doneIconImage: PresentationResourcesChat.chatInputPanelApplyButtonImage(presentationTheme), maybeAccentColor: navigationBar.accentTextColor)
}
func checkButtonPallete() -> TGCheckButtonPallete! {

View File

@ -189,6 +189,8 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate
self.rateButton.accessibilityValue = self.strings.VoiceOver_Media_PlaybackRateFast
self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange
default:
break
}
}
}
@ -383,6 +385,8 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateInactiveIcon(self.theme), for: [])
case .x2:
self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateActiveIcon(self.theme), for: [])
default:
break
}
}
if let (size, leftInset, rightInset) = self.validLayout {

View File

@ -663,6 +663,8 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
nextRate = .x2
case .x2:
nextRate = .x1
default:
nextRate = .x1
}
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in
return settings.withUpdatedVoicePlaybackRate(nextRate)
@ -813,7 +815,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
})]
}
private func joinGroupCall(peerId: PeerId, info: GroupCallInfo) {
open func joinGroupCall(peerId: PeerId, info: GroupCallInfo) {
self.context.joinGroupCall(peerId: peerId, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
}
}

View File

@ -3,8 +3,11 @@ import UIKit
import AsyncDisplayKit
import Display
private let circleDiameter: CGFloat = 80.0
final class IconButtonNode: HighlightTrackingButtonNode {
private let iconNode: ASImageNode
private var circleNode: ASImageNode?
var icon: UIImage? {
didSet {
@ -14,12 +17,44 @@ final class IconButtonNode: HighlightTrackingButtonNode {
}
}
var circleColor: UIColor? {
didSet {
if let color = self.circleColor {
let circleNode: ASImageNode
if let current = self.circleNode {
circleNode = current
} else {
circleNode = ASImageNode()
circleNode.alpha = 0.0
circleNode.displaysAsynchronously = false
circleNode.displayWithoutProcessing = true
circleNode.bounds = CGRect(origin: CGPoint(), size: CGSize(width: circleDiameter, height: circleDiameter))
circleNode.position = CGPoint(x: self.frame.width / 2.0, y: self.frame.height / 2.0)
self.insertSubnode(circleNode, at: 0)
self.circleNode = circleNode
}
circleNode.image = generateFilledCircleImage(diameter: circleDiameter, color: color)
} else if let current = self.circleNode {
self.circleNode = nil
current.removeFromSupernode()
}
}
}
override var isEnabled: Bool {
didSet {
self.alpha = self.isEnabled ? 1.0 : 0.4
}
}
var isPressing = false {
didSet {
if self.isPressing != oldValue && !self.isPressing {
self.highligthedChanged(false)
}
}
}
init() {
self.iconNode = ASImageNode()
self.iconNode.isLayerBacked = true
@ -33,11 +68,17 @@ final class IconButtonNode: HighlightTrackingButtonNode {
self.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if highlighted {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.09, curve: .spring)
let transition: ContainedViewLayoutTransition = .animated(duration: 0.18, curve: .linear)
transition.updateSublayerTransformScale(node: strongSelf, scale: 0.8)
} else {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.18, curve: .spring)
if let circleNode = strongSelf.circleNode {
transition.updateAlpha(node: circleNode, alpha: 1.0)
}
} else if !strongSelf.isPressing {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.35, curve: .linear)
transition.updateSublayerTransformScale(node: strongSelf, scale: 1.0)
if let circleNode = strongSelf.circleNode {
transition.updateAlpha(node: circleNode, alpha: 0.0)
}
}
}
}
@ -51,5 +92,6 @@ final class IconButtonNode: HighlightTrackingButtonNode {
if let image = self.iconNode.image {
self.iconNode.frame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size)
}
self.circleNode?.position = CGPoint(x: size.width / 2.0, y: size.height / 2.0)
}
}

View File

@ -73,6 +73,11 @@ final class OverlayAudioPlayerControllerImpl: ViewController, OverlayAudioPlayer
}
})
}
}, requestSearchByArtist: { [weak self] artist in
if let strongSelf = self {
strongSelf.context.sharedContext.openSearch(filter: .music, query: artist)
strongSelf.dismiss()
}
})
self.ready.set(self.controllerNode.ready.get())

View File

@ -20,6 +20,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
private let type: MediaManagerPlayerType
private let requestDismiss: () -> Void
private let requestShare: (MessageId) -> Void
private let requestSearchByArtist: (String) -> Void
private let playlistLocation: SharedMediaPlaylistLocation?
private let isGlobalSearch: Bool
@ -42,13 +43,14 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
private var presentationDataDisposable: Disposable?
private let replacementHistoryNodeReadyDisposable = MetaDisposable()
init(context: AccountContext, peerId: PeerId, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, playlistLocation: SharedMediaPlaylistLocation?, requestDismiss: @escaping () -> Void, requestShare: @escaping (MessageId) -> Void) {
init(context: AccountContext, peerId: PeerId, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, playlistLocation: SharedMediaPlaylistLocation?, requestDismiss: @escaping () -> Void, requestShare: @escaping (MessageId) -> Void, requestSearchByArtist: @escaping (String) -> Void) {
self.context = context
self.peerId = peerId
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.type = type
self.requestDismiss = requestDismiss
self.requestShare = requestShare
self.requestSearchByArtist = requestSearchByArtist
self.playlistLocation = playlistLocation
if case .regular = initialOrder {
@ -224,6 +226,10 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
self?.requestShare(messageId)
}
self.controlsNode.requestSearchByArtist = { [weak self] artist in
self?.requestSearchByArtist(artist)
}
self.controlsNode.updateOrder = { [weak self] order in
if let strongSelf = self {
let reversed: Bool

View File

@ -57,12 +57,13 @@ private func timestampLabelWidthForDuration(_ timestamp: Double) -> CGFloat {
return size.width
}
private let titleFont = Font.semibold(17.0)
private let descriptionFont = Font.regular(17.0)
private let titleFont = Font.semibold(18.0)
private let descriptionFont = Font.regular(18.0)
private func stringsForDisplayData(_ data: SharedMediaPlaybackDisplayData?, presentationData: PresentationData) -> (NSAttributedString?, NSAttributedString?) {
private func stringsForDisplayData(_ data: SharedMediaPlaybackDisplayData?, presentationData: PresentationData) -> (NSAttributedString?, NSAttributedString?, Bool) {
var titleString: NSAttributedString?
var descriptionString: NSAttributedString?
var hasArtist = false
if let data = data {
let titleText: String
@ -71,6 +72,7 @@ private func stringsForDisplayData(_ data: SharedMediaPlaybackDisplayData?, pres
case let .music(title, performer, _, _):
titleText = title ?? presentationData.strings.MediaPlayer_UnknownTrack
subtitleText = performer ?? presentationData.strings.MediaPlayer_UnknownArtist
hasArtist = performer != nil
case .voice, .instantVideo:
titleText = ""
subtitleText = ""
@ -80,7 +82,7 @@ private func stringsForDisplayData(_ data: SharedMediaPlaybackDisplayData?, pres
descriptionString = NSAttributedString(string: subtitleText, font: descriptionFont, textColor: presentationData.theme.list.itemSecondaryTextColor)
}
return (titleString, descriptionString)
return (titleString, descriptionString, hasArtist)
}
final class OverlayPlayerControlsNode: ASDisplayNode {
@ -97,6 +99,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
private let titleNode: TextNode
private let descriptionNode: TextNode
private let shareNode: HighlightableButtonNode
private let artistButton: HighlightTrackingButtonNode
private let scrubberNode: MediaPlayerScrubbingNode
private let leftDurationLabel: MediaPlayerTimeTextNode
@ -105,6 +108,10 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
private let backwardButton: IconButtonNode
private let forwardButton: IconButtonNode
private var seekTimer: SwiftSignalKit.Timer?
private var seekRate: AudioPlaybackRate = .x2
private var previousRate: AudioPlaybackRate?
private var currentIsPaused: Bool?
private let playPauseButton: IconButtonNode
@ -124,6 +131,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
var requestCollapse: (() -> Void)?
var requestShare: ((MessageId) -> Void)?
var requestSearchByArtist: ((String) -> Void)?
var updateOrder: ((MusicPlaybackSettingsOrder) -> Void)?
var control: ((SharedMediaPlayerControlAction) -> Void)?
@ -167,6 +175,8 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
self.descriptionNode.isUserInteractionEnabled = false
self.descriptionNode.displaysAsynchronously = false
self.artistButton = HighlightTrackingButtonNode()
self.shareNode = HighlightableButtonNode()
self.shareNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Share"), color: presentationData.theme.list.itemAccentColor), for: [])
@ -215,6 +225,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
self.addSubnode(self.albumArtNode)
self.addSubnode(self.titleNode)
self.addSubnode(self.descriptionNode)
self.addSubnode(self.artistButton)
self.addSubnode(self.shareNode)
self.addSubnode(self.leftDurationLabel)
@ -400,6 +411,23 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
self.forwardButton.addTarget(self, action: #selector(self.forwardPressed), forControlEvents: .touchUpInside)
self.playPauseButton.addTarget(self, action: #selector(self.playPausePressed), forControlEvents: .touchUpInside)
self.rateButton.addTarget(self, action: #selector(self.rateButtonPressed), forControlEvents: .touchUpInside)
self.artistButton.addTarget(self, action: #selector(self.artistPressed), forControlEvents: .touchUpInside)
self.artistButton.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if highlighted {
strongSelf.descriptionNode.layer.removeAnimation(forKey: "opacity")
strongSelf.descriptionNode.alpha = 0.4
} else {
strongSelf.descriptionNode.alpha = 1.0
strongSelf.descriptionNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
}
}
}
self.playPauseButton.circleColor = presentationData.theme.list.controlSecondaryColor.withAlphaComponent(0.35)
self.backwardButton.circleColor = presentationData.theme.list.controlSecondaryColor.withAlphaComponent(0.35)
self.forwardButton.circleColor = presentationData.theme.list.controlSecondaryColor.withAlphaComponent(0.35)
}
deinit {
@ -411,6 +439,80 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
super.didLoad()
self.albumArtNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.albumArtTap(_:))))
let backwardLongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.seekForwardLongPress(_:)))
backwardLongPressGestureRecognizer.minimumPressDuration = 0.3
self.backwardButton.view.addGestureRecognizer(backwardLongPressGestureRecognizer)
let forwardLongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.seekForwardLongPress(_:)))
forwardLongPressGestureRecognizer.minimumPressDuration = 0.3
self.forwardButton.view.addGestureRecognizer(forwardLongPressGestureRecognizer)
}
@objc private func seekBackwardLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) {
switch gestureRecognizer.state {
case .began:
self.backwardButton.isPressing = true
self.previousRate = self.currentRate
self.seekRate = .x4
self.control?(.setBaseRate(self.seekRate))
let seekTimer = SwiftSignalKit.Timer(timeout: 2.0, repeat: true, completion: { [weak self] in
if let strongSelf = self {
if strongSelf.seekRate == .x4 {
strongSelf.seekRate = .x8
} else if strongSelf.seekRate == .x8 {
strongSelf.seekRate = .x16
}
strongSelf.control?(.setBaseRate(strongSelf.seekRate))
if strongSelf.seekRate == .x16 {
strongSelf.seekTimer?.invalidate()
strongSelf.seekTimer = nil
}
}
}, queue: Queue.mainQueue())
self.seekTimer = seekTimer
seekTimer.start()
case .ended, .cancelled:
self.backwardButton.isPressing = false
self.control?(.setBaseRate(self.previousRate ?? .x1))
self.seekTimer?.invalidate()
self.seekTimer = nil
default:
break
}
}
@objc private func seekForwardLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) {
switch gestureRecognizer.state {
case .began:
self.forwardButton.isPressing = true
self.previousRate = self.currentRate
self.seekRate = .x4
self.control?(.setBaseRate(self.seekRate))
let seekTimer = SwiftSignalKit.Timer(timeout: 2.0, repeat: true, completion: { [weak self] in
if let strongSelf = self {
if strongSelf.seekRate == .x4 {
strongSelf.seekRate = .x8
} else if strongSelf.seekRate == .x8 {
strongSelf.seekRate = .x16
}
strongSelf.control?(.setBaseRate(strongSelf.seekRate))
if strongSelf.seekRate == .x16 {
strongSelf.seekTimer?.invalidate()
strongSelf.seekTimer = nil
}
}
}, queue: Queue.mainQueue())
self.seekTimer = seekTimer
seekTimer.start()
case .ended, .cancelled:
self.forwardButton.isPressing = false
self.control?(.setBaseRate(self.previousRate ?? .x1))
self.seekTimer?.invalidate()
self.seekTimer = nil
default:
break
}
}
func updatePresentationData(_ presentationData: PresentationData) {
@ -419,6 +521,10 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
}
self.presentationData = presentationData
self.playPauseButton.circleColor = presentationData.theme.list.controlSecondaryColor.withAlphaComponent(0.35)
self.backwardButton.circleColor = presentationData.theme.list.controlSecondaryColor.withAlphaComponent(0.35)
self.forwardButton.circleColor = presentationData.theme.list.controlSecondaryColor.withAlphaComponent(0.35)
self.backgroundNode.image = generateBackground(theme: presentationData.theme)
self.collapseNode.setImage(generateCollapseIcon(theme: presentationData.theme), for: [])
self.shareNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Share"), color: presentationData.theme.list.itemAccentColor), for: [])
@ -456,7 +562,8 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
let infoVerticalOrigin: CGFloat = panelHeight - OverlayPlayerControlsNode.basePanelHeight + 36.0
let (titleString, descriptionString) = stringsForDisplayData(self.displayData, presentationData: self.presentationData)
let (titleString, descriptionString, hasArtist) = stringsForDisplayData(self.displayData, presentationData: self.presentationData)
self.artistButton.isUserInteractionEnabled = hasArtist
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - sideInset * 2.0 - leftInset - rightInset - infoLabelsLeftInset - infoLabelsRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .left, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets()))
let makeDescriptionLayout = TextNode.asyncLayout(self.descriptionNode)
@ -465,9 +572,12 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: self.isExpanded ? floor((width - titleLayout.size.width) / 2.0) : (leftInset + sideInset + infoLabelsLeftInset), y: infoVerticalOrigin + 1.0), size: titleLayout.size))
let _ = titleApply()
transition.updateFrame(node: self.descriptionNode, frame: CGRect(origin: CGPoint(x: self.isExpanded ? floor((width - descriptionLayout.size.width) / 2.0) : (leftInset + sideInset + infoLabelsLeftInset), y: infoVerticalOrigin + 26.0), size: descriptionLayout.size))
let descriptionFrame = CGRect(origin: CGPoint(x: self.isExpanded ? floor((width - descriptionLayout.size.width) / 2.0) : (leftInset + sideInset + infoLabelsLeftInset), y: infoVerticalOrigin + 24.0), size: descriptionLayout.size)
transition.updateFrame(node: self.descriptionNode, frame: descriptionFrame)
let _ = descriptionApply()
self.artistButton.frame = descriptionFrame.insetBy(dx: -8.0, dy: -8.0)
var albumArt: SharedMediaPlaybackAlbumArt?
if let displayData = self.displayData {
switch displayData {
@ -753,6 +863,13 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
}
}
@objc func artistPressed() {
let (_, descriptionString, _) = stringsForDisplayData(self.displayData, presentationData: self.presentationData)
if let artist = descriptionString?.string {
self.requestSearchByArtist?(artist)
}
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event)
if result == self.view {

View File

@ -219,6 +219,8 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
nextRate = .x2
case .x2:
nextRate = .x1
default:
nextRate = .x1
}
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in
return settings.withUpdatedVoicePlaybackRate(nextRate)

View File

@ -950,6 +950,12 @@ public final class SharedAccountContextImpl: SharedAccountContext {
})
}
public func openSearch(filter: ChatListSearchFilter, query: String?) {
if let rootController = self.mainWindow?.viewController as? TelegramRootController {
rootController.openChatsController(activateSearch: true, filter: filter, query: query)
}
}
public func navigateToChat(accountId: AccountRecordId, peerId: PeerId, messageId: MessageId?) {
self.navigateToChatImpl(accountId, peerId, messageId)
}

View File

@ -227,6 +227,8 @@ final class SharedMediaPlayer {
rateValue = 1.0
case .x2:
rateValue = 1.8
default:
rateValue = 1.0
}
}
@ -446,6 +448,12 @@ final class SharedMediaPlayer {
rateValue = 1.0
case .x2:
rateValue = 1.8
case .x4:
rateValue = 4.0
case .x8:
rateValue = 8.0
case .x16:
rateValue = 16.0
}
switch playbackItem {
case let .audio(player):

View File

@ -144,7 +144,7 @@ public final class TelegramRootController: NavigationController {
rootTabController.setControllers(controllers, selectedIndex: nil)
}
public func openChatsController(activateSearch: Bool) {
public func openChatsController(activateSearch: Bool, filter: ChatListSearchFilter = .chats, query: String? = nil) {
guard let rootTabController = self.rootTabController else {
return
}
@ -158,7 +158,7 @@ public final class TelegramRootController: NavigationController {
}
if activateSearch {
self.chatListController?.activateSearch()
self.chatListController?.activateSearch(filter: filter, query: query)
}
}

View File

@ -17,6 +17,9 @@ public enum MusicPlaybackSettingsLooping: Int32 {
public enum AudioPlaybackRate: Int32 {
case x1 = 1000
case x2 = 2000
case x4 = 4000
case x8 = 8000
case x16 = 16000
var doubleValue: Double {
return Double(self.rawValue) / 1000.0