mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Update settings screen
This commit is contained in:
parent
76a340cff8
commit
37895d675b
@ -4134,7 +4134,7 @@ Unused sets are archived when you add more.";
|
||||
"Chat.DeleteMessagesConfirmation_1" = "Delete message";
|
||||
"Chat.DeleteMessagesConfirmation_any" = "Delete %@ messages";
|
||||
|
||||
"Settings.Search" = "Search";
|
||||
"Settings.Search" = "Search Settings";
|
||||
|
||||
"SettingsSearch.FAQ" = "FAQ";
|
||||
|
||||
@ -5633,8 +5633,8 @@ Any member of this group will be able to see messages in the channel.";
|
||||
|
||||
"Call.RemoteVideoPaused" = "%@'s video paused";
|
||||
|
||||
"Settings.SetProfilePhotoOrVideo" = "Set Profile Photo or Video";
|
||||
"Settings.SetNewProfilePhotoOrVideo" = "Set New Profile Photo or Video";
|
||||
"Settings.SetProfilePhotoOrVideo" = "Set Photo or Video";
|
||||
"Settings.SetNewProfilePhotoOrVideo" = "Set New Photo or Video";
|
||||
"Settings.ViewVideo" = "View Video";
|
||||
"Settings.RemoveVideo" = "Remove Video";
|
||||
|
||||
@ -5669,3 +5669,15 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"Stats.GroupShowMoreTopInviters_3_10" = "Show %@ More";
|
||||
"Stats.GroupShowMoreTopInviters_many" = "Show %@ More";
|
||||
"Stats.GroupShowMoreTopInviters_any" = "Show %@ More";
|
||||
|
||||
"Settings.AddAnotherAccount" = "Add Another Account";
|
||||
"Settings.AddAnotherAccount.Help" = "You can add up to three accounts with different phone numbers.";
|
||||
|
||||
"ProfilePhoto.OpenGallery" = "Open Gallery";
|
||||
"ProfilePhoto.SearchWeb" = "Search Web";
|
||||
"ProfilePhoto.OpenInEditor" = "Open in Editor";
|
||||
|
||||
"Settings.EditAccount" = "Edit Account";
|
||||
"Settings.EditPhoto" = "Edit Photo";
|
||||
|
||||
"Settings.FrequentlyAskedQuestions" = "Frequently Asked Questions";
|
||||
|
@ -21,6 +21,7 @@ public enum ChatListSearchItemHeaderType: Int32 {
|
||||
case nearbyVenues
|
||||
case chats
|
||||
case chatTypes
|
||||
case faq
|
||||
}
|
||||
|
||||
public final class ChatListSearchItemHeader: ListViewItemHeader {
|
||||
@ -107,6 +108,8 @@ public final class ChatListSearchItemHeaderNode: ListViewItemHeaderNode {
|
||||
self.sectionHeaderNode.title = strings.Cache_ByPeerHeader.uppercased()
|
||||
case .chatTypes:
|
||||
self.sectionHeaderNode.title = strings.ChatList_ChatTypesSection.uppercased()
|
||||
case .faq:
|
||||
self.sectionHeaderNode.title = strings.Settings_FrequentlyAskedQuestions.uppercased()
|
||||
}
|
||||
|
||||
self.sectionHeaderNode.action = actionTitle
|
||||
@ -157,6 +160,8 @@ public final class ChatListSearchItemHeaderNode: ListViewItemHeaderNode {
|
||||
self.sectionHeaderNode.title = strings.Cache_ByPeerHeader.uppercased()
|
||||
case .chatTypes:
|
||||
self.sectionHeaderNode.title = strings.ChatList_ChatTypesSection.uppercased()
|
||||
case .faq:
|
||||
self.sectionHeaderNode.title = strings.Settings_FrequentlyAskedQuestions.uppercased()
|
||||
}
|
||||
|
||||
self.sectionHeaderNode.action = actionTitle
|
||||
|
@ -274,6 +274,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
private final var itemNodes: [ListViewItemNode] = []
|
||||
private final var itemHeaderNodes: [Int64: ListViewItemHeaderNode] = [:]
|
||||
|
||||
public final var itemHeaderNodesAlpha: CGFloat = 1.0
|
||||
|
||||
public final var displayedItemRangeChanged: (ListViewDisplayedItemRange, Any?) -> Void = { _, _ in }
|
||||
public private(set) final var displayedItemRange: ListViewDisplayedItemRange = ListViewDisplayedItemRange(loadedRange: nil, visibleRange: nil)
|
||||
|
||||
@ -3215,6 +3217,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
stickLocationDistanceFactor = max(0.0, min(1.0, stickLocationDistance / itemHeaderHeight))
|
||||
}
|
||||
visibleHeaderNodes.insert(id)
|
||||
|
||||
let initialHeaderNodeAlpha = self.itemHeaderNodesAlpha
|
||||
if let headerNode = self.itemHeaderNodes[id] {
|
||||
switch transition.0 {
|
||||
case .immediate:
|
||||
@ -3252,15 +3256,16 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
headerNode.animateRemoved(duration: 0.2)
|
||||
}
|
||||
} else if hasValidNodes && headerNode.alpha.isZero {
|
||||
headerNode.alpha = 1.0
|
||||
headerNode.alpha = initialHeaderNodeAlpha
|
||||
if animateInsertion {
|
||||
headerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
headerNode.layer.animateAlpha(from: 0.0, to: initialHeaderNodeAlpha, duration: 0.2)
|
||||
headerNode.layer.animateScale(from: 0.2, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
headerNode.updateStickDistanceFactor(stickLocationDistanceFactor, transition: transition.0)
|
||||
} else {
|
||||
let headerNode = item.node()
|
||||
headerNode.alpha = initialHeaderNodeAlpha
|
||||
if headerNode.item !== item {
|
||||
item.updateNode(headerNode, previous: nil, next: nil)
|
||||
headerNode.item = item
|
||||
@ -3276,7 +3281,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
self.addSubnode(headerNode)
|
||||
}
|
||||
if animateInsertion {
|
||||
headerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
headerNode.layer.animateAlpha(from: 0.0, to: initialHeaderNodeAlpha, duration: 0.3)
|
||||
headerNode.layer.animateScale(from: 0.2, to: 1.0, duration: 0.3)
|
||||
}
|
||||
headerNode.updateStickDistanceFactor(stickLocationDistanceFactor, transition: .immediate)
|
||||
|
@ -107,7 +107,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
return 38.0
|
||||
}
|
||||
|
||||
private var presentationData: NavigationBarPresentationData
|
||||
var presentationData: NavigationBarPresentationData
|
||||
|
||||
private var validLayout: (CGSize, CGFloat, CGFloat, CGFloat, CGFloat, Bool)?
|
||||
private var requestedLayout: Bool = false
|
||||
|
@ -311,6 +311,9 @@ open class TabBarController: ViewController {
|
||||
self.navigationBar?.setSecondaryContentNode(currentController.navigationBar?.secondaryContentNode)
|
||||
currentController.displayNode.recursivelyEnsureDisplaySynchronously(true)
|
||||
self.statusBar.statusBarStyle = currentController.statusBar.statusBarStyle
|
||||
if let navigationBarPresentationData = currentController.navigationBar?.presentationData {
|
||||
self.navigationBar?.updatePresentationData(navigationBarPresentationData)
|
||||
}
|
||||
} else {
|
||||
self.navigationItem.title = nil
|
||||
self.navigationItem.leftBarButtonItem = nil
|
||||
|
@ -487,6 +487,32 @@ public enum TabBarItemContextActionType {
|
||||
}
|
||||
}
|
||||
|
||||
public func setNavigationBarPresentationData(_ presentationData: NavigationBarPresentationData, animated: Bool) {
|
||||
if animated, let navigationBar = self.navigationBar {
|
||||
UIView.transition(with: navigationBar.view, duration: 0.3, options: [.transitionCrossDissolve], animations: {
|
||||
}, completion: nil)
|
||||
}
|
||||
self.navigationBar?.updatePresentationData(presentationData)
|
||||
if let parent = self.parent as? TabBarController {
|
||||
if parent.currentController === self {
|
||||
if animated, let navigationBar = parent.navigationBar {
|
||||
UIView.transition(with: navigationBar.view, duration: 0.3, options: [.transitionCrossDissolve], animations: {
|
||||
}, completion: nil)
|
||||
}
|
||||
parent.navigationBar?.updatePresentationData(presentationData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func setStatusBarStyle(_ style: StatusBarStyle, animated: Bool) {
|
||||
self.statusBar.updateStatusBarStyle(style, animated: animated)
|
||||
if let parent = self.parent as? TabBarController {
|
||||
if parent.currentController === self {
|
||||
parent.statusBar.updateStatusBarStyle(style, animated: animated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
self.view.window?.rootViewController?.present(viewControllerToPresent, animated: flag, completion: completion)
|
||||
}
|
||||
|
@ -49,9 +49,10 @@ public class ItemListMultilineInputItem: ListViewItem, ItemListItem {
|
||||
let maxLength: ItemListMultilineInputItemTextLimit?
|
||||
let minimalHeight: CGFloat?
|
||||
let inlineAction: ItemListMultilineInputInlineAction?
|
||||
let noInsets: Bool
|
||||
public let tag: ItemListItemTag?
|
||||
|
||||
public init(presentationData: ItemListPresentationData, text: String, placeholder: String, maxLength: ItemListMultilineInputItemTextLimit?, sectionId: ItemListSectionId, style: ItemListStyle, capitalization: Bool = true, autocorrection: Bool = true, returnKeyType: UIReturnKeyType = .default, minimalHeight: CGFloat? = nil, textUpdated: @escaping (String) -> Void, shouldUpdateText: @escaping (String) -> Bool = { _ in return true }, processPaste: ((String) -> Void)? = nil, updatedFocus: ((Bool) -> Void)? = nil, tag: ItemListItemTag? = nil, action: (() -> Void)? = nil, inlineAction: ItemListMultilineInputInlineAction? = nil) {
|
||||
public init(presentationData: ItemListPresentationData, text: String, placeholder: String, maxLength: ItemListMultilineInputItemTextLimit?, sectionId: ItemListSectionId, style: ItemListStyle, capitalization: Bool = true, autocorrection: Bool = true, returnKeyType: UIReturnKeyType = .default, minimalHeight: CGFloat? = nil, textUpdated: @escaping (String) -> Void, shouldUpdateText: @escaping (String) -> Bool = { _ in return true }, processPaste: ((String) -> Void)? = nil, updatedFocus: ((Bool) -> Void)? = nil, tag: ItemListItemTag? = nil, action: (() -> Void)? = nil, inlineAction: ItemListMultilineInputInlineAction? = nil, noInsets: Bool = false) {
|
||||
self.presentationData = presentationData
|
||||
self.text = text
|
||||
self.placeholder = placeholder
|
||||
@ -69,6 +70,7 @@ public class ItemListMultilineInputItem: ListViewItem, ItemListItem {
|
||||
self.tag = tag
|
||||
self.action = action
|
||||
self.inlineAction = inlineAction
|
||||
self.noInsets = noInsets
|
||||
}
|
||||
|
||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
@ -241,7 +243,7 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod
|
||||
}
|
||||
|
||||
let contentSize = CGSize(width: params.width, height: contentHeight)
|
||||
let insets = itemListNeighborsGroupedInsets(neighbors)
|
||||
let insets = item.noInsets ? UIEdgeInsets() : itemListNeighborsGroupedInsets(neighbors)
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||
let layoutSize = layout.size
|
||||
@ -325,8 +327,10 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod
|
||||
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||
if !item.noInsets {
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||
}
|
||||
|
||||
if strongSelf.textNode.attributedPlaceholderText == nil || !strongSelf.textNode.attributedPlaceholderText!.isEqual(to: attributedPlaceholderText) {
|
||||
strongSelf.textNode.attributedPlaceholderText = attributedPlaceholderText
|
||||
|
12
submodules/LegacyComponents/LegacyImages.xcassets/Editor/Camera.imageset/Contents.json
vendored
Normal file
12
submodules/LegacyComponents/LegacyImages.xcassets/Editor/Camera.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_camera.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/LegacyComponents/LegacyImages.xcassets/Editor/Camera.imageset/ic_camera.pdf
vendored
Normal file
BIN
submodules/LegacyComponents/LegacyImages.xcassets/Editor/Camera.imageset/ic_camera.pdf
vendored
Normal file
Binary file not shown.
@ -49,7 +49,7 @@
|
||||
[_wrapperView addSubview:_previewView];
|
||||
[camera attachPreviewView:_previewView];
|
||||
|
||||
_iconView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"AttachmentMenuInteractiveCameraIcon")];
|
||||
_iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Editor/Camera"]];
|
||||
[self addSubview:_iconView];
|
||||
|
||||
[self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesture:)]];
|
||||
|
@ -13,16 +13,13 @@ NSString *const TGAttachmentGifCellIdentifier = @"AttachmentGifCell";
|
||||
{
|
||||
_typeLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||
_typeLabel.backgroundColor = [UIColor clearColor];
|
||||
_typeLabel.font = TGSystemFontOfSize(12);
|
||||
_typeLabel.font = TGBoldSystemFontOfSize(13);
|
||||
_typeLabel.textColor = [UIColor whiteColor];
|
||||
_typeLabel.text = @"GIF";
|
||||
[self addSubview:_typeLabel];
|
||||
|
||||
[_typeLabel sizeToFit];
|
||||
|
||||
CGSize typeSize = CGSizeMake(ceil(_typeLabel.frame.size.width), ceil(_typeLabel.frame.size.height));
|
||||
_typeLabel.frame = CGRectMake(4, self.frame.size.height - typeSize.height - 2, typeSize.width, typeSize.height);
|
||||
|
||||
_gradientView.hidden = false;
|
||||
|
||||
[self bringSubviewToFront:_cornersView];
|
||||
@ -33,7 +30,9 @@ NSString *const TGAttachmentGifCellIdentifier = @"AttachmentGifCell";
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
_typeLabel.frame = CGRectMake(4, self.frame.size.height - _typeLabel.frame.size.height - 2, _typeLabel.frame.size.width, _typeLabel.frame.size.height);
|
||||
|
||||
CGSize typeSize = _typeLabel.frame.size;
|
||||
_typeLabel.frame = CGRectMake(self.frame.size.width - typeSize.width - 3.0, self.frame.size.height - typeSize.height - 1.0, typeSize.width, typeSize.height);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -27,13 +27,9 @@ NSString *const TGAttachmentVideoCellIdentifier = @"AttachmentVideoCell";
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil)
|
||||
{
|
||||
_iconView = [[UIImageView alloc] init];
|
||||
_iconView.contentMode = UIViewContentModeCenter;
|
||||
[self addSubview:_iconView];
|
||||
|
||||
_durationLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||
_durationLabel.backgroundColor = [UIColor clearColor];
|
||||
_durationLabel.font = TGSystemFontOfSize(12);
|
||||
_durationLabel.font = TGBoldSystemFontOfSize(13);
|
||||
_durationLabel.textColor = [UIColor whiteColor];
|
||||
[self addSubview:_durationLabel];
|
||||
|
||||
@ -44,10 +40,7 @@ NSString *const TGAttachmentVideoCellIdentifier = @"AttachmentVideoCell";
|
||||
_adjustmentsDisposable = [[SMetaDisposable alloc] init];
|
||||
|
||||
if (iosMajorVersion() >= 11)
|
||||
{
|
||||
_iconView.accessibilityIgnoresInvertColors = true;
|
||||
_durationLabel.accessibilityIgnoresInvertColors = true;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -67,13 +60,6 @@ NSString *const TGAttachmentVideoCellIdentifier = @"AttachmentVideoCell";
|
||||
durationFrame.size = CGSizeMake(ceil(_durationLabel.frame.size.width), ceil(_durationLabel.frame.size.height));
|
||||
_durationLabel.frame = durationFrame;
|
||||
|
||||
if (asset.subtypes & TGMediaAssetSubtypeVideoTimelapse)
|
||||
_iconView.image = TGComponentsImageNamed(@"ModernMediaItemTimelapseIcon");
|
||||
else if (asset.subtypes & TGMediaAssetSubtypeVideoHighFrameRate)
|
||||
_iconView.image = TGComponentsImageNamed(@"ModernMediaItemSloMoIcon");
|
||||
else
|
||||
_iconView.image = TGComponentsImageNamed(@"ModernMediaItemVideoIcon");
|
||||
|
||||
SSignal *adjustmentsSignal = [self.editingContext adjustmentsSignalForItem:self.asset];
|
||||
|
||||
__weak TGAttachmentVideoCell *weakSelf = self;
|
||||
@ -155,11 +141,9 @@ NSString *const TGAttachmentVideoCellIdentifier = @"AttachmentVideoCell";
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
_iconView.frame = CGRectMake(0, self.frame.size.height - 19, 19, 19);
|
||||
|
||||
|
||||
CGSize durationSize = _durationLabel.frame.size;
|
||||
_durationLabel.frame = CGRectMake(self.frame.size.width - durationSize.width - 4, self.frame.size.height - durationSize.height - 2, durationSize.width, durationSize.height);
|
||||
_durationLabel.frame = CGRectMake(self.frame.size.width - durationSize.width - 3.0, self.frame.size.height - durationSize.height - 1.0, durationSize.width, durationSize.height);
|
||||
}
|
||||
|
||||
- (UIImage *)transitionImageSquared
|
||||
|
@ -143,7 +143,7 @@
|
||||
};
|
||||
[itemViews addObject:carouselItem];
|
||||
|
||||
TGMenuSheetButtonItemView *galleryItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:_signup ? TGLocalized(@"Common.ChoosePhoto") : TGLocalized(@"AttachmentMenu.PhotoOrVideo") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
|
||||
TGMenuSheetButtonItemView *galleryItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"ProfilePhoto.OpenGallery") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
|
||||
{
|
||||
__strong TGMediaAvatarMenuMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
@ -160,7 +160,7 @@
|
||||
|
||||
if (_hasSearchButton)
|
||||
{
|
||||
TGMenuSheetButtonItemView *viewItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"AttachmentMenu.WebSearch") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
|
||||
TGMenuSheetButtonItemView *viewItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"ProfilePhoto.SearchWeb") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
|
||||
{
|
||||
__strong TGMediaAvatarMenuMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
|
@ -552,7 +552,7 @@
|
||||
if (strongSelf->_dismissed)
|
||||
return;
|
||||
|
||||
[strongSelf setProgressVisible:progressVisible value:progress animated:true];
|
||||
[strongSelf setProgressVisible:progressVisible value:progress animated:progressVisible];
|
||||
[strongSelf updateDoneButtonEnabled:doneEnabled animated:true];
|
||||
if (progressVisible)
|
||||
strongSelf->_hadProgress = true;
|
||||
|
@ -28,7 +28,7 @@ private func extractAnchor(string: String) -> (String, String?) {
|
||||
|
||||
private let refreshTimeout: Int32 = 60 * 60 * 12
|
||||
|
||||
func cachedFaqInstantPage(context: AccountContext) -> Signal<ResolvedUrl, NoError> {
|
||||
public func cachedFaqInstantPage(context: AccountContext) -> Signal<ResolvedUrl, NoError> {
|
||||
var faqUrl = context.sharedContext.currentPresentationData.with { $0 }.strings.Settings_FAQ_URL
|
||||
if faqUrl == "Settings.FAQ_URL" || faqUrl.isEmpty {
|
||||
faqUrl = "https://telegram.org/faq#general-questions"
|
||||
@ -71,7 +71,7 @@ func cachedFaqInstantPage(context: AccountContext) -> Signal<ResolvedUrl, NoErro
|
||||
}
|
||||
}
|
||||
|
||||
func faqSearchableItems(context: AccountContext) -> Signal<[SettingsSearchableItem], NoError> {
|
||||
func faqSearchableItems(context: AccountContext, suggestAccountDeletion: Bool) -> Signal<[SettingsSearchableItem], NoError> {
|
||||
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
|
||||
return cachedFaqInstantPage(context: context)
|
||||
|> map { resolvedUrl -> [SettingsSearchableItem] in
|
||||
@ -105,7 +105,7 @@ func faqSearchableItems(context: AccountContext) -> Signal<[SettingsSearchableIt
|
||||
if case let .text(itemText, _) = item, case let .url(text, url, _) = itemText {
|
||||
let (_, anchor) = extractAnchor(string: url)
|
||||
var index = nextIndex
|
||||
if anchor?.contains("delete-my-account") ?? false {
|
||||
if suggestAccountDeletion && (anchor?.contains("delete-my-account") ?? false) {
|
||||
index = 1
|
||||
} else {
|
||||
nextIndex += 1
|
||||
|
@ -84,13 +84,13 @@ private final class ChangePhoneNumberIntroControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
final class ChangePhoneNumberIntroController: ViewController {
|
||||
public final class ChangePhoneNumberIntroController: ViewController {
|
||||
private let context: AccountContext
|
||||
private var didPlayPresentationAnimation = false
|
||||
|
||||
private var presentationData: PresentationData
|
||||
|
||||
init(context: AccountContext, phoneNumber: String) {
|
||||
public init(context: AccountContext, phoneNumber: String) {
|
||||
self.context = context
|
||||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
@ -110,7 +110,7 @@ final class ChangePhoneNumberIntroController: ViewController {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func loadDisplayNode() {
|
||||
public override func loadDisplayNode() {
|
||||
self.displayNode = ChangePhoneNumberIntroControllerNode(presentationData: self.presentationData)
|
||||
(self.displayNode as! ChangePhoneNumberIntroControllerNode).dismiss = { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
@ -121,7 +121,7 @@ final class ChangePhoneNumberIntroController: ViewController {
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
public override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
/*if !self.didPlayPresentationAnimation {
|
||||
|
@ -57,14 +57,14 @@ private enum DataAndStorageSection: Int32 {
|
||||
case enableSensitiveContent
|
||||
}
|
||||
|
||||
enum DataAndStorageEntryTag: ItemListItemTag {
|
||||
public enum DataAndStorageEntryTag: ItemListItemTag {
|
||||
case automaticDownloadReset
|
||||
case autoplayGifs
|
||||
case autoplayVideos
|
||||
case saveEditedPhotos
|
||||
case downloadInBackground
|
||||
|
||||
func isEqual(to other: ItemListItemTag) -> Bool {
|
||||
public func isEqual(to other: ItemListItemTag) -> Bool {
|
||||
if let other = other as? DataAndStorageEntryTag, self == other {
|
||||
return true
|
||||
} else {
|
||||
@ -518,7 +518,7 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat
|
||||
return entries
|
||||
}
|
||||
|
||||
func dataAndStorageController(context: AccountContext, focusOnItemTag: DataAndStorageEntryTag? = nil) -> ViewController {
|
||||
public func dataAndStorageController(context: AccountContext, focusOnItemTag: DataAndStorageEntryTag? = nil) -> ViewController {
|
||||
let initialState = DataAndStorageControllerState()
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
|
||||
|
@ -129,7 +129,7 @@ private func logoutOptionsEntries(presentationData: PresentationData, canAddAcco
|
||||
return entries
|
||||
}
|
||||
|
||||
func logoutOptionsController(context: AccountContext, navigationController: NavigationController, canAddAccounts: Bool, phoneNumber: String) -> ViewController {
|
||||
public func logoutOptionsController(context: AccountContext, navigationController: NavigationController, canAddAccounts: Bool, phoneNumber: String) -> ViewController {
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
var replaceTopControllerImpl: ((ViewController) -> Void)?
|
||||
@ -250,6 +250,7 @@ func logoutOptionsController(context: AccountContext, navigationController: Navi
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal, tabBarItem: nil)
|
||||
controller.navigationPresentation = .modal
|
||||
pushControllerImpl = { [weak navigationController] value in
|
||||
navigationController?.pushViewController(value, animated: false)
|
||||
}
|
||||
|
@ -258,19 +258,33 @@ private enum SettingsSearchRecentEntryStableId: Hashable {
|
||||
}
|
||||
|
||||
private enum SettingsSearchRecentEntry: Comparable, Identifiable {
|
||||
case recent(Int, SettingsSearchableItem)
|
||||
case recent(Int, SettingsSearchableItem, ChatListSearchItemHeader)
|
||||
case faq(Int, SettingsSearchableItem, ChatListSearchItemHeader)
|
||||
|
||||
var stableId: SettingsSearchRecentEntryStableId {
|
||||
switch self {
|
||||
case let .recent(_, item):
|
||||
case let .recent(_, item, _), let .faq(_, item, _):
|
||||
return .recent(item.id)
|
||||
}
|
||||
}
|
||||
|
||||
var header: ChatListSearchItemHeader {
|
||||
switch self {
|
||||
case let .recent(_, _, header), let .faq(_, _, header):
|
||||
return header
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: SettingsSearchRecentEntry, rhs: SettingsSearchRecentEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .recent(lhsIndex, lhsItem):
|
||||
if case let .recent(rhsIndex, rhsItem) = rhs, lhsIndex == rhsIndex, lhsItem.id == rhsItem.id {
|
||||
case let .recent(lhsIndex, lhsItem, lhsHeader):
|
||||
if case let .recent(rhsIndex, rhsItem, rhsHeader) = rhs, lhsIndex == rhsIndex, lhsItem.id == rhsItem.id, lhsHeader.id == rhsHeader.id {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .faq(lhsIndex, lhsItem, lhsHeader):
|
||||
if case let .faq(rhsIndex, rhsItem, rhsHeader) = rhs, lhsIndex == rhsIndex, lhsItem.id == rhsItem.id, lhsHeader.id == rhsHeader.id {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -280,17 +294,26 @@ private enum SettingsSearchRecentEntry: Comparable, Identifiable {
|
||||
|
||||
static func <(lhs: SettingsSearchRecentEntry, rhs: SettingsSearchRecentEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .recent(lhsIndex, _):
|
||||
case let .recent(lhsIndex, _, _):
|
||||
switch rhs {
|
||||
case let .recent(rhsIndex, _):
|
||||
case let .recent(rhsIndex, _, _):
|
||||
return lhsIndex <= rhsIndex
|
||||
case .faq:
|
||||
return false
|
||||
}
|
||||
case let .faq(lhsIndex, _, _):
|
||||
switch rhs {
|
||||
case .recent:
|
||||
return true
|
||||
case let .faq(rhsIndex, _, _):
|
||||
return lhsIndex <= rhsIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, interaction: SettingsSearchInteraction, header: ListViewItemHeader) -> ListViewItem {
|
||||
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, interaction: SettingsSearchInteraction) -> ListViewItem {
|
||||
switch self {
|
||||
case let .recent(_, item):
|
||||
case let .recent(_, item, header), let .faq(_, item, header):
|
||||
return SettingsSearchRecentItem(account: account, theme: theme, strings: strings, title: item.title, breadcrumbs: item.breadcrumbs, action: {
|
||||
interaction.openItem(item)
|
||||
}, deleted: {
|
||||
@ -307,18 +330,18 @@ private struct SettingsSearchContainerRecentTransition {
|
||||
let isEmpty: Bool
|
||||
}
|
||||
|
||||
private func preparedSettingsSearchContainerRecentTransition(from fromEntries: [SettingsSearchRecentEntry], to toEntries: [SettingsSearchRecentEntry], account: Account, theme: PresentationTheme, strings: PresentationStrings, interaction: SettingsSearchInteraction, header: ListViewItemHeader) -> SettingsSearchContainerRecentTransition {
|
||||
private func preparedSettingsSearchContainerRecentTransition(from fromEntries: [SettingsSearchRecentEntry], to toEntries: [SettingsSearchRecentEntry], account: Account, theme: PresentationTheme, strings: PresentationStrings, interaction: SettingsSearchInteraction) -> SettingsSearchContainerRecentTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction, header: header), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction, header: header), directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction), directionHint: nil) }
|
||||
|
||||
return SettingsSearchContainerRecentTransition(deletions: deletions, insertions: insertions, updates: updates, isEmpty: toEntries.isEmpty)
|
||||
}
|
||||
|
||||
|
||||
private final class SettingsSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
public final class SettingsSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
private let listNode: ListView
|
||||
private let recentListNode: ListView
|
||||
|
||||
@ -335,7 +358,7 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
|
||||
private var presentationDataDisposable: Disposable?
|
||||
private let presentationDataPromise: Promise<PresentationData>
|
||||
|
||||
init(context: AccountContext, openResult: @escaping (SettingsSearchableItem) -> Void, exceptionsList: Signal<NotificationExceptionsList?, NoError>, archivedStickerPacks: Signal<[ArchivedStickerPackItem]?, NoError>, privacySettings: Signal<AccountPrivacySettings?, NoError>, hasWallet: Signal<Bool, NoError>, activeSessionsContext: Signal<ActiveSessionsContext?, NoError>, webSessionsContext: Signal<WebSessionsContext?, NoError>) {
|
||||
public init(context: AccountContext, openResult: @escaping (SettingsSearchableItem) -> Void, exceptionsList: Signal<NotificationExceptionsList?, NoError>, archivedStickerPacks: Signal<[ArchivedStickerPackItem]?, NoError>, privacySettings: Signal<AccountPrivacySettings?, NoError>, hasWallet: Signal<Bool, NoError>, activeSessionsContext: Signal<ActiveSessionsContext?, NoError>, webSessionsContext: Signal<WebSessionsContext?, NoError>) {
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.presentationDataPromise = Promise(self.presentationData)
|
||||
|
||||
@ -354,14 +377,20 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
|
||||
self.addSubnode(self.recentListNode)
|
||||
self.addSubnode(self.listNode)
|
||||
|
||||
let interaction = SettingsSearchInteraction(openItem: openResult, deleteRecentItem: { id in
|
||||
let interaction = SettingsSearchInteraction(openItem: { result in
|
||||
addRecentSettingsSearchItem(postbox: context.account.postbox, item: result.id)
|
||||
openResult(result)
|
||||
}, deleteRecentItem: { id in
|
||||
removeRecentSettingsSearchItem(postbox: context.account.postbox, item: id)
|
||||
})
|
||||
|
||||
let searchableItems = Promise<[SettingsSearchableItem]>()
|
||||
searchableItems.set(settingsSearchableItems(context: context, notificationExceptionsList: exceptionsList, archivedStickerPacks: archivedStickerPacks, privacySettings: privacySettings, hasWallet: hasWallet, activeSessionsContext: activeSessionsContext, webSessionsContext: webSessionsContext))
|
||||
|
||||
let queryAndFoundItems = combineLatest(searchableItems.get(), faqSearchableItems(context: context))
|
||||
let faqItems = Promise<[SettingsSearchableItem]>()
|
||||
faqItems.set(faqSearchableItems(context: context, suggestAccountDeletion: false))
|
||||
|
||||
let queryAndFoundItems = combineLatest(searchableItems.get(), faqSearchableItems(context: context, suggestAccountDeletion: true))
|
||||
|> mapToSignal { searchableItems, faqSearchableItems -> Signal<(String, [SettingsSearchableItem])?, NoError> in
|
||||
return self.searchQuery.get()
|
||||
|> mapToSignal { query -> Signal<(String, [SettingsSearchableItem])?, NoError> in
|
||||
@ -429,20 +458,26 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
|
||||
}
|
||||
|
||||
let previousRecentItems = Atomic<[SettingsSearchRecentEntry]?>(value: nil)
|
||||
self.recentDisposable = (combineLatest(recentSearchItems, self.presentationDataPromise.get())
|
||||
|> deliverOnMainQueue).start(next: { [weak self] recentSearchItems, presentationData in
|
||||
self.recentDisposable = (combineLatest(recentSearchItems, faqItems.get(), self.presentationDataPromise.get())
|
||||
|> deliverOnMainQueue).start(next: { [weak self] recentSearchItems, faqItems, presentationData in
|
||||
if let strongSelf = self {
|
||||
var entries: [SettingsSearchRecentEntry] = []
|
||||
for i in 0 ..< recentSearchItems.count {
|
||||
entries.append(.recent(i, recentSearchItems[i]))
|
||||
}
|
||||
|
||||
let header = ChatListSearchItemHeader(type: .recentPeers, theme: presentationData.theme, strings: presentationData.strings, actionTitle: presentationData.strings.WebSearch_RecentSectionClear, action: {
|
||||
let recentHeader = ChatListSearchItemHeader(type: .recentPeers, theme: presentationData.theme, strings: presentationData.strings, actionTitle: presentationData.strings.WebSearch_RecentSectionClear, action: {
|
||||
clearRecentSettingsSearchItems(postbox: context.account.postbox)
|
||||
})
|
||||
let faqHeader = ChatListSearchItemHeader(type: .faq, theme: presentationData.theme, strings: presentationData.strings)
|
||||
|
||||
var entries: [SettingsSearchRecentEntry] = []
|
||||
for i in 0 ..< recentSearchItems.count {
|
||||
entries.append(.recent(i, recentSearchItems[i], recentHeader))
|
||||
}
|
||||
|
||||
for i in 0 ..< faqItems.count {
|
||||
entries.append(.faq(i, faqItems[i], faqHeader))
|
||||
}
|
||||
|
||||
|
||||
let previousEntries = previousRecentItems.swap(entries)
|
||||
let transition = preparedSettingsSearchContainerRecentTransition(from: previousEntries ?? [], to: entries, account: context.account, theme: presentationData.theme, strings: presentationData.strings, interaction: interaction, header: header)
|
||||
let transition = preparedSettingsSearchContainerRecentTransition(from: previousEntries ?? [], to: entries, account: context.account, theme: presentationData.theme, strings: presentationData.strings, interaction: interaction)
|
||||
strongSelf.enqueueRecentTransition(transition, firstTime: previousEntries == nil)
|
||||
}
|
||||
})
|
||||
@ -510,7 +545,7 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
|
||||
self.recentListNode.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
|
||||
}
|
||||
|
||||
override func searchTextUpdated(text: String) {
|
||||
public override func searchTextUpdated(text: String) {
|
||||
if text.isEmpty {
|
||||
self.searchQuery.set(.single(nil))
|
||||
} else {
|
||||
@ -569,7 +604,7 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
|
||||
}
|
||||
}
|
||||
|
||||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
public override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
||||
|
||||
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||
@ -593,7 +628,7 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN
|
||||
}
|
||||
}
|
||||
|
||||
override func scrollToTop() {
|
||||
public override func scrollToTop() {
|
||||
let listNodeToScroll: ListView
|
||||
if !self.listNode.isHidden {
|
||||
listNodeToScroll = self.listNode
|
||||
@ -657,8 +692,6 @@ private final class SettingsSearchItemNode: ItemListControllerSearchNode {
|
||||
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: SettingsSearchContainerNode(context: self.context, openResult: { [weak self] result in
|
||||
if let strongSelf = self {
|
||||
addRecentSettingsSearchItem(postbox: strongSelf.context.account.postbox, item: result.id)
|
||||
|
||||
result.present(strongSelf.context, strongSelf.getNavigationController?(), { [weak self] mode, controller in
|
||||
if let strongSelf = self {
|
||||
switch mode {
|
||||
|
@ -40,7 +40,17 @@ class SettingsSearchRecentItem: ListViewItem {
|
||||
async {
|
||||
let node = SettingsSearchRecentItemNode()
|
||||
let makeLayout = node.asyncLayout()
|
||||
let (nodeLayout, nodeApply) = makeLayout(self, params, nextItem == nil, !(previousItem is SettingsSearchRecentItem))
|
||||
|
||||
var previousHeader: ListViewItemHeader?
|
||||
if let previousItem = previousItem as? SettingsSearchRecentItem {
|
||||
previousHeader = previousItem.header
|
||||
}
|
||||
var nextHeader: ListViewItemHeader?
|
||||
if let nextItem = nextItem as? SettingsSearchRecentItem {
|
||||
nextHeader = nextItem.header
|
||||
}
|
||||
|
||||
let (nodeLayout, nodeApply) = makeLayout(self, params, nextItem == nil || nextHeader?.id != self.header?.id, !(previousItem is SettingsSearchRecentItem) || previousHeader?.id != self.header?.id)
|
||||
node.contentSize = nodeLayout.contentSize
|
||||
node.insets = nodeLayout.insets
|
||||
|
||||
@ -53,7 +63,16 @@ class SettingsSearchRecentItem: ListViewItem {
|
||||
if let nodeValue = node() as? SettingsSearchRecentItemNode {
|
||||
let layout = nodeValue.asyncLayout()
|
||||
async {
|
||||
let (nodeLayout, apply) = layout(self, params, nextItem == nil, !(previousItem is SettingsSearchRecentItem))
|
||||
var previousHeader: ListViewItemHeader?
|
||||
if let previousItem = previousItem as? SettingsSearchRecentItem {
|
||||
previousHeader = previousItem.header
|
||||
}
|
||||
var nextHeader: ListViewItemHeader?
|
||||
if let nextItem = nextItem as? SettingsSearchRecentItem {
|
||||
nextHeader = nextItem.header
|
||||
}
|
||||
|
||||
let (nodeLayout, apply) = layout(self, params, nextItem == nil || nextHeader?.id != self.header?.id, !(previousItem is SettingsSearchRecentItem) || previousHeader?.id != self.header?.id)
|
||||
Queue.mainQueue().async {
|
||||
completion(nodeLayout, { info in
|
||||
apply().1(info)
|
||||
|
@ -35,7 +35,7 @@ enum SettingsSearchableItemIcon {
|
||||
case faq
|
||||
}
|
||||
|
||||
enum SettingsSearchableItemId: Hashable {
|
||||
public enum SettingsSearchableItemId: Hashable {
|
||||
case profile(Int32)
|
||||
case proxy(Int32)
|
||||
case savedMessages(Int32)
|
||||
@ -152,20 +152,20 @@ enum SettingsSearchableItemId: Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
enum SettingsSearchableItemPresentation {
|
||||
public enum SettingsSearchableItemPresentation {
|
||||
case push
|
||||
case modal
|
||||
case immediate
|
||||
case dismiss
|
||||
}
|
||||
|
||||
struct SettingsSearchableItem {
|
||||
let id: SettingsSearchableItemId
|
||||
public struct SettingsSearchableItem {
|
||||
public let id: SettingsSearchableItemId
|
||||
let title: String
|
||||
let alternate: [String]
|
||||
let icon: SettingsSearchableItemIcon
|
||||
let breadcrumbs: [String]
|
||||
let present: (AccountContext, NavigationController?, @escaping (SettingsSearchableItemPresentation, ViewController?) -> Void) -> Void
|
||||
public let present: (AccountContext, NavigationController?, @escaping (SettingsSearchableItemPresentation, ViewController?) -> Void) -> Void
|
||||
}
|
||||
|
||||
private func synonyms(_ string: String?) -> [String] {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -27,6 +27,8 @@ public struct PresentationResourcesSettings {
|
||||
public static let proxy = renderIcon(name: "Settings/MenuIcons/Proxy")
|
||||
public static let savedMessages = renderIcon(name: "Settings/MenuIcons/SavedMessages")
|
||||
public static let recentCalls = renderIcon(name: "Settings/MenuIcons/RecentCalls")
|
||||
public static let devices = renderIcon(name: "Settings/MenuIcons/Sessions")
|
||||
public static let chatFolders = renderIcon(name: "Settings/MenuIcons/ChatListFilters")
|
||||
public static let stickers = renderIcon(name: "Settings/MenuIcons/Stickers")
|
||||
|
||||
public static let notifications = renderIcon(name: "Settings/MenuIcons/Notifications")
|
||||
|
@ -205,6 +205,7 @@ framework(
|
||||
"//submodules/StatisticsUI:StatisticsUI",
|
||||
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
||||
"//submodules/TooltipUI:TooltipUI",
|
||||
"//submodules/AuthTransferUI:AuthTransferUI",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
@ -200,6 +200,7 @@ swift_library(
|
||||
"//submodules/Svg:Svg",
|
||||
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
||||
"//submodules/TooltipUI:TooltipUI",
|
||||
"//submodules/AuthTransferUI:AuthTransferUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Settings/EditAccount.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/EditAccount.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_editaccount.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Settings/EditAccount.imageset/ic_editaccount.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Settings/EditAccount.imageset/ic_editaccount.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Settings/SetAvatar.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/SetAvatar.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_addphoto.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Settings/SetAvatar.imageset/ic_addphoto.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Settings/SetAvatar.imageset/ic_addphoto.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Settings/SetUsername.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/SetUsername.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_username.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Settings/SetUsername.imageset/ic_username.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Settings/SetUsername.imageset/ic_username.pdf
vendored
Normal file
Binary file not shown.
Binary file not shown.
@ -2048,6 +2048,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self?.displayDiceTooltip(dice: dice)
|
||||
}, animateDiceSuccess: { [weak self] in
|
||||
self?.chatDisplayNode.animateQuizCorrectOptionSelected()
|
||||
}, greetingStickerNode: { [weak self] in
|
||||
return self?.chatDisplayNode.greetingStickerNode
|
||||
}, requestMessageUpdate: { [weak self] id in
|
||||
if let strongSelf = self {
|
||||
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id)
|
||||
|
@ -119,6 +119,7 @@ public final class ChatControllerInteraction {
|
||||
let displayPsa: (String, ASDisplayNode) -> Void
|
||||
let displayDiceTooltip: (TelegramMediaDice) -> Void
|
||||
let animateDiceSuccess: () -> Void
|
||||
let greetingStickerNode: () -> (ASDisplayNode, ASDisplayNode, () -> Void)?
|
||||
|
||||
let requestMessageUpdate: (MessageId) -> Void
|
||||
let cancelInteractiveKeyboardGestures: () -> Void
|
||||
@ -136,7 +137,7 @@ public final class ChatControllerInteraction {
|
||||
var searchTextHighightState: (String, [MessageIndex])?
|
||||
var seenOneTimeAnimatedMedia = Set<MessageId>()
|
||||
|
||||
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId, Bool) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
|
||||
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId, Bool) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, greetingStickerNode: @escaping () -> (ASDisplayNode, ASDisplayNode, () -> Void)?, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
|
||||
self.openMessage = openMessage
|
||||
self.openPeer = openPeer
|
||||
self.openPeerMention = openPeerMention
|
||||
@ -199,6 +200,7 @@ public final class ChatControllerInteraction {
|
||||
self.openMessagePollResults = openMessagePollResults
|
||||
self.displayDiceTooltip = displayDiceTooltip
|
||||
self.animateDiceSuccess = animateDiceSuccess
|
||||
self.greetingStickerNode = greetingStickerNode
|
||||
|
||||
self.requestMessageUpdate = requestMessageUpdate
|
||||
self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures
|
||||
@ -244,6 +246,8 @@ public final class ChatControllerInteraction {
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: {
|
||||
}, greetingStickerNode: {
|
||||
return nil
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
@ -715,6 +715,20 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
var greetingStickerNode: (ASDisplayNode, ASDisplayNode, () -> Void)? {
|
||||
if let greetingStickerNode = self.emptyNode?.greetingStickerNode {
|
||||
self.historyNode.itemHeaderNodesAlpha = 0.0
|
||||
return (greetingStickerNode, self, { [weak self] in
|
||||
self?.historyNode.forEachItemHeaderNode { node in
|
||||
node.alpha = 1.0
|
||||
node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private var isInFocus: Bool = false
|
||||
|
||||
func inFocusUpdated(isInFocus: Bool) {
|
||||
|
@ -71,6 +71,16 @@ private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNode
|
||||
private var didSetupSticker = false
|
||||
private let disposable = MetaDisposable()
|
||||
|
||||
var greetingStickerNode: ASDisplayNode? {
|
||||
if let animationNode = self.stickerNode.animationNode, animationNode.supernode === stickerNode {
|
||||
return animationNode
|
||||
} else if self.stickerNode.imageNode.supernode === stickerNode {
|
||||
return self.stickerNode.imageNode
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
init(account: Account, interaction: ChatPanelInterfaceInteraction?) {
|
||||
self.account = account
|
||||
self.interaction = interaction
|
||||
@ -655,7 +665,8 @@ final class ChatEmptyNode: ASDisplayNode {
|
||||
let node: ASDisplayNode & ChatEmptyNodeContent
|
||||
switch contentType {
|
||||
case .regular:
|
||||
node = ChatEmptyNodeRegularChatContent()
|
||||
// node = ChatEmptyNodeRegularChatContent()
|
||||
node = ChatEmptyNodeNearbyChatContent(account: self.account, interaction: self.interaction)
|
||||
case .secret:
|
||||
node = ChatEmptyNodeSecretChatContent()
|
||||
case .group:
|
||||
@ -668,9 +679,8 @@ final class ChatEmptyNode: ASDisplayNode {
|
||||
self.content = (contentType, node)
|
||||
self.addSubnode(node)
|
||||
contentTransition = .immediate
|
||||
|
||||
self.isUserInteractionEnabled = contentType == .peerNearby
|
||||
}
|
||||
self.isUserInteractionEnabled = contentType == .peerNearby || contentType == .regular
|
||||
|
||||
let displayRect = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top - insets.bottom))
|
||||
|
||||
@ -686,6 +696,15 @@ final class ChatEmptyNode: ASDisplayNode {
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: contentFrame)
|
||||
}
|
||||
|
||||
var greetingStickerNode: ASDisplayNode? {
|
||||
if let (_, node) = self.content {
|
||||
if let node = node as? ChatEmptyNodeNearbyChatContent {
|
||||
return node.greetingStickerNode
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -173,8 +173,8 @@ final class ChatMediaInputStickerGridItem: GridItem {
|
||||
final class ChatMediaInputStickerGridItemNode: GridItemNode {
|
||||
private var currentState: (Account, StickerPackItem, CGSize)?
|
||||
private var currentSize: CGSize?
|
||||
private let imageNode: TransformImageNode
|
||||
private var animationNode: AnimatedStickerNode?
|
||||
let imageNode: TransformImageNode
|
||||
var animationNode: AnimatedStickerNode?
|
||||
private var placeholderNode: ShimmerEffectNode?
|
||||
private var didSetUpAnimationNode = false
|
||||
private var item: ChatMediaInputStickerGridItem?
|
||||
|
@ -39,6 +39,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
private var animationNode: GenericAnimatedStickerNode?
|
||||
private var didSetUpAnimationNode = false
|
||||
private var isPlaying = false
|
||||
private var animateGreeting = false
|
||||
private weak var greetingStickerParentNode: ASDisplayNode?
|
||||
private var greetingCompletion: (() -> Void)?
|
||||
|
||||
private var swipeToReplyNode: ChatMessageSwipeToReplyNode?
|
||||
private var swipeToReplyFeedback: HapticFeedback?
|
||||
@ -234,22 +237,33 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
self.animationNode = animationNode
|
||||
} else {
|
||||
let animationNode = AnimatedStickerNode()
|
||||
animationNode.started = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.imageNode.alpha = 0.0
|
||||
|
||||
if let item = strongSelf.item {
|
||||
if let _ = strongSelf.emojiFile {
|
||||
item.controllerInteraction.seenOneTimeAnimatedMedia.insert(item.message.id)
|
||||
let animationNode: AnimatedStickerNode
|
||||
if let (node, parentNode, greetingCompletion) = item.controllerInteraction.greetingStickerNode(), let greetingStickerNode = node as? AnimatedStickerNode {
|
||||
animationNode = greetingStickerNode
|
||||
self.imageNode.alpha = 0.0
|
||||
self.animateGreeting = true
|
||||
self.greetingStickerParentNode = parentNode
|
||||
self.greetingCompletion = greetingCompletion
|
||||
self.dateAndStatusNode.alpha = 0.0
|
||||
} else {
|
||||
animationNode = AnimatedStickerNode()
|
||||
animationNode.started = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.imageNode.alpha = 0.0
|
||||
|
||||
if let item = strongSelf.item {
|
||||
if let _ = strongSelf.emojiFile {
|
||||
item.controllerInteraction.seenOneTimeAnimatedMedia.insert(item.message.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.animationNode = animationNode
|
||||
}
|
||||
|
||||
if let animationNode = self.animationNode {
|
||||
if let animationNode = self.animationNode, !self.animateGreeting {
|
||||
self.contextSourceNode.contentNode.insertSubnode(animationNode, aboveSubnode: self.imageNode)
|
||||
}
|
||||
}
|
||||
@ -745,8 +759,38 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
|
||||
strongSelf.imageNode.frame = updatedContentFrame
|
||||
strongSelf.animationNode?.frame = updatedContentFrame.insetBy(dx: imageInset, dy: imageInset)
|
||||
if let animationNode = strongSelf.animationNode as? AnimatedStickerNode {
|
||||
|
||||
let animationNodeFrame = updatedContentFrame.insetBy(dx: imageInset, dy: imageInset)
|
||||
if let animationNode = strongSelf.animationNode, let parentNode = strongSelf.greetingStickerParentNode, strongSelf.animateGreeting {
|
||||
strongSelf.animateGreeting = false
|
||||
|
||||
let initialFrame = animationNode.view.convert(animationNode.bounds, to: parentNode.view)
|
||||
parentNode.addSubnode(animationNode)
|
||||
animationNode.frame = initialFrame
|
||||
if true {
|
||||
let targetScale = animationNodeFrame.width / initialFrame.width
|
||||
animationNode.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3, removeOnCompletion: false)
|
||||
animationNode.layer.animatePosition(from: initialFrame.center, to: CGPoint(x: animationNodeFrame.midX, y: initialFrame.center.y + 173.0), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { [weak self] finished in
|
||||
if let strongSelf = self {
|
||||
animationNode.layer.removeAllAnimations()
|
||||
strongSelf.animationNode?.frame = animationNodeFrame
|
||||
strongSelf.contextSourceNode.contentNode.insertSubnode(animationNode, aboveSubnode: strongSelf.imageNode)
|
||||
|
||||
if let animationNode = strongSelf.animationNode as? AnimatedStickerNode {
|
||||
animationNode.updateLayout(size: updatedContentFrame.insetBy(dx: imageInset, dy: imageInset).size)
|
||||
}
|
||||
|
||||
strongSelf.dateAndStatusNode.alpha = 1.0
|
||||
strongSelf.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
||||
strongSelf.greetingCompletion?()
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if strongSelf.animationNode?.supernode === strongSelf.contextSourceNode.contentNode {
|
||||
strongSelf.animationNode?.frame = animationNodeFrame
|
||||
}
|
||||
if let animationNode = strongSelf.animationNode as? AnimatedStickerNode, strongSelf.animationNode?.supernode === strongSelf.contextSourceNode.contentNode {
|
||||
animationNode.updateLayout(size: updatedContentFrame.insetBy(dx: imageInset, dy: imageInset).size)
|
||||
}
|
||||
imageApply()
|
||||
|
@ -427,6 +427,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: {
|
||||
}, greetingStickerNode: {
|
||||
return nil
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
|
||||
|
@ -325,7 +325,7 @@ public func createChannelController(context: AccountContext) -> ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: stateValue.with({ $0.avatar }) != nil, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: true)!
|
||||
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: stateValue.with({ $0.avatar }) != nil, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
|
||||
let _ = currentAvatarMixin.swap(mixin)
|
||||
mixin.requestSearchController = { assetsController in
|
||||
let controller = WebSearchController(context: context, peer: peer, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: title, completion: { result in
|
||||
|
@ -583,7 +583,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
||||
}
|
||||
}
|
||||
|
||||
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: stateValue.with({ $0.avatar }) != nil, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: true)!
|
||||
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: stateValue.with({ $0.avatar }) != nil, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
|
||||
let _ = currentAvatarMixin.swap(mixin)
|
||||
mixin.requestSearchController = { assetsController in
|
||||
let controller = WebSearchController(context: context, peer: peer, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: title, completion: { result in
|
||||
|
@ -141,6 +141,8 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: {
|
||||
}, greetingStickerNode: {
|
||||
return nil
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
@ -128,6 +128,8 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: {
|
||||
}, greetingStickerNode: {
|
||||
return nil
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
|
||||
|
@ -7,16 +7,25 @@ enum PeerInfoScreenActionColor {
|
||||
case destructive
|
||||
}
|
||||
|
||||
enum PeerInfoScreenActionAligmnent {
|
||||
case natural
|
||||
case center
|
||||
}
|
||||
|
||||
final class PeerInfoScreenActionItem: PeerInfoScreenItem {
|
||||
let id: AnyHashable
|
||||
let text: String
|
||||
let color: PeerInfoScreenActionColor
|
||||
let icon: UIImage?
|
||||
let alignment: PeerInfoScreenActionAligmnent
|
||||
let action: (() -> Void)?
|
||||
|
||||
init(id: AnyHashable, text: String, color: PeerInfoScreenActionColor = .accent, action: (() -> Void)?) {
|
||||
init(id: AnyHashable, text: String, color: PeerInfoScreenActionColor = .accent, icon: UIImage? = nil, alignment: PeerInfoScreenActionAligmnent = .natural, action: (() -> Void)?) {
|
||||
self.id = id
|
||||
self.text = text
|
||||
self.color = color
|
||||
self.icon = icon
|
||||
self.alignment = alignment
|
||||
self.action = action
|
||||
}
|
||||
|
||||
@ -27,6 +36,7 @@ final class PeerInfoScreenActionItem: PeerInfoScreenItem {
|
||||
|
||||
private final class PeerInfoScreenActionItemNode: PeerInfoScreenItemNode {
|
||||
private let selectionNode: PeerInfoScreenSelectableBackgroundNode
|
||||
private let iconNode: ASImageNode
|
||||
private let textNode: ImmediateTextNode
|
||||
private let bottomSeparatorNode: ASDisplayNode
|
||||
|
||||
@ -36,6 +46,10 @@ private final class PeerInfoScreenActionItemNode: PeerInfoScreenItemNode {
|
||||
var bringToFrontForHighlightImpl: (() -> Void)?
|
||||
self.selectionNode = PeerInfoScreenSelectableBackgroundNode(bringToFrontForHighlight: { bringToFrontForHighlightImpl?() })
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
self.iconNode.isLayerBacked = true
|
||||
self.iconNode.displaysAsynchronously = false
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
@ -54,7 +68,7 @@ private final class PeerInfoScreenActionItemNode: PeerInfoScreenItemNode {
|
||||
self.addSubnode(self.textNode)
|
||||
}
|
||||
|
||||
override func update(width: CGFloat, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
guard let item = item as? PeerInfoScreenActionItem else {
|
||||
return 10.0
|
||||
}
|
||||
@ -63,7 +77,10 @@ private final class PeerInfoScreenActionItemNode: PeerInfoScreenItemNode {
|
||||
|
||||
self.selectionNode.pressed = item.action
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
let leftInset = (item.icon == nil ? sideInset : sideInset + 29.0 + 16.0)
|
||||
let rightInset = sideInset
|
||||
let separatorInset = item.icon == nil ? sideInset : leftInset - 1.0
|
||||
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
@ -78,11 +95,23 @@ private final class PeerInfoScreenActionItemNode: PeerInfoScreenItemNode {
|
||||
self.textNode.maximumNumberOfLines = 1
|
||||
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue)
|
||||
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude))
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: width - (leftInset + rightInset), height: .greatestFiniteMagnitude))
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: 11.0), size: textSize)
|
||||
let textFrame = CGRect(origin: CGPoint(x: item.alignment == .center ? floorToScreenPixels((width - textSize.width) / 2.0) : leftInset, y: 12.0), size: textSize)
|
||||
|
||||
let height = textSize.height + 22.0
|
||||
let height = textSize.height + 24.0
|
||||
|
||||
if let icon = item.icon {
|
||||
if self.iconNode.supernode == nil {
|
||||
self.addSubnode(self.iconNode)
|
||||
}
|
||||
self.iconNode.image = generateTintedImage(image: icon, color: textColorValue)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: sideInset, y: floorToScreenPixels((height - icon.size.height) / 2.0)), size: icon.size)
|
||||
transition.updateFrame(node: self.iconNode, frame: iconFrame)
|
||||
} else if self.iconNode.supernode != nil {
|
||||
self.iconNode.image = nil
|
||||
self.iconNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.textNode, frame: textFrame)
|
||||
|
||||
@ -90,7 +119,7 @@ private final class PeerInfoScreenActionItemNode: PeerInfoScreenItemNode {
|
||||
self.selectionNode.update(size: CGSize(width: width, height: height + highlightNodeOffset), theme: presentationData.theme, transition: transition)
|
||||
transition.updateFrame(node: self.selectionNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -highlightNodeOffset), size: CGSize(width: width, height: height + highlightNodeOffset)))
|
||||
|
||||
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: sideInset, y: height - UIScreenPixel), size: CGSize(width: width - sideInset, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: separatorInset, y: height - UIScreenPixel), size: CGSize(width: width - separatorInset, height: UIScreenPixel)))
|
||||
transition.updateAlpha(node: self.bottomSeparatorNode, alpha: bottomItem == nil ? 0.0 : 1.0)
|
||||
|
||||
return height
|
||||
|
@ -63,7 +63,7 @@ private final class PeerInfoScreenAddressItemNode: PeerInfoScreenItemNode {
|
||||
self.addSubnode(self.selectionNode)
|
||||
}
|
||||
|
||||
override func update(width: CGFloat, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
guard let item = item as? PeerInfoScreenAddressItem else {
|
||||
return 10.0
|
||||
}
|
||||
@ -72,13 +72,13 @@ private final class PeerInfoScreenAddressItemNode: PeerInfoScreenItemNode {
|
||||
|
||||
self.selectionNode.pressed = item.action
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
let addressItem = ItemListAddressItem(theme: presentationData.theme, label: item.label, text: item.text, imageSignal: item.imageSignal, sectionId: 0, style: .blocks, displayDecorations: false, action: nil, longTapAction: item.longTapAction, linkItemAction: item.linkItemAction)
|
||||
|
||||
let params = ListViewItemLayoutParams(width: width, leftInset: 0.0, rightInset: 0.0, availableHeight: 1000.0)
|
||||
let params = ListViewItemLayoutParams(width: width, leftInset: safeInsets.left, rightInset: safeInsets.right, availableHeight: 1000.0)
|
||||
|
||||
let itemNode: ItemListAddressItemNode
|
||||
if let current = self.itemNode {
|
||||
|
@ -52,7 +52,7 @@ private final class PeerInfoScreenCallListItemNode: PeerInfoScreenItemNode {
|
||||
self.addSubnode(self.selectionNode)
|
||||
}
|
||||
|
||||
override func update(width: CGFloat, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
guard let item = item as? PeerInfoScreenCallListItem else {
|
||||
return 10.0
|
||||
}
|
||||
@ -61,13 +61,13 @@ private final class PeerInfoScreenCallListItemNode: PeerInfoScreenItemNode {
|
||||
|
||||
self.selectionNode.pressed = nil
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
let addressItem = ItemListCallListItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, messages: item.messages, sectionId: 0, style: .blocks, displayDecorations: false)
|
||||
|
||||
let params = ListViewItemLayoutParams(width: width, leftInset: 0.0, rightInset: 0.0, availableHeight: 1000.0)
|
||||
let params = ListViewItemLayoutParams(width: width, leftInset: safeInsets.left, rightInset: safeInsets.right, availableHeight: 1000.0)
|
||||
|
||||
let itemNode: ItemListCallListItemNode
|
||||
if let current = self.itemNode {
|
||||
|
@ -31,14 +31,14 @@ private final class PeerInfoScreenCommentItemNode: PeerInfoScreenItemNode {
|
||||
self.addSubnode(self.textNode)
|
||||
}
|
||||
|
||||
override func update(width: CGFloat, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
guard let item = item as? PeerInfoScreenCommentItem else {
|
||||
return 10.0
|
||||
}
|
||||
|
||||
self.item = item
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
let verticalInset: CGFloat = 7.0
|
||||
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
|
@ -66,7 +66,7 @@ private final class PeerInfoScreenDisclosureEncryptionKeyItemNode: PeerInfoScree
|
||||
self.addSubnode(self.arrowNode)
|
||||
}
|
||||
|
||||
override func update(width: CGFloat, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
guard let item = item as? PeerInfoScreenDisclosureEncryptionKeyItem else {
|
||||
return 10.0
|
||||
}
|
||||
@ -79,7 +79,7 @@ private final class PeerInfoScreenDisclosureEncryptionKeyItemNode: PeerInfoScree
|
||||
|
||||
self.selectionNode.pressed = item.action
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
@ -90,9 +90,9 @@ private final class PeerInfoScreenDisclosureEncryptionKeyItemNode: PeerInfoScree
|
||||
|
||||
let arrowInset: CGFloat = 18.0
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: 11.0), size: textSize)
|
||||
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: 12.0), size: textSize)
|
||||
|
||||
let height = textSize.height + 22.0
|
||||
let height = textSize.height + 24.0
|
||||
|
||||
if let arrowImage = PresentationResourcesItemList.disclosureArrowImage(presentationData.theme) {
|
||||
self.arrowNode.image = arrowImage
|
||||
|
@ -3,15 +3,41 @@ import Display
|
||||
import TelegramPresentationData
|
||||
|
||||
final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem {
|
||||
enum Label {
|
||||
case none
|
||||
case text(String)
|
||||
case badge(String, UIColor)
|
||||
|
||||
var text: String {
|
||||
switch self {
|
||||
case .none:
|
||||
return ""
|
||||
case let .text(text), let .badge(text, _):
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
var badgeColor: UIColor? {
|
||||
switch self {
|
||||
case .none, .text:
|
||||
return nil
|
||||
case let .badge(_, color):
|
||||
return color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let id: AnyHashable
|
||||
let label: String
|
||||
let label: Label
|
||||
let text: String
|
||||
let icon: UIImage?
|
||||
let action: (() -> Void)?
|
||||
|
||||
init(id: AnyHashable, label: String, text: String, action: (() -> Void)?) {
|
||||
init(id: AnyHashable, label: Label = .none, text: String, icon: UIImage? = nil, action: (() -> Void)?) {
|
||||
self.id = id
|
||||
self.label = label
|
||||
self.text = text
|
||||
self.icon = icon
|
||||
self.action = action
|
||||
}
|
||||
|
||||
@ -22,6 +48,8 @@ final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem {
|
||||
|
||||
private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
private let selectionNode: PeerInfoScreenSelectableBackgroundNode
|
||||
private let iconNode: ASImageNode
|
||||
private let labelBadgeNode: ASImageNode
|
||||
private let labelNode: ImmediateTextNode
|
||||
private let textNode: ImmediateTextNode
|
||||
private let arrowNode: ASImageNode
|
||||
@ -33,6 +61,15 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
var bringToFrontForHighlightImpl: (() -> Void)?
|
||||
self.selectionNode = PeerInfoScreenSelectableBackgroundNode(bringToFrontForHighlight: { bringToFrontForHighlightImpl?() })
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
self.iconNode.isLayerBacked = true
|
||||
self.iconNode.displaysAsynchronously = false
|
||||
|
||||
self.labelBadgeNode = ASImageNode()
|
||||
self.labelBadgeNode.displayWithoutProcessing = true
|
||||
self.labelBadgeNode.displaysAsynchronously = false
|
||||
self.labelBadgeNode.isLayerBacked = true
|
||||
|
||||
self.labelNode = ImmediateTextNode()
|
||||
self.labelNode.displaysAsynchronously = false
|
||||
self.labelNode.isUserInteractionEnabled = false
|
||||
@ -63,36 +100,56 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
self.addSubnode(self.arrowNode)
|
||||
}
|
||||
|
||||
override func update(width: CGFloat, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
guard let item = item as? PeerInfoScreenDisclosureItem else {
|
||||
return 10.0
|
||||
}
|
||||
|
||||
let previousItem = self.item
|
||||
self.item = item
|
||||
|
||||
self.selectionNode.pressed = item.action
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
let leftInset = (item.icon == nil ? sideInset : sideInset + 29.0 + 16.0)
|
||||
let rightInset = sideInset + 18.0
|
||||
let separatorInset = item.icon == nil ? sideInset : leftInset - 1.0
|
||||
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
let textColorValue: UIColor = presentationData.theme.list.itemPrimaryTextColor
|
||||
let labelColorValue: UIColor = presentationData.theme.list.itemSecondaryTextColor
|
||||
|
||||
self.labelNode.attributedText = NSAttributedString(string: item.label, font: Font.regular(17.0), textColor: labelColorValue)
|
||||
let labelColorValue: UIColor
|
||||
let labelFont: UIFont
|
||||
if case .badge = item.label {
|
||||
labelColorValue = presentationData.theme.list.itemCheckColors.foregroundColor
|
||||
labelFont = Font.regular(15.0)
|
||||
} else {
|
||||
labelColorValue = presentationData.theme.list.itemSecondaryTextColor
|
||||
labelFont = Font.regular(17.0)
|
||||
}
|
||||
self.labelNode.attributedText = NSAttributedString(string: item.label.text, font: labelFont, textColor: labelColorValue)
|
||||
|
||||
self.textNode.maximumNumberOfLines = 1
|
||||
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue)
|
||||
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude))
|
||||
let labelSize = self.labelNode.updateLayout(CGSize(width: width - textSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude))
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: width - (leftInset + rightInset), height: .greatestFiniteMagnitude))
|
||||
let labelSize = self.labelNode.updateLayout(CGSize(width: width - textSize.width - (leftInset + rightInset), height: .greatestFiniteMagnitude))
|
||||
|
||||
let arrowInset: CGFloat = 18.0
|
||||
let textFrame = CGRect(origin: CGPoint(x: leftInset, y: 12.0), size: textSize)
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: 11.0), size: textSize)
|
||||
let labelFrame = CGRect(origin: CGPoint(x: width - sideInset - arrowInset - labelSize.width, y: 11.0), size: labelSize)
|
||||
let height = textSize.height + 24.0
|
||||
|
||||
let height = textSize.height + 22.0
|
||||
if let icon = item.icon {
|
||||
if self.iconNode.supernode == nil {
|
||||
self.addSubnode(self.iconNode)
|
||||
}
|
||||
self.iconNode.image = icon
|
||||
let iconFrame = CGRect(origin: CGPoint(x: sideInset, y: floorToScreenPixels((height - icon.size.height) / 2.0)), size: icon.size)
|
||||
transition.updateFrame(node: self.iconNode, frame: iconFrame)
|
||||
} else if self.iconNode.supernode != nil {
|
||||
self.iconNode.image = nil
|
||||
self.iconNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if let arrowImage = PresentationResourcesItemList.disclosureArrowImage(presentationData.theme) {
|
||||
self.arrowNode.image = arrowImage
|
||||
@ -100,6 +157,29 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
transition.updateFrame(node: self.arrowNode, frame: arrowFrame)
|
||||
}
|
||||
|
||||
let badgeDiameter: CGFloat = 20.0
|
||||
if case let .badge(text, badgeColor) = item.label, !text.isEmpty {
|
||||
if previousItem?.label.badgeColor != badgeColor {
|
||||
self.labelBadgeNode.image = generateStretchableFilledCircleImage(diameter: badgeDiameter, color: badgeColor)
|
||||
}
|
||||
if self.labelBadgeNode.supernode == nil {
|
||||
self.insertSubnode(self.labelBadgeNode, belowSubnode: self.labelNode)
|
||||
}
|
||||
} else {
|
||||
self.labelBadgeNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
let badgeWidth = max(badgeDiameter, labelSize.width + 10.0)
|
||||
let labelFrame: CGRect
|
||||
if case .badge = item.label {
|
||||
labelFrame = CGRect(origin: CGPoint(x: width - rightInset - badgeWidth + (badgeWidth - labelSize.width) / 2.0, y: floor((height - labelSize.height) / 2.0)), size: labelSize)
|
||||
} else {
|
||||
labelFrame = CGRect(origin: CGPoint(x: width - rightInset - labelSize.width, y: 12.0), size: labelSize)
|
||||
}
|
||||
|
||||
let labelBadgeNodeFrame = CGRect(origin: CGPoint(x: width - rightInset - badgeWidth, y: labelFrame.minY - 1.0), size: CGSize(width: badgeWidth, height: badgeDiameter))
|
||||
|
||||
transition.updateFrame(node: self.labelBadgeNode, frame: labelBadgeNodeFrame)
|
||||
transition.updateFrame(node: self.labelNode, frame: labelFrame)
|
||||
transition.updateFrame(node: self.textNode, frame: textFrame)
|
||||
|
||||
@ -107,7 +187,7 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
self.selectionNode.update(size: CGSize(width: width, height: height + highlightNodeOffset), theme: presentationData.theme, transition: transition)
|
||||
transition.updateFrame(node: self.selectionNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -highlightNodeOffset), size: CGSize(width: width, height: height + highlightNodeOffset)))
|
||||
|
||||
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: sideInset, y: height - UIScreenPixel), size: CGSize(width: width - sideInset, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: separatorInset, y: height - UIScreenPixel), size: CGSize(width: width - separatorInset, height: UIScreenPixel)))
|
||||
transition.updateAlpha(node: self.bottomSeparatorNode, alpha: bottomItem == nil ? 0.0 : 1.0)
|
||||
|
||||
return height
|
||||
|
@ -31,14 +31,14 @@ private final class PeerInfoScreenHeaderItemNode: PeerInfoScreenItemNode {
|
||||
self.addSubnode(self.textNode)
|
||||
}
|
||||
|
||||
override func update(width: CGFloat, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
guard let item = item as? PeerInfoScreenHeaderItem else {
|
||||
return 10.0
|
||||
}
|
||||
|
||||
self.item = item
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
let verticalInset: CGFloat = 7.0
|
||||
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
|
@ -178,7 +178,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func update(width: CGFloat, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
guard let item = item as? PeerInfoScreenLabeledValueItem else {
|
||||
return 10.0
|
||||
}
|
||||
@ -188,7 +188,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
||||
|
||||
self.selectionNode.pressed = item.action
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
|
@ -21,22 +21,28 @@ enum PeerInfoScreenMemberItemAction {
|
||||
final class PeerInfoScreenMemberItem: PeerInfoScreenItem {
|
||||
let id: AnyHashable
|
||||
let context: AccountContext
|
||||
let enclosingPeer: Peer
|
||||
let enclosingPeer: Peer?
|
||||
let member: PeerInfoMember
|
||||
let badge: String?
|
||||
let action: ((PeerInfoScreenMemberItemAction) -> Void)?
|
||||
let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||
|
||||
init(
|
||||
id: AnyHashable,
|
||||
context: AccountContext,
|
||||
enclosingPeer: Peer,
|
||||
enclosingPeer: Peer?,
|
||||
member: PeerInfoMember,
|
||||
action: ((PeerInfoScreenMemberItemAction) -> Void)?
|
||||
badge: String? = nil,
|
||||
action: ((PeerInfoScreenMemberItemAction) -> Void)?,
|
||||
contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.context = context
|
||||
self.enclosingPeer = enclosingPeer
|
||||
self.member = member
|
||||
self.badge = badge
|
||||
self.action = action
|
||||
self.contextAction = contextAction
|
||||
}
|
||||
|
||||
func node() -> PeerInfoScreenItemNode {
|
||||
@ -103,7 +109,7 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func update(width: CGFloat, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
guard let item = item as? PeerInfoScreenMemberItem else {
|
||||
return 10.0
|
||||
}
|
||||
@ -116,7 +122,7 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
@ -152,14 +158,38 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode {
|
||||
item.action?(.remove)
|
||||
}))
|
||||
}
|
||||
if actions.contains(.logout) {
|
||||
options.append(ItemListPeerItemRevealOption(type: .destructive, title: presentationData.strings.Settings_Context_Logout, action: {
|
||||
item.action?(.remove)
|
||||
}))
|
||||
}
|
||||
|
||||
let peerItem = ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: item.context, peer: item.member.peer, height: .peerList, presence: item.member.presence, text: .presence, label: label == nil ? .none : .text(label!, .standard), editing: ItemListPeerItemEditing(editable: !options.isEmpty, editing: false, revealed: nil), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, selectable: false, sectionId: 0, action: nil, setPeerIdWithRevealedOptions: { lhs, rhs in
|
||||
let itemLabel: ItemListPeerItemLabel
|
||||
if let label = label {
|
||||
itemLabel = .text(label, .standard)
|
||||
} else if let badge = item.badge {
|
||||
itemLabel = .badge(badge)
|
||||
} else {
|
||||
itemLabel = .none
|
||||
}
|
||||
|
||||
let itemHeight: ItemListPeerItemHeight
|
||||
let itemText: ItemListPeerItemText
|
||||
if case .account = item.member {
|
||||
itemHeight = .generic
|
||||
itemText = .none
|
||||
} else {
|
||||
itemHeight = .peerList
|
||||
itemText = .presence
|
||||
}
|
||||
|
||||
let peerItem = ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: item.context, peer: item.member.peer, height: itemHeight, presence: item.member.presence, text: itemText, label: itemLabel, editing: ItemListPeerItemEditing(editable: !options.isEmpty, editing: false, revealed: nil), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, selectable: false, sectionId: 0, action: nil, setPeerIdWithRevealedOptions: { lhs, rhs in
|
||||
|
||||
}, removePeer: { _ in
|
||||
|
||||
}, contextAction: nil, hasTopStripe: false, hasTopGroupInset: false, noInsets: true, displayDecorations: false)
|
||||
}, contextAction: item.contextAction, hasTopStripe: false, hasTopGroupInset: false, noInsets: true, displayDecorations: false)
|
||||
|
||||
let params = ListViewItemLayoutParams(width: width, leftInset: 0.0, rightInset: 0.0, availableHeight: 1000.0)
|
||||
let params = ListViewItemLayoutParams(width: width, leftInset: safeInsets.left, rightInset: safeInsets.right, availableHeight: 1000.0)
|
||||
|
||||
let itemNode: ItemListPeerItemNode
|
||||
if let current = self.itemNode {
|
||||
|
@ -59,7 +59,7 @@ private final class PeerInfoScreenSwitchItemNode: PeerInfoScreenItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func update(width: CGFloat, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
guard let item = item as? PeerInfoScreenSwitchItem else {
|
||||
return 10.0
|
||||
}
|
||||
@ -78,7 +78,7 @@ private final class PeerInfoScreenSwitchItemNode: PeerInfoScreenItemNode {
|
||||
|
||||
self.selectionNode.pressed = nil
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
@ -91,9 +91,9 @@ private final class PeerInfoScreenSwitchItemNode: PeerInfoScreenItemNode {
|
||||
|
||||
let arrowInset: CGFloat = 18.0
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: 11.0), size: textSize)
|
||||
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: 12.0), size: textSize)
|
||||
|
||||
let height = textSize.height + 22.0
|
||||
let height = textSize.height + 24.0
|
||||
|
||||
transition.updateFrame(node: self.textNode, frame: textFrame)
|
||||
|
||||
|
@ -9,6 +9,10 @@ import PeerPresenceStatusManager
|
||||
import TelegramStringFormatting
|
||||
import TelegramPresentationData
|
||||
import PeerAvatarGalleryUI
|
||||
import TelegramUIPreferences
|
||||
import TelegramNotices
|
||||
import AccountUtils
|
||||
import DeviceAccess
|
||||
|
||||
enum PeerInfoUpdatingAvatar {
|
||||
case none
|
||||
@ -19,22 +23,26 @@ final class PeerInfoState {
|
||||
let isEditing: Bool
|
||||
let selectedMessageIds: Set<MessageId>?
|
||||
let updatingAvatar: PeerInfoUpdatingAvatar?
|
||||
let updatingBio: String?
|
||||
|
||||
init(
|
||||
isEditing: Bool,
|
||||
selectedMessageIds: Set<MessageId>?,
|
||||
updatingAvatar: PeerInfoUpdatingAvatar?
|
||||
updatingAvatar: PeerInfoUpdatingAvatar?,
|
||||
updatingBio: String?
|
||||
) {
|
||||
self.isEditing = isEditing
|
||||
self.selectedMessageIds = selectedMessageIds
|
||||
self.updatingAvatar = updatingAvatar
|
||||
self.updatingBio = updatingBio
|
||||
}
|
||||
|
||||
func withIsEditing(_ isEditing: Bool) -> PeerInfoState {
|
||||
return PeerInfoState(
|
||||
isEditing: isEditing,
|
||||
selectedMessageIds: self.selectedMessageIds,
|
||||
updatingAvatar: self.updatingAvatar
|
||||
updatingAvatar: self.updatingAvatar,
|
||||
updatingBio: self.updatingBio
|
||||
)
|
||||
}
|
||||
|
||||
@ -42,7 +50,8 @@ final class PeerInfoState {
|
||||
return PeerInfoState(
|
||||
isEditing: self.isEditing,
|
||||
selectedMessageIds: selectedMessageIds,
|
||||
updatingAvatar: self.updatingAvatar
|
||||
updatingAvatar: self.updatingAvatar,
|
||||
updatingBio: self.updatingBio
|
||||
)
|
||||
}
|
||||
|
||||
@ -50,9 +59,74 @@ final class PeerInfoState {
|
||||
return PeerInfoState(
|
||||
isEditing: self.isEditing,
|
||||
selectedMessageIds: self.selectedMessageIds,
|
||||
updatingAvatar: updatingAvatar
|
||||
updatingAvatar: updatingAvatar,
|
||||
updatingBio: self.updatingBio
|
||||
)
|
||||
}
|
||||
|
||||
func withUpdatingBio(_ updatingBio: String?) -> PeerInfoState {
|
||||
return PeerInfoState(
|
||||
isEditing: self.isEditing,
|
||||
selectedMessageIds: self.selectedMessageIds,
|
||||
updatingAvatar: self.updatingAvatar,
|
||||
updatingBio: updatingBio
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
final class TelegramGlobalSettings {
|
||||
let suggestPhoneNumberConfirmation: Bool
|
||||
let accountsAndPeers: [(Account, Peer, Int32)]
|
||||
let activeSessionsContext: ActiveSessionsContext
|
||||
let webSessionsContext: WebSessionsContext
|
||||
let otherSessionsCount: Int
|
||||
let proxySettings: ProxySettings
|
||||
let notificationAuthorizationStatus: AccessType
|
||||
let notificationWarningSuppressed: Bool
|
||||
let notificationExceptions: NotificationExceptionsList?
|
||||
let inAppNotificationSettings: InAppNotificationSettings
|
||||
let privacySettings: AccountPrivacySettings?
|
||||
let unreadTrendingStickerPacks: Int
|
||||
let archivedStickerPacks: [ArchivedStickerPackItem]?
|
||||
let hasPassport: Bool
|
||||
let hasWatchApp: Bool
|
||||
let enableQRLogin: Bool
|
||||
|
||||
init(
|
||||
suggestPhoneNumberConfirmation: Bool,
|
||||
accountsAndPeers: [(Account, Peer, Int32)],
|
||||
activeSessionsContext: ActiveSessionsContext,
|
||||
webSessionsContext: WebSessionsContext,
|
||||
otherSessionsCount: Int,
|
||||
proxySettings: ProxySettings,
|
||||
notificationAuthorizationStatus: AccessType,
|
||||
notificationWarningSuppressed: Bool,
|
||||
notificationExceptions: NotificationExceptionsList?,
|
||||
inAppNotificationSettings: InAppNotificationSettings,
|
||||
privacySettings: AccountPrivacySettings?,
|
||||
unreadTrendingStickerPacks: Int,
|
||||
archivedStickerPacks: [ArchivedStickerPackItem]?,
|
||||
hasPassport: Bool,
|
||||
hasWatchApp: Bool,
|
||||
enableQRLogin: Bool
|
||||
) {
|
||||
self.suggestPhoneNumberConfirmation = suggestPhoneNumberConfirmation
|
||||
self.accountsAndPeers = accountsAndPeers
|
||||
self.activeSessionsContext = activeSessionsContext
|
||||
self.webSessionsContext = webSessionsContext
|
||||
self.otherSessionsCount = otherSessionsCount
|
||||
self.proxySettings = proxySettings
|
||||
self.notificationAuthorizationStatus = notificationAuthorizationStatus
|
||||
self.notificationWarningSuppressed = notificationWarningSuppressed
|
||||
self.notificationExceptions = notificationExceptions
|
||||
self.inAppNotificationSettings = inAppNotificationSettings
|
||||
self.privacySettings = privacySettings
|
||||
self.unreadTrendingStickerPacks = unreadTrendingStickerPacks
|
||||
self.archivedStickerPacks = archivedStickerPacks
|
||||
self.hasPassport = hasPassport
|
||||
self.hasWatchApp = hasWatchApp
|
||||
self.enableQRLogin = enableQRLogin
|
||||
}
|
||||
}
|
||||
|
||||
final class PeerInfoScreenData {
|
||||
@ -67,6 +141,7 @@ final class PeerInfoScreenData {
|
||||
let linkedDiscussionPeer: Peer?
|
||||
let members: PeerInfoMembersData?
|
||||
let encryptionKeyFingerprint: SecretChatKeyFingerprint?
|
||||
let globalSettings: TelegramGlobalSettings?
|
||||
|
||||
init(
|
||||
peer: Peer?,
|
||||
@ -79,7 +154,8 @@ final class PeerInfoScreenData {
|
||||
groupsInCommon: GroupsInCommonContext?,
|
||||
linkedDiscussionPeer: Peer?,
|
||||
members: PeerInfoMembersData?,
|
||||
encryptionKeyFingerprint: SecretChatKeyFingerprint?
|
||||
encryptionKeyFingerprint: SecretChatKeyFingerprint?,
|
||||
globalSettings: TelegramGlobalSettings?
|
||||
) {
|
||||
self.peer = peer
|
||||
self.cachedData = cachedData
|
||||
@ -92,6 +168,7 @@ final class PeerInfoScreenData {
|
||||
self.linkedDiscussionPeer = linkedDiscussionPeer
|
||||
self.members = members
|
||||
self.encryptionKeyFingerprint = encryptionKeyFingerprint
|
||||
self.globalSettings = globalSettings
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,10 +176,12 @@ private enum PeerInfoScreenInputUserKind {
|
||||
case user
|
||||
case bot
|
||||
case support
|
||||
case settings
|
||||
}
|
||||
|
||||
private enum PeerInfoScreenInputData: Equatable {
|
||||
case none
|
||||
case settings
|
||||
case user(userId: PeerId, secretChatId: PeerId?, kind: PeerInfoScreenInputUserKind)
|
||||
case channel
|
||||
case group(groupId: PeerId)
|
||||
@ -183,22 +262,26 @@ enum PeerInfoMembersData: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
private func peerInfoScreenInputData(context: AccountContext, peerId: PeerId) -> Signal<PeerInfoScreenInputData, NoError> {
|
||||
private func peerInfoScreenInputData(context: AccountContext, peerId: PeerId, isSettings: Bool) -> Signal<PeerInfoScreenInputData, NoError> {
|
||||
return context.account.postbox.combinedView(keys: [.basicPeer(peerId)])
|
||||
|> map { view -> PeerInfoScreenInputData in
|
||||
guard let peer = (view.views[.basicPeer(peerId)] as? BasicPeerView)?.peer else {
|
||||
return .none
|
||||
}
|
||||
if let user = peer as? TelegramUser {
|
||||
let kind: PeerInfoScreenInputUserKind
|
||||
if user.flags.contains(.isSupport) {
|
||||
kind = .support
|
||||
} else if user.botInfo != nil {
|
||||
kind = .bot
|
||||
if isSettings && user.id == context.account.peerId {
|
||||
return .settings
|
||||
} else {
|
||||
kind = .user
|
||||
let kind: PeerInfoScreenInputUserKind
|
||||
if user.flags.contains(.isSupport) {
|
||||
kind = .support
|
||||
} else if user.botInfo != nil {
|
||||
kind = .bot
|
||||
} else {
|
||||
kind = .user
|
||||
}
|
||||
return .user(userId: user.id, secretChatId: nil, kind: kind)
|
||||
}
|
||||
return .user(userId: user.id, secretChatId: nil, kind: kind)
|
||||
} else if let channel = peer as? TelegramChannel {
|
||||
if case .group = channel.info {
|
||||
return .group(groupId: channel.id)
|
||||
@ -248,10 +331,10 @@ func peerInfoProfilePhotosWithCache(context: AccountContext, peerId: PeerId) ->
|
||||
}
|
||||
|
||||
func keepPeerInfoScreenDataHot(context: AccountContext, peerId: PeerId) -> Signal<Never, NoError> {
|
||||
return peerInfoScreenInputData(context: context, peerId: peerId)
|
||||
return peerInfoScreenInputData(context: context, peerId: peerId, isSettings: false)
|
||||
|> mapToSignal { inputData -> Signal<Never, NoError> in
|
||||
switch inputData {
|
||||
case .none:
|
||||
case .none, .settings:
|
||||
return .complete()
|
||||
case .user, .channel, .group:
|
||||
return combineLatest(
|
||||
@ -263,11 +346,92 @@ func keepPeerInfoScreenDataHot(context: AccountContext, peerId: PeerId) -> Signa
|
||||
}
|
||||
}
|
||||
|
||||
func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, ignoreGroupInCommon: PeerId?) -> Signal<PeerInfoScreenData, NoError> {
|
||||
return peerInfoScreenInputData(context: context, peerId: peerId)
|
||||
func peerInfoScreenSettingsData(context: AccountContext, peerId: PeerId, accountsAndPeers: Signal<[(Account, Peer, Int32)], NoError>, activeSessionsContextAndCount: Signal<(ActiveSessionsContext, Int, WebSessionsContext), NoError>, notificationExceptions: Signal<NotificationExceptionsList?, NoError>, privacySettings: Signal<AccountPrivacySettings?, NoError>, archivedStickerPacks: Signal<[ArchivedStickerPackItem]?, NoError>, hasPassport: Signal<Bool, NoError>) -> Signal<PeerInfoScreenData, NoError> {
|
||||
let preferences = context.sharedContext.accountManager.sharedData(keys: [
|
||||
SharedDataKeys.proxySettings,
|
||||
ApplicationSpecificSharedDataKeys.inAppNotificationSettings,
|
||||
ApplicationSpecificSharedDataKeys.experimentalUISettings
|
||||
])
|
||||
|
||||
let notificationsAuthorizationStatus = Promise<AccessType>(.allowed)
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
notificationsAuthorizationStatus.set(
|
||||
.single(.allowed)
|
||||
|> then(DeviceAccess.authorizationStatus(applicationInForeground: context.sharedContext.applicationBindings.applicationInForeground, subject: .notifications)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
let notificationsWarningSuppressed = Promise<Bool>(true)
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
notificationsWarningSuppressed.set(
|
||||
.single(true)
|
||||
|> then(context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.permissionWarningKey(permission: .notifications)!)
|
||||
|> map { noticeView -> Bool in
|
||||
let timestamp = noticeView.value.flatMap({ ApplicationSpecificNotice.getTimestampValue($0) })
|
||||
if let timestamp = timestamp, timestamp > 0 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return combineLatest(
|
||||
context.account.viewTracker.peerView(peerId, updateData: true),
|
||||
accountsAndPeers,
|
||||
activeSessionsContextAndCount,
|
||||
privacySettings,
|
||||
preferences,
|
||||
combineLatest(notificationExceptions, notificationsAuthorizationStatus.get(), notificationsWarningSuppressed.get()),
|
||||
combineLatest(context.account.viewTracker.featuredStickerPacks(), archivedStickerPacks),
|
||||
hasPassport,
|
||||
(context.watchManager?.watchAppInstalled ?? .single(false)),
|
||||
context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|
||||
)
|
||||
|> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, hasWatchApp, accountPreferences -> PeerInfoScreenData in
|
||||
let (notificationExceptions, notificationsAuthorizationStatus, notificationsWarningSuppressed) = notifications
|
||||
let (featuredStickerPacks, archivedStickerPacks) = stickerPacks
|
||||
|
||||
let proxySettings: ProxySettings = sharedPreferences.entries[SharedDataKeys.proxySettings] as? ProxySettings ?? ProxySettings.defaultSettings
|
||||
let inAppNotificationSettings: InAppNotificationSettings = sharedPreferences.entries[ApplicationSpecificSharedDataKeys.inAppNotificationSettings] as? InAppNotificationSettings ?? InAppNotificationSettings.defaultSettings
|
||||
let experimentalUISettings: ExperimentalUISettings = sharedPreferences.entries[ApplicationSpecificSharedDataKeys.experimentalUISettings] as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings
|
||||
|
||||
let unreadTrendingStickerPacks = featuredStickerPacks.reduce(0, { count, item -> Int in
|
||||
return item.unread ? count + 1 : count
|
||||
})
|
||||
|
||||
var enableQRLogin = false
|
||||
if let appConfiguration = accountPreferences.values[PreferencesKeys.appConfiguration] as? AppConfiguration, let data = appConfiguration.data, let enableQR = data["qr_login_camera"] as? Bool, enableQR {
|
||||
enableQRLogin = true
|
||||
}
|
||||
|
||||
let globalSettings = TelegramGlobalSettings(suggestPhoneNumberConfirmation: false, accountsAndPeers: accountsAndPeers, activeSessionsContext: accountSessions.0, webSessionsContext: accountSessions.2, otherSessionsCount: accountSessions.1, proxySettings: proxySettings, notificationAuthorizationStatus: notificationsAuthorizationStatus, notificationWarningSuppressed: notificationsWarningSuppressed, notificationExceptions: notificationExceptions, inAppNotificationSettings: inAppNotificationSettings, privacySettings: privacySettings, unreadTrendingStickerPacks: unreadTrendingStickerPacks, archivedStickerPacks: archivedStickerPacks, hasPassport: hasPassport, hasWatchApp: hasWatchApp, enableQRLogin: enableQRLogin)
|
||||
|
||||
return PeerInfoScreenData(
|
||||
peer: peerView.peers[peerId],
|
||||
cachedData: peerView.cachedData,
|
||||
status: nil,
|
||||
notificationSettings: nil,
|
||||
globalNotificationSettings: nil,
|
||||
isContact: false,
|
||||
availablePanes: [],
|
||||
groupsInCommon: nil,
|
||||
linkedDiscussionPeer: nil,
|
||||
members: nil,
|
||||
encryptionKeyFingerprint: nil,
|
||||
globalSettings: globalSettings
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, isSettings: Bool, ignoreGroupInCommon: PeerId?) -> Signal<PeerInfoScreenData, NoError> {
|
||||
return peerInfoScreenInputData(context: context, peerId: peerId, isSettings: isSettings)
|
||||
|> mapToSignal { inputData -> Signal<PeerInfoScreenData, NoError> in
|
||||
switch inputData {
|
||||
case .none:
|
||||
case .none, .settings:
|
||||
return .single(PeerInfoScreenData(
|
||||
peer: nil,
|
||||
cachedData: nil,
|
||||
@ -279,7 +443,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
groupsInCommon: nil,
|
||||
linkedDiscussionPeer: nil,
|
||||
members: nil,
|
||||
encryptionKeyFingerprint: nil
|
||||
encryptionKeyFingerprint: nil,
|
||||
globalSettings: nil
|
||||
))
|
||||
case let .user(userPeerId, secretChatId, kind):
|
||||
let groupsInCommon: GroupsInCommonContext?
|
||||
@ -417,7 +582,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
groupsInCommon: groupsInCommon,
|
||||
linkedDiscussionPeer: nil,
|
||||
members: nil,
|
||||
encryptionKeyFingerprint: encryptionKeyFingerprint
|
||||
encryptionKeyFingerprint: encryptionKeyFingerprint,
|
||||
globalSettings: nil
|
||||
)
|
||||
}
|
||||
case .channel:
|
||||
@ -467,7 +633,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
groupsInCommon: nil,
|
||||
linkedDiscussionPeer: discussionPeer,
|
||||
members: nil,
|
||||
encryptionKeyFingerprint: nil
|
||||
encryptionKeyFingerprint: nil,
|
||||
globalSettings: nil
|
||||
)
|
||||
}
|
||||
case let .group(groupId):
|
||||
@ -604,14 +771,18 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
groupsInCommon: nil,
|
||||
linkedDiscussionPeer: discussionPeer,
|
||||
members: membersData,
|
||||
encryptionKeyFingerprint: nil
|
||||
encryptionKeyFingerprint: nil,
|
||||
globalSettings: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func canEditPeerInfo(peer: Peer?) -> Bool {
|
||||
func canEditPeerInfo(context: AccountContext, peer: Peer?) -> Bool {
|
||||
if context.account.peerId == peer?.id {
|
||||
return true
|
||||
}
|
||||
if let channel = peer as? TelegramChannel {
|
||||
if channel.hasPermission(.changeInfo) {
|
||||
return true
|
||||
@ -639,12 +810,15 @@ struct PeerInfoMemberActions: OptionSet {
|
||||
|
||||
static let restrict = PeerInfoMemberActions(rawValue: 1 << 0)
|
||||
static let promote = PeerInfoMemberActions(rawValue: 1 << 1)
|
||||
static let logout = PeerInfoMemberActions(rawValue: 1 << 2)
|
||||
}
|
||||
|
||||
func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer, member: PeerInfoMember) -> PeerInfoMemberActions {
|
||||
func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer?, member: PeerInfoMember) -> PeerInfoMemberActions {
|
||||
var result: PeerInfoMemberActions = []
|
||||
|
||||
if member.id != accountPeerId {
|
||||
if peer == nil {
|
||||
result.insert(.logout)
|
||||
} else if member.id != accountPeerId {
|
||||
if let channel = peer as? TelegramChannel {
|
||||
if channel.flags.contains(.isCreator) {
|
||||
result.insert(.restrict)
|
||||
@ -671,6 +845,8 @@ func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer, member:
|
||||
}
|
||||
case .legacyGroupMember:
|
||||
break
|
||||
case .account:
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if let group = peer as? TelegramGroup {
|
||||
@ -687,6 +863,8 @@ func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer, member:
|
||||
}
|
||||
case .channelMember:
|
||||
break
|
||||
case .account:
|
||||
break
|
||||
}
|
||||
case .member:
|
||||
switch member {
|
||||
@ -696,6 +874,8 @@ func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer, member:
|
||||
}
|
||||
case .channelMember:
|
||||
break
|
||||
case .account:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import TelegramPresentationData
|
||||
import PhotoResources
|
||||
import PeerAvatarGalleryUI
|
||||
import TelegramStringFormatting
|
||||
import PhoneNumberFormat
|
||||
import ActivityIndicator
|
||||
import TelegramUniversalVideoContent
|
||||
import GalleryUI
|
||||
@ -180,6 +181,8 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
let isReady = Promise<Bool>()
|
||||
private var didSetReady: Bool = false
|
||||
|
||||
var item: PeerInfoAvatarListItem?
|
||||
|
||||
var isExpanded: Bool = false {
|
||||
didSet {
|
||||
if let videoNode = self.videoNode, videoNode.canAttachContent != self.isExpanded {
|
||||
@ -190,7 +193,7 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var isCentral: Bool = false
|
||||
|
||||
init(context: AccountContext) {
|
||||
@ -229,6 +232,8 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func setup(item: PeerInfoAvatarListItem, synchronous: Bool) {
|
||||
self.item = item
|
||||
|
||||
let representations: [ImageRepresentationWithReference]
|
||||
let videoRepresentations: [TelegramMediaImage.VideoRepresentation]
|
||||
let immediateThumbnailData: Data?
|
||||
@ -339,7 +344,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
||||
private var stripNodes: [ASImageNode] = []
|
||||
private let activeStripImage: UIImage
|
||||
private var appliedStripNodeCurrentIndex: Int?
|
||||
private var currentIndex: Int = 0
|
||||
var currentIndex: Int = 0
|
||||
private var transitionFraction: CGFloat = 0.0
|
||||
|
||||
private var validLayout: CGSize?
|
||||
@ -970,7 +975,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
|
||||
func update(peer: Peer?, updatingAvatar: PeerInfoUpdatingAvatar?, theme: PresentationTheme, avatarSize: CGFloat) {
|
||||
if let peer = peer {
|
||||
let overrideImage: AvatarNodeImageOverride?
|
||||
if canEditPeerInfo(peer: peer) {
|
||||
if canEditPeerInfo(context: self.context, peer: peer) {
|
||||
if let updatingAvatar = updatingAvatar {
|
||||
switch updatingAvatar {
|
||||
case let .image(representation):
|
||||
@ -1174,6 +1179,8 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
|
||||
case .search:
|
||||
text = ""
|
||||
icon = PresentationResourcesRootController.navigationCompactSearchIcon(presentationData.theme)
|
||||
case .editPhoto:
|
||||
text = presentationData.strings.Settings_EditPhoto
|
||||
}
|
||||
|
||||
let font: UIFont = isBold ? Font.semibold(17.0) : Font.regular(17.0)
|
||||
@ -1211,6 +1218,7 @@ enum PeerInfoHeaderNavigationButtonKey {
|
||||
case select
|
||||
case selectionDone
|
||||
case search
|
||||
case editPhoto
|
||||
}
|
||||
|
||||
struct PeerInfoHeaderNavigationButtonSpec: Equatable {
|
||||
@ -1627,6 +1635,7 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
|
||||
private let requestUpdateLayout: () -> Void
|
||||
|
||||
let avatarNode: PeerInfoEditingAvatarNode
|
||||
let avatarTextNode: HighlightableButtonNode
|
||||
|
||||
var itemNodes: [PeerInfoHeaderTextFieldNodeKey: PeerInfoHeaderTextFieldNode] = [:]
|
||||
|
||||
@ -1636,9 +1645,17 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
|
||||
|
||||
self.avatarNode = PeerInfoEditingAvatarNode(context: context)
|
||||
|
||||
self.avatarTextNode = HighlightableButtonNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.avatarNode)
|
||||
|
||||
self.avatarTextNode.addTarget(self, action: #selector(textPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
@objc private func textPressed() {
|
||||
self.avatarNode.tapped?()
|
||||
}
|
||||
|
||||
func editingTextForKey(_ key: PeerInfoHeaderTextFieldNodeKey) -> String? {
|
||||
@ -1649,13 +1666,25 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
|
||||
self.itemNodes[key]?.layer.addShakeAnimation()
|
||||
}
|
||||
|
||||
func update(width: CGFloat, safeInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, peer: Peer?, cachedData: CachedPeerData?, isContact: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
func update(width: CGFloat, safeInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, peer: Peer?, cachedData: CachedPeerData?, isContact: Bool, isSettings: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 10.0), size: CGSize(width: avatarSize, height: avatarSize))
|
||||
transition.updateFrameAdditiveToCenter(node: self.avatarNode, frame: CGRect(origin: avatarFrame.center, size: CGSize()))
|
||||
|
||||
var contentHeight: CGFloat = statusBarHeight + 10.0 + avatarSize + 20.0
|
||||
|
||||
if isSettings {
|
||||
if self.avatarTextNode.supernode == nil {
|
||||
self.addSubnode(self.avatarTextNode)
|
||||
}
|
||||
|
||||
self.avatarTextNode.setAttributedTitle(NSAttributedString(string: presentationData.strings.Settings_SetNewProfilePhotoOrVideo, font: Font.regular(17.0), textColor: presentationData.theme.list.itemAccentColor), for: [])
|
||||
|
||||
let avatarTextSize = self.avatarTextNode.measure(CGSize(width: width, height: 32.0))
|
||||
transition.updateFrame(node: self.avatarTextNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((width - avatarTextSize.width) / 2.0), y: contentHeight - 1.0), size: avatarTextSize))
|
||||
contentHeight += 32.0
|
||||
}
|
||||
|
||||
var fieldKeys: [PeerInfoHeaderTextFieldNodeKey] = []
|
||||
if let user = peer as? TelegramUser {
|
||||
if !user.isDeleted {
|
||||
@ -1666,12 +1695,12 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
|
||||
}
|
||||
} else if let _ = peer as? TelegramGroup {
|
||||
fieldKeys.append(.title)
|
||||
if canEditPeerInfo(peer: peer) {
|
||||
if canEditPeerInfo(context: self.context, peer: peer) {
|
||||
fieldKeys.append(.description)
|
||||
}
|
||||
} else if let _ = peer as? TelegramChannel {
|
||||
fieldKeys.append(.title)
|
||||
if canEditPeerInfo(peer: peer) {
|
||||
if canEditPeerInfo(context: self.context, peer: peer) {
|
||||
fieldKeys.append(.description)
|
||||
}
|
||||
}
|
||||
@ -1715,20 +1744,20 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
|
||||
switch key {
|
||||
case .firstName:
|
||||
placeholder = presentationData.strings.UserInfo_FirstNamePlaceholder
|
||||
isEnabled = isContact
|
||||
isEnabled = isContact || isSettings
|
||||
case .lastName:
|
||||
placeholder = presentationData.strings.UserInfo_LastNamePlaceholder
|
||||
isEnabled = isContact
|
||||
isEnabled = isContact || isSettings
|
||||
case .title:
|
||||
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
placeholder = presentationData.strings.GroupInfo_ChannelListNamePlaceholder
|
||||
} else {
|
||||
placeholder = presentationData.strings.GroupInfo_GroupNamePlaceholder
|
||||
}
|
||||
isEnabled = canEditPeerInfo(peer: peer)
|
||||
isEnabled = canEditPeerInfo(context: self.context, peer: peer)
|
||||
case .description:
|
||||
placeholder = presentationData.strings.Channel_Edit_AboutItem
|
||||
isEnabled = canEditPeerInfo(peer: peer)
|
||||
isEnabled = canEditPeerInfo(context: self.context, peer: peer)
|
||||
}
|
||||
let itemHeight = itemNode.update(width: width, safeInset: safeInset, hasPrevious: hasPrevious, placeholder: placeholder, isEnabled: isEnabled, presentationData: presentationData, updateText: updateText)
|
||||
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: width, height: itemHeight)))
|
||||
@ -1759,6 +1788,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
private var presentationData: PresentationData?
|
||||
|
||||
private let isOpenedFromChat: Bool
|
||||
private let isSettings: Bool
|
||||
private let videoCallsEnabled: Bool
|
||||
|
||||
private(set) var isAvatarExpanded: Bool
|
||||
@ -1775,6 +1805,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let subtitleNodeContainer: ASDisplayNode
|
||||
let subtitleNodeRawContainer: ASDisplayNode
|
||||
let subtitleNode: MultiScaleTextNode
|
||||
let usernameNodeContainer: ASDisplayNode
|
||||
let usernameNodeRawContainer: ASDisplayNode
|
||||
let usernameNode: MultiScaleTextNode
|
||||
private var buttonNodes: [PeerInfoHeaderButtonKey: PeerInfoHeaderButtonNode] = [:]
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let expandedBackgroundNode: ASDisplayNode
|
||||
@ -1788,10 +1821,11 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
|
||||
var navigationTransition: PeerInfoHeaderNavigationTransition?
|
||||
|
||||
init(context: AccountContext, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool) {
|
||||
init(context: AccountContext, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isSettings: Bool) {
|
||||
self.context = context
|
||||
self.isAvatarExpanded = avatarInitiallyExpanded
|
||||
self.isOpenedFromChat = isOpenedFromChat
|
||||
self.isSettings = isSettings
|
||||
self.videoCallsEnabled = context.sharedContext.immediateExperimentalUISettings.videoCalls
|
||||
|
||||
self.avatarListNode = PeerInfoAvatarListNode(context: context, readyWhenGalleryLoads: avatarInitiallyExpanded)
|
||||
@ -1816,6 +1850,11 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.subtitleNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded])
|
||||
self.subtitleNode.displaysAsynchronously = false
|
||||
|
||||
self.usernameNodeContainer = ASDisplayNode()
|
||||
self.usernameNodeRawContainer = ASDisplayNode()
|
||||
self.usernameNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded])
|
||||
self.usernameNode.displaysAsynchronously = false
|
||||
|
||||
self.regularContentNode = PeerInfoHeaderRegularContentNode()
|
||||
var requestUpdateLayoutImpl: (() -> Void)?
|
||||
self.editingContentNode = PeerInfoHeaderEditingContentNode(context: context, requestUpdateLayout: {
|
||||
@ -1846,6 +1885,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.regularContentNode.addSubnode(self.titleNodeContainer)
|
||||
self.subtitleNodeContainer.addSubnode(self.subtitleNode)
|
||||
self.regularContentNode.addSubnode(self.subtitleNodeContainer)
|
||||
self.usernameNodeContainer.addSubnode(self.usernameNode)
|
||||
self.regularContentNode.addSubnode(self.usernameNodeContainer)
|
||||
self.regularContentNode.addSubnode(self.avatarListNode)
|
||||
self.regularContentNode.addSubnode(self.avatarListNode.listContainerNode.controlsClippingOffsetNode)
|
||||
self.addSubnode(self.regularContentNode)
|
||||
@ -1910,7 +1951,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.avatarListNode.listContainerNode.updateEntryIsHidden(entry: entry)
|
||||
}
|
||||
|
||||
func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, notificationSettings: TelegramPeerNotificationSettings?, statusData: PeerInfoStatusData?, isContact: Bool, state: PeerInfoState, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat {
|
||||
func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, notificationSettings: TelegramPeerNotificationSettings?, statusData: PeerInfoStatusData?, isContact: Bool, isSettings: Bool, state: PeerInfoState, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat {
|
||||
var contentOffset = contentOffset
|
||||
|
||||
if isMediaOnly {
|
||||
@ -1942,7 +1983,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.regularContentNode.alpha = state.isEditing ? 0.0 : 1.0
|
||||
self.editingContentNode.alpha = state.isEditing ? 1.0 : 0.0
|
||||
|
||||
let editingContentHeight = self.editingContentNode.update(width: width, safeInset: containerInset, statusBarHeight: statusBarHeight, navigationHeight: navigationHeight, isModalOverlay: isModalOverlay, peer: state.isEditing ? peer : nil, cachedData: cachedData, isContact: isContact, presentationData: presentationData, transition: transition)
|
||||
let editingContentHeight = self.editingContentNode.update(width: width, safeInset: containerInset, statusBarHeight: statusBarHeight, navigationHeight: navigationHeight, isModalOverlay: isModalOverlay, peer: state.isEditing ? peer : nil, cachedData: cachedData, isContact: isContact, isSettings: isSettings, presentationData: presentationData, transition: transition)
|
||||
transition.updateFrame(node: self.editingContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -contentOffset), size: CGSize(width: width, height: editingContentHeight)))
|
||||
|
||||
var transitionSourceHeight: CGFloat = 0.0
|
||||
@ -1982,23 +2023,28 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let expandedAvatarListHeight = min(width, containerHeight - expandedAvatarControlsHeight)
|
||||
let expandedAvatarListSize = CGSize(width: width, height: expandedAvatarListHeight)
|
||||
|
||||
let buttonKeys: [PeerInfoHeaderButtonKey] = peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat, videoCallsEnabled: self.videoCallsEnabled)
|
||||
let buttonKeys: [PeerInfoHeaderButtonKey] = self.isSettings ? [] : peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat, videoCallsEnabled: self.videoCallsEnabled)
|
||||
|
||||
var isVerified = false
|
||||
let titleString: NSAttributedString
|
||||
let subtitleString: NSAttributedString
|
||||
let usernameString: NSAttributedString
|
||||
if let peer = peer, peer.isVerified {
|
||||
isVerified = true
|
||||
}
|
||||
|
||||
if let peer = peer {
|
||||
if peer.id == self.context.account.peerId {
|
||||
if peer.id == self.context.account.peerId && !self.isSettings {
|
||||
titleString = NSAttributedString(string: presentationData.strings.Conversation_SavedMessages, font: Font.medium(24.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
|
||||
} else {
|
||||
titleString = NSAttributedString(string: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), font: Font.medium(24.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
|
||||
}
|
||||
|
||||
if let statusData = statusData {
|
||||
if self.isSettings, let user = peer as? TelegramUser {
|
||||
let formattedPhone = formatPhoneNumber(user.phone ?? "")
|
||||
subtitleString = NSAttributedString(string: formattedPhone, font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
|
||||
usernameString = NSAttributedString(string: user.addressName.flatMap { "@\($0)" } ?? "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemAccentColor)
|
||||
} else if let statusData = statusData {
|
||||
let subtitleColor: UIColor
|
||||
if statusData.isActivity {
|
||||
subtitleColor = presentationData.theme.list.itemAccentColor
|
||||
@ -2006,12 +2052,15 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
subtitleColor = presentationData.theme.list.itemSecondaryTextColor
|
||||
}
|
||||
subtitleString = NSAttributedString(string: statusData.text, font: Font.regular(15.0), textColor: subtitleColor)
|
||||
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemAccentColor)
|
||||
} else {
|
||||
subtitleString = NSAttributedString(string: " ", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
|
||||
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemAccentColor)
|
||||
}
|
||||
} else {
|
||||
titleString = NSAttributedString(string: " ", font: Font.medium(24.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
|
||||
subtitleString = NSAttributedString(string: " ", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
|
||||
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemAccentColor)
|
||||
}
|
||||
|
||||
let textSideInset: CGFloat = 44.0
|
||||
@ -2029,6 +2078,11 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
TitleNodeStateExpanded: MultiScaleTextState(attributedText: subtitleString, constrainedSize: CGSize(width: titleConstrainedSize.width - 82.0, height: titleConstrainedSize.height))
|
||||
], mainState: TitleNodeStateRegular)
|
||||
|
||||
let usernameNodeLayout = self.usernameNode.updateLayout(states: [
|
||||
TitleNodeStateRegular: MultiScaleTextState(attributedText: usernameString, constrainedSize: CGSize(width: titleConstrainedSize.width, height: titleConstrainedSize.height)),
|
||||
TitleNodeStateExpanded: MultiScaleTextState(attributedText: usernameString, constrainedSize: CGSize(width: titleConstrainedSize.width - titleNodeLayout[TitleNodeStateExpanded]!.size.width - 8.0, height: titleConstrainedSize.height))
|
||||
], mainState: TitleNodeStateRegular)
|
||||
|
||||
self.titleNode.update(stateFractions: [
|
||||
TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0,
|
||||
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
|
||||
@ -2039,14 +2093,19 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
|
||||
], transition: transition)
|
||||
|
||||
self.usernameNode.update(stateFractions: [
|
||||
TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0,
|
||||
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
|
||||
], transition: transition)
|
||||
|
||||
let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 10.0), size: CGSize(width: avatarSize, height: avatarSize))
|
||||
let avatarCenter = CGPoint(x: (1.0 - transitionFraction) * avatarFrame.midX + transitionFraction * transitionSourceAvatarFrame.midX, y: (1.0 - transitionFraction) * avatarFrame.midY + transitionFraction * transitionSourceAvatarFrame.midY)
|
||||
let avatarAlpha = transitionFraction
|
||||
|
||||
let titleSize = titleNodeLayout[TitleNodeStateRegular]!.size
|
||||
let titleExpandedSize = titleNodeLayout[TitleNodeStateExpanded]!.size
|
||||
let subtitleSize = subtitleNodeLayout[TitleNodeStateRegular]!.size
|
||||
let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size
|
||||
|
||||
if let image = self.titleCredibilityIconNode.image {
|
||||
transition.updateFrame(node: self.titleCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleSize.width + 4.0, y: floor((titleSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
|
||||
@ -2058,14 +2117,25 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
|
||||
let titleFrame: CGRect
|
||||
let subtitleFrame: CGRect
|
||||
let usernameFrame: CGRect
|
||||
let usernameSpacing: CGFloat = 4.0
|
||||
if self.isAvatarExpanded {
|
||||
let minTitleSize = CGSize(width: titleSize.width * 0.7, height: titleSize.height * 0.7)
|
||||
let minTitleFrame = CGRect(origin: CGPoint(x: 16.0, y: expandedAvatarHeight - expandedAvatarControlsHeight + 9.0 + (subtitleSize.height.isZero ? 10.0 : 0.0)), size: minTitleSize)
|
||||
titleFrame = CGRect(origin: CGPoint(x: minTitleFrame.midX - titleSize.width / 2.0, y: minTitleFrame.midY - titleSize.height / 2.0), size: titleSize)
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: 16.0, y: minTitleFrame.maxY + 4.0), size: subtitleSize)
|
||||
usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize)
|
||||
} else {
|
||||
titleFrame = CGRect(origin: CGPoint(x: floor((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 10.0 + (subtitleSize.height.isZero ? 11.0 : 0.0)), size: titleSize)
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||
|
||||
let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width
|
||||
if usernameSize.width == 0.0 || totalSubtitleWidth > width - textSideInset * 2.0 {
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||
usernameFrame = CGRect(origin: CGPoint(x: floor((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize)
|
||||
} else {
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - totalSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||
usernameFrame = CGRect(origin: CGPoint(x: subtitleFrame.maxX + usernameSpacing, y: titleFrame.maxY + 1.0), size: usernameSize)
|
||||
}
|
||||
}
|
||||
|
||||
let singleTitleLockOffset: CGFloat = (peer?.id == self.context.account.peerId || subtitleSize.height.isZero) ? 8.0 : 0.0
|
||||
@ -2190,7 +2260,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
|
||||
self.avatarListNode.listContainerNode.update(size: expandedAvatarListSize, peer: peer, isExpanded: self.isAvatarExpanded, transition: transition)
|
||||
|
||||
let panelWithAvatarHeight: CGFloat = 112.0 + avatarSize
|
||||
let panelWithAvatarHeight: CGFloat = (self.isSettings ? 40.0 : 112.0) + avatarSize
|
||||
let buttonsCollapseStart = titleCollapseOffset
|
||||
let buttonsCollapseEnd = panelWithAvatarHeight - (navigationHeight - statusBarHeight) + 10.0
|
||||
|
||||
@ -2231,8 +2301,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.subtitleNodeRawContainer.frame = rawSubtitleFrame
|
||||
transition.updateFrameAdditiveToCenter(node: self.subtitleNodeContainer, frame: CGRect(origin: rawSubtitleFrame.center, size: CGSize()))
|
||||
transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
|
||||
transition.updateFrame(node: self.usernameNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
|
||||
transition.updateSublayerTransformScale(node: self.titleNodeContainer, scale: titleScale)
|
||||
transition.updateSublayerTransformScale(node: self.subtitleNodeContainer, scale: subtitleScale)
|
||||
transition.updateSublayerTransformScale(node: self.usernameNodeContainer, scale: subtitleScale)
|
||||
if self.isSettings {
|
||||
transition.updateAlpha(node: self.subtitleNode, alpha: 1.0 - titleCollapseFraction, beginWithCurrentState: true)
|
||||
transition.updateAlpha(node: self.usernameNode, alpha: 1.0 - titleCollapseFraction, beginWithCurrentState: true)
|
||||
}
|
||||
} else {
|
||||
let titleScale: CGFloat
|
||||
let subtitleScale: CGFloat
|
||||
@ -2249,16 +2325,26 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
|
||||
let rawSubtitleFrame = subtitleFrame
|
||||
self.subtitleNodeRawContainer.frame = rawSubtitleFrame
|
||||
let rawUsernameFrame = usernameFrame
|
||||
self.usernameNodeRawContainer.frame = rawUsernameFrame
|
||||
if self.isAvatarExpanded {
|
||||
transition.updateFrameAdditive(node: self.titleNodeContainer, frame: CGRect(origin: rawTitleFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset + apparentTitleLockOffset))
|
||||
transition.updateFrameAdditive(node: self.subtitleNodeContainer, frame: CGRect(origin: rawSubtitleFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset))
|
||||
transition.updateFrameAdditive(node: self.usernameNodeContainer, frame: CGRect(origin: rawUsernameFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset))
|
||||
} else {
|
||||
transition.updateFrameAdditiveToCenter(node: self.titleNodeContainer, frame: CGRect(origin: rawTitleFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset + apparentTitleLockOffset))
|
||||
transition.updateFrameAdditiveToCenter(node: self.subtitleNodeContainer, frame: CGRect(origin: rawSubtitleFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset))
|
||||
transition.updateFrameAdditiveToCenter(node: self.usernameNodeContainer, frame: CGRect(origin: rawUsernameFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset))
|
||||
}
|
||||
transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
|
||||
transition.updateFrame(node: self.usernameNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
|
||||
transition.updateSublayerTransformScaleAdditive(node: self.titleNodeContainer, scale: titleScale)
|
||||
transition.updateSublayerTransformScaleAdditive(node: self.subtitleNodeContainer, scale: subtitleScale)
|
||||
transition.updateSublayerTransformScaleAdditive(node: self.usernameNodeContainer, scale: subtitleScale)
|
||||
if self.isSettings {
|
||||
transition.updateAlpha(node: self.subtitleNode, alpha: 1.0 - titleCollapseFraction, beginWithCurrentState: true)
|
||||
transition.updateAlpha(node: self.usernameNode, alpha: 1.0 - titleCollapseFraction, beginWithCurrentState: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2440,7 +2526,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
if self.isAvatarExpanded {
|
||||
resolvedRegularHeight = expandedAvatarListSize.height + expandedAvatarControlsHeight
|
||||
} else {
|
||||
resolvedRegularHeight = 112.0 + avatarSize + navigationHeight
|
||||
resolvedRegularHeight = (self.isSettings ? 40.0 : 112.0) + avatarSize + navigationHeight
|
||||
}
|
||||
|
||||
let backgroundFrame: CGRect
|
||||
|
@ -15,6 +15,7 @@ enum PeerInfoMemberRole {
|
||||
enum PeerInfoMember: Equatable {
|
||||
case channelMember(RenderedChannelParticipant)
|
||||
case legacyGroupMember(peer: RenderedPeer, role: PeerInfoMemberRole, invitedBy: PeerId?, presence: TelegramUserPresence?)
|
||||
case account(peer: RenderedPeer)
|
||||
|
||||
var id: PeerId {
|
||||
switch self {
|
||||
@ -22,6 +23,8 @@ enum PeerInfoMember: Equatable {
|
||||
return channelMember.peer.id
|
||||
case let .legacyGroupMember(legacyGroupMember):
|
||||
return legacyGroupMember.peer.peerId
|
||||
case let .account(peer):
|
||||
return peer.peerId
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +34,8 @@ enum PeerInfoMember: Equatable {
|
||||
return channelMember.peer
|
||||
case let .legacyGroupMember(legacyGroupMember):
|
||||
return legacyGroupMember.peer.peers[legacyGroupMember.peer.peerId]!
|
||||
case let .account(peer):
|
||||
return peer.peers[peer.peerId]!
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +45,8 @@ enum PeerInfoMember: Equatable {
|
||||
return channelMember.presences[channelMember.peer.id] as? TelegramUserPresence
|
||||
case let .legacyGroupMember(legacyGroupMember):
|
||||
return legacyGroupMember.presence
|
||||
case .account:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,6 +65,8 @@ enum PeerInfoMember: Equatable {
|
||||
}
|
||||
case let .legacyGroupMember(legacyGroupMember):
|
||||
return legacyGroupMember.role
|
||||
case .account:
|
||||
return .member
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,6 +81,8 @@ enum PeerInfoMember: Equatable {
|
||||
}
|
||||
case .legacyGroupMember:
|
||||
return nil
|
||||
case .account:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,105 @@
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import ItemListUI
|
||||
|
||||
final class PeerInfoScreenMultilineInputItem: PeerInfoScreenItem {
|
||||
let id: AnyHashable
|
||||
let text: String
|
||||
let placeholder: String
|
||||
let textUpdated: (String) -> Void
|
||||
let maxLength: Int?
|
||||
|
||||
init(
|
||||
id: AnyHashable,
|
||||
text: String,
|
||||
placeholder: String,
|
||||
textUpdated: @escaping (String) -> Void,
|
||||
maxLength: Int?
|
||||
) {
|
||||
self.id = id
|
||||
self.text = text
|
||||
self.placeholder = placeholder
|
||||
self.textUpdated = textUpdated
|
||||
self.maxLength = maxLength
|
||||
}
|
||||
|
||||
func node() -> PeerInfoScreenItemNode {
|
||||
return PeerInfoScreenMultilineInputItemNode()
|
||||
}
|
||||
}
|
||||
|
||||
private final class PeerInfoScreenMultilineInputItemNode: PeerInfoScreenItemNode {
|
||||
private let bottomSeparatorNode: ASDisplayNode
|
||||
|
||||
private var item: PeerInfoScreenMultilineInputItem?
|
||||
private var itemNode: ItemListMultilineInputItemNode?
|
||||
|
||||
override init() {
|
||||
self.bottomSeparatorNode = ASDisplayNode()
|
||||
self.bottomSeparatorNode.isLayerBacked = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.clipsToBounds = true
|
||||
|
||||
self.addSubnode(self.bottomSeparatorNode)
|
||||
}
|
||||
|
||||
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
guard let item = item as? PeerInfoScreenMultilineInputItem else {
|
||||
return 10.0
|
||||
}
|
||||
|
||||
self.item = item
|
||||
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
let inputItem = ItemListMultilineInputItem(presentationData: ItemListPresentationData(presentationData), text: item.text, placeholder: item.placeholder, maxLength: item.maxLength.flatMap { ItemListMultilineInputItemTextLimit(value: $0, display: true) }, sectionId: 0, style: .blocks, textUpdated: { updatedText in
|
||||
item.textUpdated(updatedText)
|
||||
}, noInsets: true)
|
||||
|
||||
let params = ListViewItemLayoutParams(width: width, leftInset: safeInsets.left, rightInset: safeInsets.right, availableHeight: 1000.0)
|
||||
|
||||
let itemNode: ItemListMultilineInputItemNode
|
||||
if let current = self.itemNode {
|
||||
itemNode = current
|
||||
inputItem.updateNode(async: { $0() }, node: {
|
||||
return itemNode
|
||||
}, params: params, previousItem: nil, nextItem: nil, animation: .None, completion: { (layout, apply) in
|
||||
let nodeFrame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: layout.size.height))
|
||||
|
||||
itemNode.contentSize = layout.contentSize
|
||||
itemNode.insets = layout.insets
|
||||
itemNode.frame = nodeFrame
|
||||
|
||||
apply(ListViewItemApply(isOnScreen: true))
|
||||
})
|
||||
} else {
|
||||
var itemNodeValue: ListViewItemNode?
|
||||
inputItem.nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: false, previousItem: nil, nextItem: nil, completion: { node, apply in
|
||||
itemNodeValue = node
|
||||
apply().1(ListViewItemApply(isOnScreen: true))
|
||||
})
|
||||
itemNode = itemNodeValue as! ItemListMultilineInputItemNode
|
||||
self.itemNode = itemNode
|
||||
self.addSubnode(itemNode)
|
||||
}
|
||||
|
||||
let height = itemNode.contentSize.height
|
||||
|
||||
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(), size: itemNode.bounds.size))
|
||||
|
||||
var separatorInset: CGFloat = sideInset
|
||||
if bottomItem != nil {
|
||||
separatorInset += 49.0
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: separatorInset, y: height - UIScreenPixel), size: CGSize(width: width - sideInset, height: UIScreenPixel)))
|
||||
transition.updateAlpha(node: self.bottomSeparatorNode, alpha: bottomItem == nil ? 0.0 : 1.0)
|
||||
|
||||
return height
|
||||
}
|
||||
}
|
@ -433,6 +433,8 @@ public class PeerMediaCollectionController: TelegramBaseController {
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: {
|
||||
}, greetingStickerNode: {
|
||||
return nil
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
@ -1183,6 +1183,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: {
|
||||
}, greetingStickerNode: {
|
||||
return nil
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
@ -22,7 +22,7 @@ public final class TelegramRootController: NavigationController {
|
||||
public var contactsController: ContactsController?
|
||||
public var callListController: CallListController?
|
||||
public var chatListController: ChatListController?
|
||||
public var accountSettingsController: ViewController?
|
||||
public var accountSettingsController: PeerInfoScreen?
|
||||
|
||||
private var permissionsDisposable: Disposable?
|
||||
private var presentationDataDisposable: Disposable?
|
||||
@ -110,7 +110,7 @@ public final class TelegramRootController: NavigationController {
|
||||
sharedContext.switchingData = (nil, nil, nil)
|
||||
}
|
||||
|
||||
let accountSettingsController = restoreSettignsController ?? settingsController(context: self.context, accountManager: context.sharedContext.accountManager, enableDebugActions: !GlobalExperimentalSettings.isAppStoreBuild)
|
||||
let accountSettingsController = PeerInfoScreen(context: self.context, peerId: self.context.account.peerId, avatarInitiallyExpanded: false, isOpenedFromChat: false, nearbyPeerDistance: nil, callMessages: [], isSettings: true)
|
||||
controllers.append(accountSettingsController)
|
||||
|
||||
tabBarController.setControllers(controllers, selectedIndex: restoreSettignsController != nil ? (controllers.count - 1) : (controllers.count - 2))
|
||||
|
Loading…
x
Reference in New Issue
Block a user