diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 39290bfcbb..b4c13880e2 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7549,18 +7549,18 @@ Sorry for the inconvenience."; "Chat.MultipleTypingMore" = "%@ and %@ others"; "DialogList.ExtendedPinLimitError" = "Sorry, you can pin more than **%1$@** chats to the top. Unpin some of the currently pinned ones or subscribe to **Telegram Premium** to double the limit to **%2$@** chats."; -"DialogList.ExtendedPinLimitIncrease" = "Increase Limit"; "Group.Username.RemoveExistingUsernamesTitle" = "Too Many Public Links"; "Group.Username.RemoveExistingUsernamesOrExtendInfo" = "You have reserved too many public links. Try revoking a link from an older group or channel, or upgrade to **Telegram Premium** to double the limit to **%@** public links."; -"Group.Username.IncreaseLimit" = "Increase Limit"; "OldChannels.TooManyCommunitiesTitle" = "Too Many Communities"; "OldChannels.TooManyCommunitiesText" = "You are a member of **%@** groups and channels. Please leave some before joining a new one or upgrade to **Telegram Premium** to double the limit to **%@** groups and channels."; -"OldChannels.IncreaseLimit" = "Increase Limit"; "OldChannels.LeaveCommunities_1" = "Leave %@ Community"; "OldChannels.LeaveCommunities_any" = "Leave %@ Communities"; -"Stickers.FaveLimitReachedInfo" = "Sorry, you can't add more than **%@** stickers to favorites. Replace an older saved sticker or subscribe to **Telegram Premium** to double the limit to **%@** favorites stickers."; -"Stickers.FaveLimitReplaceOlder" = "Replace Older Sticker"; -"Stickers.FaveLimitIncrease" = "Increase Limit"; +"Premium.LimitReached" = "Limit Reached"; +"Premium.IncreaseLimit" = "Increase Limit"; + +"Premium.MaxFoldersCountText" = "You have reached the limit of **%@** folders. You can double the limit to **%@** folders by subscribing to **Telegram Premium**."; +"Premium.MaxChatsInFolderCountText" = "Sorry, you can't add more than **%@** chats to a folder. You can increase this limit to **%@** by upgrading to **Telegram Premium**."; +"Premium.MaxFileSizeText" = "Double this limit to %@ per file by subscribing to **Telegram Premium**."; diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index 1dfa364c8b..addf2778a3 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -331,7 +331,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch return nil }, action: nil as ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) - subItems.append(.action(ContextMenuActionItem(text: strings.DialogList_ExtendedPinLimitIncrease, icon: { _ in + subItems.append(.action(ContextMenuActionItem(text: strings.Premium_IncreaseLimit, icon: { _ in return nil }, action: { _, f in f(.default) diff --git a/submodules/ComponentFlow/Source/Components/RoundedRectangle.swift b/submodules/ComponentFlow/Source/Components/RoundedRectangle.swift index 0d0a73060d..3c8153a594 100644 --- a/submodules/ComponentFlow/Source/Components/RoundedRectangle.swift +++ b/submodules/ComponentFlow/Source/Components/RoundedRectangle.swift @@ -2,21 +2,35 @@ import Foundation import UIKit public final class RoundedRectangle: Component { - public let color: UIColor + public enum GradientDirection: Equatable { + case horizontal + case vertical + } + + public let colors: [UIColor] public let cornerRadius: CGFloat - - public init(color: UIColor, cornerRadius: CGFloat) { - self.color = color + public let gradientDirection: GradientDirection + + public convenience init(color: UIColor, cornerRadius: CGFloat) { + self.init(colors: [color], cornerRadius: cornerRadius) + } + + public init(colors: [UIColor], cornerRadius: CGFloat, gradientDirection: GradientDirection = .horizontal) { + self.colors = colors self.cornerRadius = cornerRadius + self.gradientDirection = gradientDirection } public static func ==(lhs: RoundedRectangle, rhs: RoundedRectangle) -> Bool { - if !lhs.color.isEqual(rhs.color) { + if lhs.colors != rhs.colors { return false } if lhs.cornerRadius != rhs.cornerRadius { return false } + if lhs.gradientDirection != rhs.gradientDirection { + return false + } return true } @@ -25,14 +39,37 @@ public final class RoundedRectangle: Component { func update(component: RoundedRectangle, availableSize: CGSize, transition: Transition) -> CGSize { if self.component != component { - let imageSize = CGSize(width: component.cornerRadius * 2.0, height: component.cornerRadius * 2.0) - UIGraphicsBeginImageContextWithOptions(imageSize, false, 0.0) - if let context = UIGraphicsGetCurrentContext() { - context.setFillColor(component.color.cgColor) - context.fillEllipse(in: CGRect(origin: CGPoint(), size: imageSize)) + if component.colors.count == 1, let color = component.colors.first { + let imageSize = CGSize(width: component.cornerRadius * 2.0, height: component.cornerRadius * 2.0) + UIGraphicsBeginImageContextWithOptions(imageSize, false, 0.0) + if let context = UIGraphicsGetCurrentContext() { + context.setFillColor(color.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: imageSize)) + } + self.image = UIGraphicsGetImageFromCurrentImageContext()?.stretchableImage(withLeftCapWidth: Int(component.cornerRadius), topCapHeight: Int(component.cornerRadius)) + UIGraphicsEndImageContext() + } else if component.colors.count > 1{ + let imageSize = availableSize + UIGraphicsBeginImageContextWithOptions(imageSize, false, 0.0) + if let context = UIGraphicsGetCurrentContext() { + context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: imageSize), cornerRadius: component.cornerRadius).cgPath) + context.clip() + + let colors = component.colors + let gradientColors = colors.map { $0.cgColor } as CFArray + let colorSpace = CGColorSpaceCreateDeviceRGB() + + var locations: [CGFloat] = [] + let delta = 1.0 / CGFloat(colors.count - 1) + for i in 0 ..< colors.count { + locations.append(delta * CGFloat(i)) + } + let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: component.gradientDirection == .horizontal ? CGPoint(x: imageSize.width, y: 0.0) : CGPoint(x: 0.0, y: imageSize.height), options: CGGradientDrawingOptions()) + } + self.image = UIGraphicsGetImageFromCurrentImageContext()?.stretchableImage(withLeftCapWidth: Int(component.cornerRadius), topCapHeight: Int(component.cornerRadius)) + UIGraphicsEndImageContext() } - self.image = UIGraphicsGetImageFromCurrentImageContext()?.stretchableImage(withLeftCapWidth: Int(component.cornerRadius), topCapHeight: Int(component.cornerRadius)) - UIGraphicsEndImageContext() } return availableSize diff --git a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift index 54a6a1d0b1..8b9a814fab 100644 --- a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift +++ b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift @@ -66,6 +66,7 @@ open class ViewControllerComponentContainer: ViewController { public let isVisible: Bool public let theme: PresentationTheme public let strings: PresentationStrings + public let dateTimeFormat: PresentationDateTimeFormat public let controller: () -> ViewController? public init( @@ -75,6 +76,7 @@ open class ViewControllerComponentContainer: ViewController { isVisible: Bool, theme: PresentationTheme, strings: PresentationStrings, + dateTimeFormat: PresentationDateTimeFormat, controller: @escaping () -> ViewController? ) { self.statusBarHeight = statusBarHeight @@ -83,6 +85,7 @@ open class ViewControllerComponentContainer: ViewController { self.isVisible = isVisible self.theme = theme self.strings = strings + self.dateTimeFormat = dateTimeFormat self.controller = controller } @@ -109,6 +112,9 @@ open class ViewControllerComponentContainer: ViewController { if lhs.strings !== rhs.strings { return false } + if lhs.dateTimeFormat != rhs.dateTimeFormat { + return false + } return true } @@ -149,6 +155,7 @@ open class ViewControllerComponentContainer: ViewController { isVisible: self.currentIsVisible, theme: self.theme ?? self.presentationData.theme, strings: self.presentationData.strings, + dateTimeFormat: self.presentationData.dateTimeFormat, controller: { [weak self] in return self?.controller } diff --git a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift index b05755a7a1..85c3938b32 100644 --- a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift @@ -696,7 +696,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa if let _ = (view.cachedData as? CachedChannelData)?.peerGeoLocation { } else { switch mode { - case .privateLink: + case .privateLink, .revokeNames: break case .initialSetup, .generic: entries.append(.typeHeader(presentationData.theme, isGroup ? presentationData.strings.Group_Setup_TypeHeader.uppercased() : presentationData.strings.Channel_Edit_LinkItem.uppercased())) @@ -808,7 +808,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Channel_Username_CreatePublicLinkHelp)) } switch mode { - case .initialSetup: + case .initialSetup, .revokeNames: break case .generic, .privateLink: entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage)) @@ -825,7 +825,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Channel_Username_CreatePrivateLinkHelp)) } switch mode { - case .initialSetup: + case .initialSetup, .revokeNames: break case .generic, .privateLink: entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage)) @@ -856,13 +856,34 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa entries.append(.forwardingInfo(presentationData.theme, forwardingEnabled ? (isGroup ? presentationData.strings.Group_Setup_ForwardingGroupInfo : presentationData.strings.Group_Setup_ForwardingChannelInfo) : (isGroup ? presentationData.strings.Group_Setup_ForwardingGroupInfoDisabled : presentationData.strings.Group_Setup_ForwardingChannelInfoDisabled))) } else if let peer = view.peers[view.peerId] as? TelegramGroup { switch mode { + case .revokeNames: + if let publicChannelsToRevoke = publicChannelsToRevoke { + entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesTitle, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(1000)").string, 500)) + + entries.append(.publicLinkAvailability(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesInfo, false)) + var index: Int32 = 0 + for peer in publicChannelsToRevoke.sorted(by: { lhs, rhs in + var lhsDate: Int32 = 0 + var rhsDate: Int32 = 0 + if let lhs = lhs as? TelegramChannel { + lhsDate = lhs.creationDate + } + if let rhs = rhs as? TelegramChannel { + rhsDate = rhs.creationDate + } + return lhsDate > rhsDate + }) { + entries.append(.existingLinkPeerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: true, revealed: state.revealedRevokePeerId == peer.id), state.revokingPeerId == nil)) + index += 1 + } + } case .privateLink: let invite = (view.cachedData as? CachedGroupData)?.exportedInvitation entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_InviteLink.uppercased())) entries.append(.privateLink(presentationData.theme, invite, importers?.importers.prefix(3).compactMap { $0.peer.peer.flatMap(EnginePeer.init) } ?? [], importers?.count ?? 0, mode != .initialSetup)) entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.GroupInfo_InviteLink_Help)) switch mode { - case .initialSetup: + case .initialSetup, .revokeNames: break case .generic, .privateLink: entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage)) @@ -963,7 +984,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa entries.append(.privateLink(presentationData.theme, invite, importers?.importers.prefix(3).compactMap { $0.peer.peer.flatMap(EnginePeer.init) } ?? [], importers?.count ?? 0, mode != .initialSetup)) entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePrivateLinkHelp)) switch mode { - case .initialSetup: + case .initialSetup, .revokeNames: break case .generic, .privateLink: entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage)) @@ -1057,6 +1078,7 @@ public enum ChannelVisibilityControllerMode { case initialSetup case generic case privateLink + case revokeNames } public func channelVisibilityController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: PeerId, mode: ChannelVisibilityControllerMode, upgradedToSupergroup: @escaping (PeerId, @escaping () -> Void) -> Void, onDismissRemoveController: ViewController? = nil) -> ViewController { @@ -1358,189 +1380,193 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta var footerItem: ItemListControllerFooterItem? var rightNavigationButton: ItemListNavigationButton? - if let peer = peer as? TelegramChannel { - var doneEnabled = true - if let selectedType = state.selectedType { - switch selectedType { - case .privateChannel: - break - case .publicChannel: - var hasLocation = false - if let cachedChannelData = view.cachedData as? CachedChannelData, cachedChannelData.peerGeoLocation != nil { - hasLocation = true - } - - if let addressNameValidationStatus = state.addressNameValidationStatus { - switch addressNameValidationStatus { - case .availability(.available): - break - default: - doneEnabled = false - } - } else { - doneEnabled = !(peer.addressName?.isEmpty ?? true) || hasLocation - } - } - } + if case .revokeNames = mode { - rightNavigationButton = ItemListNavigationButton(content: .text(mode == .initialSetup ? presentationData.strings.Common_Next : presentationData.strings.Common_Done), style: state.updatingAddressName ? .activity : .bold, enabled: doneEnabled, action: { - var updatedAddressNameValue: String? - updateState { state in - updatedAddressNameValue = updatedAddressName(mode: mode, state: state, peer: peer, cachedData: view.cachedData) - return state + } else { + if let peer = peer as? TelegramChannel { + var doneEnabled = true + if let selectedType = state.selectedType { + switch selectedType { + case .privateChannel: + break + case .publicChannel: + var hasLocation = false + if let cachedChannelData = view.cachedData as? CachedChannelData, cachedChannelData.peerGeoLocation != nil { + hasLocation = true + } + + if let addressNameValidationStatus = state.addressNameValidationStatus { + switch addressNameValidationStatus { + case .availability(.available): + break + default: + doneEnabled = false + } + } else { + doneEnabled = !(peer.addressName?.isEmpty ?? true) || hasLocation + } + } } - if let updatedCopyProtection = state.forwardingEnabled { - toggleCopyProtectionDisposable.set(context.engine.peers.toggleMessageCopyProtection(peerId: peerId, enabled: !updatedCopyProtection).start()) - } - - if let updatedJoinToSend = state.joinToSend { - toggleJoinToSendDisposable.set(context.engine.peers.toggleChannelJoinToSend(peerId: peerId, enabled: updatedJoinToSend == .members).start()) - } - - if let updatedApproveMembers = state.approveMembers { - toggleRequestToJoinDisposable.set(context.engine.peers.toggleChannelJoinRequest(peerId: peerId, enabled: updatedApproveMembers).start()) - } - - if let updatedAddressNameValue = updatedAddressNameValue { - let invokeAction: () -> Void = { - updateState { state in - return state.withUpdatedUpdatingAddressName(true) + rightNavigationButton = ItemListNavigationButton(content: .text(mode == .initialSetup ? presentationData.strings.Common_Next : presentationData.strings.Common_Done), style: state.updatingAddressName ? .activity : .bold, enabled: doneEnabled, action: { + var updatedAddressNameValue: String? + updateState { state in + updatedAddressNameValue = updatedAddressName(mode: mode, state: state, peer: peer, cachedData: view.cachedData) + return state + } + + if let updatedCopyProtection = state.forwardingEnabled { + toggleCopyProtectionDisposable.set(context.engine.peers.toggleMessageCopyProtection(peerId: peerId, enabled: !updatedCopyProtection).start()) + } + + if let updatedJoinToSend = state.joinToSend { + toggleJoinToSendDisposable.set(context.engine.peers.toggleChannelJoinToSend(peerId: peerId, enabled: updatedJoinToSend == .members).start()) + } + + if let updatedApproveMembers = state.approveMembers { + toggleRequestToJoinDisposable.set(context.engine.peers.toggleChannelJoinRequest(peerId: peerId, enabled: updatedApproveMembers).start()) + } + + if let updatedAddressNameValue = updatedAddressNameValue { + let invokeAction: () -> Void = { + updateState { state in + return state.withUpdatedUpdatingAddressName(true) + } + _ = ApplicationSpecificNotice.markAsSeenSetPublicChannelLink(accountManager: context.sharedContext.accountManager).start() + + updateAddressNameDisposable.set((context.engine.peers.updateAddressName(domain: .peer(peerId), name: updatedAddressNameValue.isEmpty ? nil : updatedAddressNameValue) |> timeout(10, queue: Queue.mainQueue(), alternate: .fail(.generic)) + |> deliverOnMainQueue).start(error: { _ in + updateState { state in + return state.withUpdatedUpdatingAddressName(false) + } + presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + }, completed: { + updateState { state in + return state.withUpdatedUpdatingAddressName(false) + } + switch mode { + case .initialSetup: + nextImpl?() + case .generic, .privateLink, .revokeNames: + dismissImpl?() + } + })) + } - _ = ApplicationSpecificNotice.markAsSeenSetPublicChannelLink(accountManager: context.sharedContext.accountManager).start() - updateAddressNameDisposable.set((context.engine.peers.updateAddressName(domain: .peer(peerId), name: updatedAddressNameValue.isEmpty ? nil : updatedAddressNameValue) |> timeout(10, queue: Queue.mainQueue(), alternate: .fail(.generic)) - |> deliverOnMainQueue).start(error: { _ in - updateState { state in - return state.withUpdatedUpdatingAddressName(false) + _ = (ApplicationSpecificNotice.getSetPublicChannelLink(accountManager: context.sharedContext.accountManager) |> deliverOnMainQueue).start(next: { showAlert in + if showAlert { + let text: String + if case .broadcast = peer.info { + text = presentationData.strings.Channel_Edit_PrivatePublicLinkAlert + } else { + text = presentationData.strings.Group_Edit_PrivatePublicLinkAlert } - presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) - }, completed: { - updateState { state in - return state.withUpdatedUpdatingAddressName(false) + presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: invokeAction)]), nil) + } else { + invokeAction() + } + }) + } else { + switch mode { + case .initialSetup: + nextImpl?() + case .generic, .privateLink, .revokeNames: + dismissImpl?() + } + } + }) + } else if let peer = peer as? TelegramGroup { + var doneEnabled = true + if let selectedType = state.selectedType { + switch selectedType { + case .privateChannel: + break + case .publicChannel: + if let addressNameValidationStatus = state.addressNameValidationStatus { + switch addressNameValidationStatus { + case .availability(.available): + break + default: + doneEnabled = false } - switch mode { - case .initialSetup: - nextImpl?() - case .generic, .privateLink: + } else { + doneEnabled = !(peer.addressName?.isEmpty ?? true) + } + } + } + + rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: state.updatingAddressName ? .activity : .bold, enabled: doneEnabled, action: { + var updatedAddressNameValue: String? + updateState { state in + updatedAddressNameValue = updatedAddressName(mode: mode, state: state, peer: peer, cachedData: nil) + return state + } + + if let updatedCopyProtection = state.forwardingEnabled { + toggleCopyProtectionDisposable.set(context.engine.peers.toggleMessageCopyProtection(peerId: peerId, enabled: !updatedCopyProtection).start()) + } + + if let updatedAddressNameValue = updatedAddressNameValue { + let invokeAction: () -> Void = { + updateState { state in + return state.withUpdatedUpdatingAddressName(true) + } + _ = ApplicationSpecificNotice.markAsSeenSetPublicChannelLink(accountManager: context.sharedContext.accountManager).start() + + let signal = context.engine.peers.convertGroupToSupergroup(peerId: peerId) + |> mapToSignal { upgradedPeerId -> Signal in + return context.engine.peers.updateAddressName(domain: .peer(upgradedPeerId), name: updatedAddressNameValue.isEmpty ? nil : updatedAddressNameValue) + |> `catch` { _ -> Signal in + return .complete() + } + |> mapToSignal { _ -> Signal in + return .complete() + } + |> then(.single(upgradedPeerId)) + |> castError(ConvertGroupToSupergroupError.self) + } + |> deliverOnMainQueue + + updateAddressNameDisposable.set((signal + |> deliverOnMainQueue).start(next: { updatedPeerId in + if let updatedPeerId = updatedPeerId { + upgradedToSupergroup(updatedPeerId, { dismissImpl?() + }) + } else { + dismissImpl?() + } + }, error: { error in + updateState { state in + return state.withUpdatedUpdatingAddressName(false) + } + switch error { + case .tooManyChannels: + pushControllerImpl?(oldChannelsController(context: context, updatedPresentationData: updatedPresentationData, intent: .upgrade)) + default: + presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) } })) + } - } - - _ = (ApplicationSpecificNotice.getSetPublicChannelLink(accountManager: context.sharedContext.accountManager) |> deliverOnMainQueue).start(next: { showAlert in - if showAlert { - let text: String - if case .broadcast = peer.info { - text = presentationData.strings.Channel_Edit_PrivatePublicLinkAlert + _ = (ApplicationSpecificNotice.getSetPublicChannelLink(accountManager: context.sharedContext.accountManager) |> deliverOnMainQueue).start(next: { showAlert in + if showAlert { + presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Group_Edit_PrivatePublicLinkAlert, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: invokeAction)]), nil) } else { - text = presentationData.strings.Group_Edit_PrivatePublicLinkAlert + invokeAction() } - presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: invokeAction)]), nil) - } else { - invokeAction() - } - }) - } else { - switch mode { - case .initialSetup: - nextImpl?() - case .generic, .privateLink: - dismissImpl?() - } - } - }) - } else if let peer = peer as? TelegramGroup { - var doneEnabled = true - if let selectedType = state.selectedType { - switch selectedType { - case .privateChannel: - break - case .publicChannel: - if let addressNameValidationStatus = state.addressNameValidationStatus { - switch addressNameValidationStatus { - case .availability(.available): - break - default: - doneEnabled = false - } - } else { - doneEnabled = !(peer.addressName?.isEmpty ?? true) - } - } - } - - rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: state.updatingAddressName ? .activity : .bold, enabled: doneEnabled, action: { - var updatedAddressNameValue: String? - updateState { state in - updatedAddressNameValue = updatedAddressName(mode: mode, state: state, peer: peer, cachedData: nil) - return state - } - - if let updatedCopyProtection = state.forwardingEnabled { - toggleCopyProtectionDisposable.set(context.engine.peers.toggleMessageCopyProtection(peerId: peerId, enabled: !updatedCopyProtection).start()) - } - - if let updatedAddressNameValue = updatedAddressNameValue { - let invokeAction: () -> Void = { - updateState { state in - return state.withUpdatedUpdatingAddressName(true) - } - _ = ApplicationSpecificNotice.markAsSeenSetPublicChannelLink(accountManager: context.sharedContext.accountManager).start() - - let signal = context.engine.peers.convertGroupToSupergroup(peerId: peerId) - |> mapToSignal { upgradedPeerId -> Signal in - return context.engine.peers.updateAddressName(domain: .peer(upgradedPeerId), name: updatedAddressNameValue.isEmpty ? nil : updatedAddressNameValue) - |> `catch` { _ -> Signal in - return .complete() - } - |> mapToSignal { _ -> Signal in - return .complete() - } - |> then(.single(upgradedPeerId)) - |> castError(ConvertGroupToSupergroupError.self) - } - |> deliverOnMainQueue - - updateAddressNameDisposable.set((signal - |> deliverOnMainQueue).start(next: { updatedPeerId in - if let updatedPeerId = updatedPeerId { - upgradedToSupergroup(updatedPeerId, { - dismissImpl?() - }) - } else { + }) + } else { + switch mode { + case .initialSetup: + nextImpl?() + case .generic, .privateLink, .revokeNames: dismissImpl?() - } - }, error: { error in - updateState { state in - return state.withUpdatedUpdatingAddressName(false) - } - switch error { - case .tooManyChannels: - pushControllerImpl?(oldChannelsController(context: context, updatedPresentationData: updatedPresentationData, intent: .upgrade)) - default: - presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) - } - })) - } - - _ = (ApplicationSpecificNotice.getSetPublicChannelLink(accountManager: context.sharedContext.accountManager) |> deliverOnMainQueue).start(next: { showAlert in - if showAlert { - presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Group_Edit_PrivatePublicLinkAlert, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: invokeAction)]), nil) - } else { - invokeAction() } - }) - } else { - switch mode { - case .initialSetup: - nextImpl?() - case .generic, .privateLink: - dismissImpl?() } - } - }) + }) + } } if state.revokingPeerId != nil { @@ -1560,7 +1586,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta switch mode { case .initialSetup: leftNavigationButton = nil - case .generic, .privateLink: + case .generic, .privateLink, .revokeNames: leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { dismissImpl?() }) @@ -1602,7 +1628,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta } if hasNamesToRevoke && selectedType == .publicChannel { - footerItem = IncreaseLimitFooterItem(theme: presentationData.theme, title: presentationData.strings.Group_Username_IncreaseLimit, colorful: true, action: {}) + footerItem = IncreaseLimitFooterItem(theme: presentationData.theme, title: presentationData.strings.Premium_IncreaseLimit, colorful: true, action: {}) } if let hadNamesToRevoke = hadNamesToRevoke { @@ -1611,16 +1637,19 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta } let title: String - if case .privateLink = mode { - title = presentationData.strings.GroupInfo_InviteLink_Title - } else { - if let cachedChannelData = view.cachedData as? CachedChannelData, cachedChannelData.peerGeoLocation != nil { - title = presentationData.strings.Group_PublicLink_Title - } else { - title = isGroup ? presentationData.strings.GroupInfo_GroupType : presentationData.strings.Channel_TypeSetup_Title - } + switch mode { + case .generic, .initialSetup: + if let cachedChannelData = view.cachedData as? CachedChannelData, cachedChannelData.peerGeoLocation != nil { + title = presentationData.strings.Group_PublicLink_Title + } else { + title = isGroup ? presentationData.strings.GroupInfo_GroupType : presentationData.strings.Channel_TypeSetup_Title + } + case .privateLink: + title = presentationData.strings.GroupInfo_InviteLink_Title + case .revokeNames: + title = presentationData.strings.Premium_LimitReached } - + let entries = channelVisibilityControllerEntries(presentationData: presentationData, mode: mode, view: view, publicChannelsToRevoke: publicChannelsToRevoke, importers: importers, state: state) var focusItemTag: ItemListItemTag? diff --git a/submodules/PeerInfoUI/Sources/OldChannelsController.swift b/submodules/PeerInfoUI/Sources/OldChannelsController.swift index 1a0e33d27f..53fcc66dc8 100644 --- a/submodules/PeerInfoUI/Sources/OldChannelsController.swift +++ b/submodules/PeerInfoUI/Sources/OldChannelsController.swift @@ -309,7 +309,7 @@ public func oldChannelsController(context: AccountContext, updatedPresentationDa buttonText = presentationData.strings.OldChannels_LeaveCommunities(Int32(state.selectedPeers.count)) colorful = false } else { - buttonText = presentationData.strings.OldChannels_IncreaseLimit + buttonText = presentationData.strings.Premium_IncreaseLimit colorful = true } let footerItem = IncreaseLimitFooterItem(theme: presentationData.theme, title: buttonText, colorful: colorful, action: { diff --git a/submodules/PremiumUI/Sources/LimitScreen.swift b/submodules/PremiumUI/Sources/LimitScreen.swift index c21329c43e..ad6af52c43 100644 --- a/submodules/PremiumUI/Sources/LimitScreen.swift +++ b/submodules/PremiumUI/Sources/LimitScreen.swift @@ -40,20 +40,45 @@ private final class LimitScreenComponent: CombinedComponent { final class State: ComponentState { private let context: AccountContext - - init(context: AccountContext) { - self.context = context + private var disposable: Disposable? + var limits: EngineConfiguration.UserLimits + var premiumLimits: EngineConfiguration.UserLimits + + init(context: AccountContext, subject: LimitScreen.Subject) { + self.context = context + self.limits = EngineConfiguration.UserLimits.defaultValue + self.premiumLimits = EngineConfiguration.UserLimits.defaultValue + super.init() + + self.disposable = (context.engine.data.get( + TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false), + TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true) + ) |> deliverOnMainQueue).start(next: { [weak self] result in + if let strongSelf = self { + let (limits, premiumLimits) = result + strongSelf.limits = limits + strongSelf.premiumLimits = premiumLimits + strongSelf.updated(transition: .immediate) + } + }) + } + + deinit { + self.disposable?.dispose() } } func makeState() -> State { - return State(context: self.context) + return State(context: self.context, subject: self.subject) } static var body: Body { - let icon = Child(BundleIconComponent.self) + let badgeBackground = Child(RoundedRectangle.self) + let badgeIcon = Child(BundleIconComponent.self) + let badgeText = Child(MultilineTextComponent.self) + let title = Child(MultilineTextComponent.self) let text = Child(MultilineTextComponent.self) @@ -66,22 +91,84 @@ private final class LimitScreenComponent: CombinedComponent { let theme = environment.theme let strings = environment.strings + let state = context.state + let subject = component.subject + let topInset: CGFloat = 34.0 + 38.0 let sideInset: CGFloat = 16.0 + environment.safeInsets.left let textSideInset: CGFloat = 24.0 + environment.safeInsets.left - let icon = icon.update( + let iconName: String + let badgeString: String + let string: String + switch subject { + case .folders: + let limit = state.limits.maxFoldersCount + let premiumLimit = state.premiumLimits.maxFoldersCount + iconName = "Premium/Folder" + badgeString = "\(limit)" + string = strings.Premium_MaxFoldersCountText("\(limit)", "\(premiumLimit)").string + case .chatsInFolder: + let limit = state.limits.maxFolderChatsCount + let premiumLimit = state.premiumLimits.maxFolderChatsCount + iconName = "Premium/Chat" + badgeString = "\(limit)" + string = strings.Premium_MaxChatsInFolderCountText("\(limit)", "\(premiumLimit)").string + case .pins: + let limit = state.limits.maxPinnedChatCount + let premiumLimit = state.premiumLimits.maxPinnedChatCount + iconName = "Premium/Pin" + badgeString = "\(limit)" + string = strings.DialogList_ExtendedPinLimitError("\(limit)", "\(premiumLimit)").string + case .files: + let limit = 2048 * 1024 * 1024 //state.limits.maxPinnedChatCount + let premiumLimit = 4096 * 1024 * 1024 //state.premiumLimits.maxPinnedChatCount + iconName = "Premium/File" + badgeString = dataSizeString(limit, formatting: DataSizeStringFormatting(strings: environment.strings, decimalSeparator: environment.dateTimeFormat.decimalSeparator)) + string = strings.Premium_MaxFileSizeText(dataSizeString(premiumLimit, formatting: DataSizeStringFormatting(strings: environment.strings, decimalSeparator: environment.dateTimeFormat.decimalSeparator))).string + } + + let badgeIcon = badgeIcon.update( component: BundleIconComponent( - name: "Premium/Tmp", - tintColor: nil + name: iconName, + tintColor: .white ), - availableSize: CGSize(width: context.availableSize.width, height: CGFloat.greatestFiniteMagnitude), + availableSize: context.availableSize, + transition: .immediate + ) + + let badgeText = badgeText.update( + component: MultilineTextComponent( + text: NSAttributedString( + string: badgeString, + font: Font.with(size: 24.0, design: .round, weight: .semibold, traits: []), + textColor: .white, + paragraphAlignment: .center + ), + horizontalAlignment: .center, + maximumNumberOfLines: 1 + ), + availableSize: context.availableSize, + transition: .immediate + ) + + let badgeBackground = badgeBackground.update( + component: RoundedRectangle( + colors: [UIColor(rgb: 0xa34fcf), UIColor(rgb: 0xc8498a), UIColor(rgb: 0xff7a23)], + cornerRadius: 23.5 + ), + availableSize: CGSize(width: badgeText.size.width + 67.0, height: 47.0), transition: .immediate ) let title = title.update( component: MultilineTextComponent( - text: NSAttributedString(string: "Limit Reached", font: Font.semibold(17.0), textColor: theme.actionSheet.primaryTextColor, paragraphAlignment: .center), + text: NSAttributedString( + string: strings.Premium_LimitReached, + font: Font.semibold(17.0), + textColor: theme.actionSheet.primaryTextColor, + paragraphAlignment: .center + ), horizontalAlignment: .center, maximumNumberOfLines: 1 ), @@ -89,19 +176,10 @@ private final class LimitScreenComponent: CombinedComponent { transition: .immediate ) - let textFont = Font.regular(15.0) - let boldTextFont = Font.semibold(15.0) + let textFont = Font.regular(16.0) + let boldTextFont = Font.semibold(16.0) - let textColor = theme.actionSheet.secondaryTextColor - let string: String - switch component.subject { - case .chatsInFolder: - string = "" - case .folders: - string = "" - case .pins: - string = strings.DialogList_ExtendedPinLimitError("\(5)", "\(10)").string - } + let textColor = theme.actionSheet.primaryTextColor let attributedText = parseMarkdownIntoAttributedString(string, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: textColor), linkAttribute: { _ in return nil })) @@ -111,7 +189,7 @@ private final class LimitScreenComponent: CombinedComponent { text: attributedText, horizontalAlignment: .center, maximumNumberOfLines: 0, - lineSpacing: 0.1 + lineSpacing: 0.2 ), availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), transition: .immediate @@ -119,7 +197,7 @@ private final class LimitScreenComponent: CombinedComponent { let button = button.update( component: SolidRoundedButtonComponent( - title: "Increase Limit", + title: strings.Premium_IncreaseLimit, theme: SolidRoundedButtonComponent.Theme( backgroundColor: .black, backgroundColors: [UIColor(rgb: 0x407af0), UIColor(rgb: 0x9551e8), UIColor(rgb: 0xbf499a), UIColor(rgb: 0xf17b30)], @@ -154,10 +232,21 @@ private final class LimitScreenComponent: CombinedComponent { let width = context.availableSize.width - context.add(icon - .position(CGPoint(x: width / 2.0, y: 57.0)) + let badgeFrame = CGRect(origin: CGPoint(x: floor((context.availableSize.width - badgeBackground.size.width) / 2.0), y: 33.0), size: badgeBackground.size) + context.add(badgeBackground + .position(CGPoint(x: badgeFrame.midX, y: badgeFrame.midY)) ) + let badgeIconFrame = CGRect(origin: CGPoint(x: badgeFrame.minX + 18.0, y: badgeFrame.minY + floor((badgeFrame.height - badgeIcon.size.height) / 2.0)), size: badgeIcon.size) + context.add(badgeIcon + .position(CGPoint(x: badgeIconFrame.midX, y: badgeIconFrame.midY)) + ) + + let badgeTextFrame = CGRect(origin: CGPoint(x: badgeFrame.maxX - badgeText.size.width - 15.0, y: badgeFrame.minY + floor((badgeFrame.height - badgeText.size.height) / 2.0)), size: badgeText.size) + context.add(badgeText + .position(CGPoint(x: badgeTextFrame.midX, y: badgeTextFrame.midY)) + ) + context.add(title .position(CGPoint(x: width / 2.0, y: topInset + 39.0)) ) @@ -174,7 +263,7 @@ private final class LimitScreenComponent: CombinedComponent { .position(CGPoint(x: width / 2.0, y: topInset + 76.0 + text.size.height + 20.0 + button.size.height + 40.0)) ) - let contentSize = CGSize(width: context.availableSize.width, height: topInset + title.size.height + text.size.height) + let contentSize = CGSize(width: context.availableSize.width, height: topInset + 76.0 + text.size.height + 20.0 + button.size.height + 40.0 + 33.0 + environment.safeInsets.bottom) return contentSize } @@ -182,7 +271,7 @@ private final class LimitScreenComponent: CombinedComponent { } public class LimitScreen: ViewController { - final class Node: ViewControllerTracingNode, UIScrollViewDelegate, UIGestureRecognizerDelegate { + final class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate { private var presentationData: PresentationData private weak var controller: LimitScreen? @@ -192,12 +281,9 @@ public class LimitScreen: ViewController { let dim: ASDisplayNode let wrappingView: UIView let containerView: UIView - let scrollView: UIScrollView let hostView: ComponentHostView - private(set) var isExpanded = false private var panGestureRecognizer: UIPanGestureRecognizer? - private var panGestureArguments: (topInset: CGFloat, offset: CGFloat, scrollView: UIScrollView?, listNode: ListView?)? private var currentIsVisible: Bool = false private var currentLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)? @@ -218,14 +304,10 @@ public class LimitScreen: ViewController { self.wrappingView = UIView() self.containerView = UIView() - self.scrollView = UIScrollView() self.hostView = ComponentHostView() super.init() - - self.scrollView.delegate = self - self.scrollView.showsVerticalScrollIndicator = false - + self.containerView.clipsToBounds = true self.containerView.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemBackgroundColor @@ -233,8 +315,7 @@ public class LimitScreen: ViewController { self.view.addSubview(self.wrappingView) self.wrappingView.addSubview(self.containerView) - self.containerView.addSubview(self.scrollView) - self.scrollView.addSubview(self.hostView) + self.containerView.addSubview(self.hostView) } override func didLoad() { @@ -248,8 +329,6 @@ public class LimitScreen: ViewController { self.wrappingView.addGestureRecognizer(panRecognizer) self.dim.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:)))) - - self.controller?.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate) } @objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) { @@ -262,16 +341,16 @@ public class LimitScreen: ViewController { if let (layout, _) = self.currentLayout { if case .regular = layout.metrics.widthClass { return false + } else { + let location = gestureRecognizer.location(in: self.containerView) + if !self.hostView.frame.contains(location) { + return false + } } } return true } - - func scrollViewDidScroll(_ scrollView: UIScrollView) { - let contentOffset = self.scrollView.contentOffset.y - self.controller?.navigationBar?.updateBackgroundAlpha(min(30.0, contentOffset) / 30.0, transition: .immediate) - } - + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer { return true @@ -303,10 +382,6 @@ public class LimitScreen: ViewController { }) let alphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut) alphaTransition.updateAlpha(node: self.dim, alpha: 0.0) - - if !self.temporaryDismiss { - self.controller?.updateModalStyleOverlayTransitionFactor(0.0, transition: positionTransition) - } } func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: Transition) { @@ -318,29 +393,11 @@ public class LimitScreen: ViewController { self.dim.frame = CGRect(origin: CGPoint(x: 0.0, y: -layout.size.height), size: CGSize(width: layout.size.width, height: layout.size.height * 3.0)) - var effectiveExpanded = self.isExpanded - if case .regular = layout.metrics.widthClass { - effectiveExpanded = true - } - let isLandscape = layout.orientation == .landscape - let edgeTopInset = isLandscape ? 0.0 : self.defaultTopInset - let topInset: CGFloat - if let (panInitialTopInset, panOffset, _, _) = self.panGestureArguments { - if effectiveExpanded { - topInset = min(edgeTopInset, panInitialTopInset + max(0.0, panOffset)) - } else { - topInset = max(0.0, panInitialTopInset + min(0.0, panOffset)) - } - } else { - topInset = effectiveExpanded ? 0.0 : edgeTopInset - } - transition.setFrame(view: self.wrappingView, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: layout.size), completion: nil) - - let modalProgress = isLandscape ? 0.0 : (1.0 - topInset / self.defaultTopInset) - self.controller?.updateModalStyleOverlayTransitionFactor(modalProgress, transition: transition.containedViewLayoutTransition) - - let clipFrame: CGRect + + transition.setFrame(view: self.wrappingView, frame: CGRect(origin: CGPoint(), size: layout.size), completion: nil) + + var clipFrame: CGRect if layout.metrics.widthClass == .compact { self.dim.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.25) if isLandscape { @@ -386,10 +443,7 @@ public class LimitScreen: ViewController { let containerSize = CGSize(width: min(layout.size.width - 20.0, floor(maxSide / 2.0)), height: min(layout.size.height, minSide) - verticalInset * 2.0) clipFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - containerSize.width) / 2.0), y: floor((layout.size.height - containerSize.height) / 2.0)), size: containerSize) } - - transition.setFrame(view: self.containerView, frame: clipFrame) - transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: clipFrame.size), completion: nil) - + let environment = ViewControllerComponentContainer.Environment( statusBarHeight: 0.0, navigationHeight: navigationHeight, @@ -397,23 +451,28 @@ public class LimitScreen: ViewController { isVisible: self.currentIsVisible, theme: self.theme ?? self.presentationData.theme, strings: self.presentationData.strings, + dateTimeFormat: self.presentationData.dateTimeFormat, controller: { [weak self] in return self?.controller } ) - var contentSize = self.hostView.update( + let contentSize = self.hostView.update( transition: transition, component: self.component, environment: { environment }, forceUpdate: true, - containerSize: CGSize(width: clipFrame.size.width, height: 10000.0) + containerSize: CGSize(width: clipFrame.size.width, height: clipFrame.size.height) ) - contentSize.height = max(layout.size.height - navigationHeight, contentSize.height) transition.setFrame(view: self.hostView, frame: CGRect(origin: CGPoint(), size: contentSize), completion: nil) - self.scrollView.contentSize = contentSize + if !isLandscape { + clipFrame.origin.y = layout.size.height - contentSize.height + transition.setFrame(view: self.containerView, frame: clipFrame) + } else { + + } } private var didPlayAppearAnimation = false @@ -433,194 +492,29 @@ public class LimitScreen: ViewController { self.animateIn() } } - - private var defaultTopInset: CGFloat { - return 390.0 -// guard let (layout, _) = self.currentLayout else{ -// return 210.0 -// } -// if case .compact = layout.metrics.widthClass { -// var factor: CGFloat = 0.2488 -// if layout.size.width <= 320.0 { -// factor = 0.15 -// } -// return floor(max(layout.size.width, layout.size.height) * factor) -// } else { -// return 210.0 -// } - } - - private func findScrollView(view: UIView?) -> (UIScrollView, ListView?)? { - if let view = view { - if let view = view as? UIScrollView { - return (view, nil) - } - if let node = view.asyncdisplaykit_node as? ListView { - return (node.scroller, node) - } - return findScrollView(view: view.superview) - } else { - return nil - } - } - + @objc func panGesture(_ recognizer: UIPanGestureRecognizer) { - guard let (layout, navigationHeight) = self.currentLayout else { - return - } - - let isLandscape = layout.orientation == .landscape - let edgeTopInset = isLandscape ? 0.0 : defaultTopInset - switch recognizer.state { case .began: - let point = recognizer.location(in: self.view) - let currentHitView = self.hitTest(point, with: nil) - - var scrollViewAndListNode = self.findScrollView(view: currentHitView) - if scrollViewAndListNode?.0.frame.height == self.frame.width { - scrollViewAndListNode = nil - } - let scrollView = scrollViewAndListNode?.0 - let listNode = scrollViewAndListNode?.1 - - let topInset: CGFloat - if self.isExpanded { - topInset = 0.0 - } else { - topInset = edgeTopInset - } - - self.panGestureArguments = (topInset, 0.0, scrollView, listNode) + break case .changed: - guard let (topInset, panOffset, scrollView, listNode) = self.panGestureArguments else { - return - } - let visibleContentOffset = listNode?.visibleContentOffset() - let contentOffset = scrollView?.contentOffset.y ?? 0.0 - - var translation = recognizer.translation(in: self.view).y - - var currentOffset = topInset + translation - - let epsilon = 1.0 - if case let .known(value) = visibleContentOffset, value <= epsilon { - if let scrollView = scrollView { - scrollView.bounces = false - scrollView.setContentOffset(CGPoint(x: 0.0, y: 0.0), animated: false) - } - } else if let scrollView = scrollView, contentOffset <= -scrollView.contentInset.top + epsilon { - scrollView.bounces = false - scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false) - } else if let scrollView = scrollView { - translation = panOffset - currentOffset = topInset + translation - if self.isExpanded { - recognizer.setTranslation(CGPoint(), in: self.view) - } else if currentOffset > 0.0 { - scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false) - } - } - - self.panGestureArguments = (topInset, translation, scrollView, listNode) - - if !self.isExpanded { - if currentOffset > 0.0, let scrollView = scrollView { - scrollView.panGestureRecognizer.setTranslation(CGPoint(), in: scrollView) - } - } + let translation = recognizer.translation(in: self.view).y var bounds = self.bounds - if self.isExpanded { - bounds.origin.y = -max(0.0, translation - edgeTopInset) - } else { - bounds.origin.y = -translation - } + bounds.origin.y = -translation bounds.origin.y = min(0.0, bounds.origin.y) self.bounds = bounds - - self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate) case .ended: - guard let (currentTopInset, panOffset, scrollView, listNode) = self.panGestureArguments else { - return - } - self.panGestureArguments = nil - - let visibleContentOffset = listNode?.visibleContentOffset() - let contentOffset = scrollView?.contentOffset.y ?? 0.0 - let translation = recognizer.translation(in: self.view).y - var velocity = recognizer.velocity(in: self.view) + let velocity = recognizer.velocity(in: self.view) - if self.isExpanded { - if case let .known(value) = visibleContentOffset, value > 0.1 { - velocity = CGPoint() - } else if case .unknown = visibleContentOffset { - velocity = CGPoint() - } else if contentOffset > 0.1 { - velocity = CGPoint() - } - } - var bounds = self.bounds - if self.isExpanded { - bounds.origin.y = -max(0.0, translation - edgeTopInset) - } else { - bounds.origin.y = -translation - } + bounds.origin.y = -translation bounds.origin.y = min(0.0, bounds.origin.y) - - scrollView?.bounces = true - - let offset = currentTopInset + panOffset - let topInset: CGFloat = edgeTopInset - - var dismissing = false - if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) || (self.isExpanded && bounds.minY.isZero && velocity.y > 1800.0) { + + if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) { self.controller?.dismiss(animated: true, completion: nil) - dismissing = true - } else if self.isExpanded { - if velocity.y > 300.0 || offset > topInset / 2.0 { - self.isExpanded = false - if let listNode = listNode { - listNode.scroller.setContentOffset(CGPoint(), animated: false) - } else if let scrollView = scrollView { - scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false) - } - - let distance = topInset - offset - let initialVelocity: CGFloat = distance.isZero ? 0.0 : abs(velocity.y / distance) - let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity)) - - self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(transition)) - } else { - self.isExpanded = true - - self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(.animated(duration: 0.3, curve: .easeInOut))) - } - } else if (velocity.y < -300.0 || offset < topInset / 2.0) { - if velocity.y > -2200.0 && velocity.y < -300.0, let listNode = listNode { - DispatchQueue.main.async { - listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) - } - } - - let initialVelocity: CGFloat = offset.isZero ? 0.0 : abs(velocity.y / offset) - let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity)) - self.isExpanded = true - - self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(transition)) } else { - if let listNode = listNode { - listNode.scroller.setContentOffset(CGPoint(), animated: false) - } else if let scrollView = scrollView { - scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false) - } - - self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(.animated(duration: 0.3, curve: .easeInOut))) - } - - if !dismissing { var bounds = self.bounds let previousBounds = bounds bounds.origin.y = 0.0 @@ -628,25 +522,15 @@ public class LimitScreen: ViewController { self.layer.animateBounds(from: previousBounds, to: self.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue) } case .cancelled: - self.panGestureArguments = nil - - self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(.animated(duration: 0.3, curve: .easeInOut))) + var bounds = self.bounds + let previousBounds = bounds + bounds.origin.y = 0.0 + self.bounds = bounds + self.layer.animateBounds(from: previousBounds, to: self.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue) default: break } } - - func update(isExpanded: Bool, transition: ContainedViewLayoutTransition) { - guard isExpanded != self.isExpanded else { - return - } - self.isExpanded = isExpanded - - guard let (layout, navigationHeight) = self.currentLayout else { - return - } - self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(transition)) - } } var node: Node { @@ -667,6 +551,7 @@ public class LimitScreen: ViewController { case folders case chatsInFolder case pins + case files } public convenience init(context: AccountContext, subject: Subject) { @@ -678,7 +563,7 @@ public class LimitScreen: ViewController { self.component = AnyComponent(component) self.theme = nil - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: context.sharedContext.currentPresentationData.with { $0 })) + super.init(navigationBarPresentationData: nil) } required public init(coder aDecoder: NSCoder) { @@ -718,37 +603,12 @@ public class LimitScreen: ViewController { self.node.updateIsVisible(isVisible: false) } - - override public func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { - var navigationLayout = self.navigationLayout(layout: layout) - var navigationFrame = navigationLayout.navigationFrame - var layout = layout - if case .regular = layout.metrics.widthClass { - let verticalInset: CGFloat = 44.0 - let maxSide = max(layout.size.width, layout.size.height) - let minSide = min(layout.size.width, layout.size.height) - let containerSize = CGSize(width: min(layout.size.width - 20.0, floor(maxSide / 2.0)), height: min(layout.size.height, minSide) - verticalInset * 2.0) - let clipFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - containerSize.width) / 2.0), y: floor((layout.size.height - containerSize.height) / 2.0)), size: containerSize) - navigationFrame.size.width = clipFrame.width - layout.size = clipFrame.size - } - - navigationFrame.size.height = 56.0 - navigationLayout.navigationFrame = navigationFrame - navigationLayout.defaultContentHeight = 56.0 - - layout.statusBarHeight = nil - - self.applyNavigationBarLayout(layout, navigationLayout: navigationLayout, additionalBackgroundHeight: 0.0, transition: transition) - } - override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { self.currentLayout = layout super.containerLayoutUpdated(layout, transition: transition) let navigationHeight: CGFloat = 56.0 - self.node.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(transition)) } } diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift new file mode 100644 index 0000000000..fecc4ab449 --- /dev/null +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -0,0 +1 @@ +import Foundation diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift index eaa8137460..178133c6f4 100644 --- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift +++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift @@ -434,6 +434,7 @@ public final class SolidRoundedButtonView: UIView { private var fontSize: CGFloat private let buttonBackgroundNode: UIImageView + private var buttonBackgroundAnimationView: UIImageView? private let buttonGlossView: SolidRoundedButtonGlossView? private let buttonNode: HighlightTrackingButton private let titleNode: ImmediateTextView @@ -506,6 +507,11 @@ public final class SolidRoundedButtonView: UIView { locations.append(delta * CGFloat(i)) } self.buttonBackgroundNode.image = generateGradientImage(size: CGSize(width: 200.0, height: height), colors: theme.backgroundColors, locations: locations, direction: .horizontal) + + let buttonBackgroundAnimationView = UIImageView() + buttonBackgroundAnimationView.image = generateGradientImage(size: CGSize(width: 200.0, height: height), colors: theme.backgroundColors, locations: locations, direction: .horizontal) + self.buttonBackgroundNode.addSubview(buttonBackgroundAnimationView) + self.buttonBackgroundAnimationView = buttonBackgroundAnimationView } else { self.buttonBackgroundNode.backgroundColor = theme.backgroundColor } @@ -575,6 +581,42 @@ public final class SolidRoundedButtonView: UIView { fatalError("init(coder:) has not been implemented") } + private func setupGradientAnimations() { + guard let buttonBackgroundAnimationView = self.buttonBackgroundAnimationView else { + return + } + + if let _ = buttonBackgroundAnimationView.layer.animation(forKey: "movement") { + } else { + let offset = (buttonBackgroundAnimationView.frame.width - self.frame.width) / 2.0 + let previousValue = buttonBackgroundAnimationView.center.x + var newValue: CGFloat = offset + if offset - previousValue < buttonBackgroundAnimationView.frame.width * 0.25 { + newValue -= CGFloat.random(in: buttonBackgroundAnimationView.frame.width * 0.3 ..< buttonBackgroundAnimationView.frame.width * 0.4) + } else { +// newValue -= CGFloat.random(in: 0.0 ..< buttonBackgroundAnimationView.frame.width * 0.1) + } + buttonBackgroundAnimationView.center = CGPoint(x: newValue, y: buttonBackgroundAnimationView.bounds.size.height / 2.0) + + CATransaction.begin() + + let animation = CABasicAnimation(keyPath: "position.x") + animation.duration = Double.random(in: 1.8 ..< 2.3) + animation.fromValue = previousValue + animation.toValue = newValue + animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) + + CATransaction.setCompletionBlock { [weak self] in +// if let isCurrentlyInHierarchy = self?.isCurrentlyInHierarchy, isCurrentlyInHierarchy { + self?.setupGradientAnimations() +// } + } + + buttonBackgroundAnimationView.layer.add(animation, forKey: "movement") + CATransaction.commit() + } + } + public func transitionToProgress() { guard self.progressNode == nil else { return @@ -659,6 +701,12 @@ public final class SolidRoundedButtonView: UIView { let buttonSize = CGSize(width: width, height: self.buttonHeight) let buttonFrame = CGRect(origin: CGPoint(), size: buttonSize) transition.updateFrame(view: self.buttonBackgroundNode, frame: buttonFrame) + + if let buttonBackgroundAnimationView = self.buttonBackgroundAnimationView { + transition.updateFrame(view: buttonBackgroundAnimationView, frame: CGRect(origin: CGPoint(), size: CGSize(width: buttonSize.width * 2.4, height: buttonSize.height))) + self.setupGradientAnimations() + } + if let buttonGlossView = self.buttonGlossView { transition.updateFrame(view: buttonGlossView, frame: buttonFrame) } diff --git a/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift b/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift new file mode 100644 index 0000000000..7a2b336fb3 --- /dev/null +++ b/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift @@ -0,0 +1,80 @@ +import Postbox +import SwiftSignalKit + +public struct UserLimitsConfiguration: Equatable { + public let maxPinnedChatCount: Int32 + public let maxChannelsCount: Int32 + public let maxPublicLinksCount: Int32 + public let maxSavedGifCount: Int32 + public let maxFavedStickerCount: Int32 + public let maxFoldersCount: Int32 + public let maxFolderChatsCount: Int32 + public let maxTextLengthCount: Int32 + + public static var defaultValue: UserLimitsConfiguration { + return UserLimitsConfiguration( + maxPinnedChatCount: 5, + maxChannelsCount: 500, + maxPublicLinksCount: 10, + maxSavedGifCount: 200, + maxFavedStickerCount: 5, + maxFoldersCount: 10, + maxFolderChatsCount: 100, + maxTextLengthCount: 4096 + ) + } + + public init( + maxPinnedChatCount: Int32, + maxChannelsCount: Int32, + maxPublicLinksCount: Int32, + maxSavedGifCount: Int32, + maxFavedStickerCount: Int32, + maxFoldersCount: Int32, + maxFolderChatsCount: Int32, + maxTextLengthCount: Int32 + ) { + self.maxPinnedChatCount = maxPinnedChatCount + self.maxChannelsCount = maxChannelsCount + self.maxPublicLinksCount = maxPublicLinksCount + self.maxSavedGifCount = maxSavedGifCount + self.maxFavedStickerCount = maxFavedStickerCount + self.maxFoldersCount = maxFoldersCount + self.maxFolderChatsCount = maxFolderChatsCount + self.maxTextLengthCount = maxTextLengthCount + } +} + +extension UserLimitsConfiguration { + init(appConfiguration: AppConfiguration, isPremium: Bool) { + let keySuffix = isPremium ? "_premium" : "_default" + let defaultValue = UserLimitsConfiguration.defaultValue + + func getValue(_ key: String, orElse defaultValue: Int32) -> Int32 { + if let value = appConfiguration.data?[key + keySuffix] as? Double { + return Int32(value) + } else { + return defaultValue + } + } + + self.maxPinnedChatCount = getValue("dialogs_pinned_limit", orElse: defaultValue.maxPinnedChatCount) + self.maxChannelsCount = getValue("channels_limit", orElse: defaultValue.maxPinnedChatCount) + self.maxPublicLinksCount = getValue("channels_public_limit", orElse: defaultValue.maxPinnedChatCount) + self.maxSavedGifCount = getValue("saved_gifs_limit", orElse: defaultValue.maxPinnedChatCount) + self.maxFavedStickerCount = getValue("stickers_faved_limit", orElse: defaultValue.maxPinnedChatCount) + self.maxFoldersCount = getValue("dialog_filters_limit", orElse: defaultValue.maxPinnedChatCount) + self.maxFolderChatsCount = getValue("dialog_filters_chats_limit", orElse: defaultValue.maxPinnedChatCount) + self.maxTextLengthCount = getValue("message_text_length_limit", orElse: defaultValue.maxPinnedChatCount) + } +} + +public func getUserLimits(postbox: Postbox) -> Signal { + return postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]) + |> mapToSignal { preferencesView -> Signal in + let appConfiguration: AppConfiguration = preferencesView.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) ?? .defaultValue + let configuration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: false) + print(configuration) + return .never() + } +} diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_wallapersState.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_WallapersState.swift similarity index 100% rename from submodules/TelegramCore/Sources/SyncCore/SyncCore_wallapersState.swift rename to submodules/TelegramCore/Sources/SyncCore/SyncCore_WallapersState.swift diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/Configuration.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/Configuration.swift index d196aa0f54..04d201154e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/Configuration.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/Configuration.swift @@ -46,6 +46,41 @@ public enum EngineConfiguration { self.maxMessageRevokeIntervalInPrivateChats = maxMessageRevokeIntervalInPrivateChats } } + + public struct UserLimits: Equatable { + public let maxPinnedChatCount: Int32 + public let maxChannelsCount: Int32 + public let maxPublicLinksCount: Int32 + public let maxSavedGifCount: Int32 + public let maxFavedStickerCount: Int32 + public let maxFoldersCount: Int32 + public let maxFolderChatsCount: Int32 + public let maxTextLengthCount: Int32 + + public static var defaultValue: UserLimits { + return UserLimits(UserLimitsConfiguration.defaultValue) + } + + public init( + maxPinnedChatCount: Int32, + maxChannelsCount: Int32, + maxPublicLinksCount: Int32, + maxSavedGifCount: Int32, + maxFavedStickerCount: Int32, + maxFoldersCount: Int32, + maxFolderChatsCount: Int32, + maxTextLengthCount: Int32 + ) { + self.maxPinnedChatCount = maxPinnedChatCount + self.maxChannelsCount = maxChannelsCount + self.maxPublicLinksCount = maxPublicLinksCount + self.maxSavedGifCount = maxSavedGifCount + self.maxFavedStickerCount = maxFavedStickerCount + self.maxFoldersCount = maxFoldersCount + self.maxFolderChatsCount = maxFolderChatsCount + self.maxTextLengthCount = maxTextLengthCount + } + } } extension EngineConfiguration.Limits { @@ -67,6 +102,21 @@ extension EngineConfiguration.Limits { } } +extension EngineConfiguration.UserLimits { + init(_ userLimitsConfiguration: UserLimitsConfiguration) { + self.init( + maxPinnedChatCount: userLimitsConfiguration.maxPinnedChatCount, + maxChannelsCount: userLimitsConfiguration.maxChannelsCount, + maxPublicLinksCount: userLimitsConfiguration.maxPublicLinksCount, + maxSavedGifCount: userLimitsConfiguration.maxSavedGifCount, + maxFavedStickerCount: userLimitsConfiguration.maxFavedStickerCount, + maxFoldersCount: userLimitsConfiguration.maxFoldersCount, + maxFolderChatsCount: userLimitsConfiguration.maxFolderChatsCount, + maxTextLengthCount: userLimitsConfiguration.maxTextLengthCount + ) + } +} + public extension TelegramEngine.EngineData.Item { enum Configuration { public struct Limits: TelegramEngineDataItem, PostboxViewDataItem { @@ -89,5 +139,28 @@ public extension TelegramEngine.EngineData.Item { return EngineConfiguration.Limits(limitsConfiguration) } } + + public struct UserLimits: TelegramEngineDataItem, PostboxViewDataItem { + public typealias Result = EngineConfiguration.UserLimits + + fileprivate let isPremium: Bool + public init(isPremium: Bool) { + self.isPremium = isPremium + } + + var key: PostboxViewKey { + return .preferences(keys: Set([PreferencesKeys.appConfiguration])) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? PreferencesView else { + preconditionFailure() + } + guard let appConfiguration = view.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) else { + return EngineConfiguration.UserLimits(UserLimitsConfiguration.defaultValue) + } + return EngineConfiguration.UserLimits(UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: self.isPremium)) + } + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 72cadcefd4..f359f6f588 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -491,7 +491,7 @@ public extension TelegramEngine { } public func toggleItemPinned(location: TogglePeerChatPinnedLocation, itemId: PinnedItemId) -> Signal { - return _internal_toggleItemPinned(postbox: self.account.postbox, location: location, itemId: itemId) + return _internal_toggleItemPinned(postbox: self.account.postbox, accountPeerId: self.account.peerId, location: location, itemId: itemId) } public func getPinnedItemIds(location: TogglePeerChatPinnedLocation) -> Signal<[PinnedItemId], NoError> { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift index 46cb72da73..938dd85d06 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift @@ -13,8 +13,10 @@ public enum TogglePeerChatPinnedResult { case limitExceeded(Int) } -func _internal_toggleItemPinned(postbox: Postbox, location: TogglePeerChatPinnedLocation, itemId: PinnedItemId) -> Signal { +func _internal_toggleItemPinned(postbox: Postbox, accountPeerId: PeerId, location: TogglePeerChatPinnedLocation, itemId: PinnedItemId) -> Signal { return postbox.transaction { transaction -> TogglePeerChatPinnedResult in + let isPremium = transaction.getPeer(accountPeerId)?.isPremium ?? false + switch location { case let .group(groupId): var itemIds = transaction.getPinnedItemIds(groupId: groupId) @@ -37,10 +39,13 @@ func _internal_toggleItemPinned(postbox: Postbox, location: TogglePeerChatPinned additionalCount = 1 } + let appConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? .defaultValue let limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration)?.get(LimitsConfiguration.self) ?? LimitsConfiguration.defaultValue + let userLimitsConfiguration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: isPremium) + let limitCount: Int if case .root = groupId { - limitCount = Int(limitsConfiguration.maxPinnedChatCount) + limitCount = Int(userLimitsConfiguration.maxPinnedChatCount) } else { limitCount = Int(limitsConfiguration.maxArchivedPinnedChatCount) } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index 74775f6111..0e2952325b 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -2252,7 +2252,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { private var initializedCredibilityIcon = false private var currentPanelStatusData: PeerInfoStatusData? - func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, notificationSettings: TelegramPeerNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: 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, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, notificationSettings: TelegramPeerNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, metrics: LayoutMetrics, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat { self.state = state self.peer = peer self.avatarListNode.listContainerNode.peer = peer @@ -2567,18 +2567,14 @@ final class PeerInfoHeaderNode: ASDisplayNode { if self.isAvatarExpanded { let minTitleSize = CGSize(width: titleSize.width * 0.7, height: titleSize.height * 0.7) - let minTitleFrame: CGRect - - minTitleFrame = CGRect(origin: CGPoint(x: 16.0, y: expandedAvatarHeight - 58.0 - UIScreenPixel + (subtitleSize.height.isZero ? 10.0 : 0.0)), size: minTitleSize) + let minTitleFrame = CGRect(origin: CGPoint(x: 16.0, y: expandedAvatarHeight - 58.0 - UIScreenPixel + (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 + 2.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 + 7.0 + (subtitleSize.height.isZero ? 11.0 : 0.0)), size: titleSize) - - titleFrame = titleFrame.offsetBy(dx: 0.0, dy: 11.0) - + titleFrame = CGRect(origin: CGPoint(x: floor((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 7.0 + (subtitleSize.height.isZero ? 11.0 : 0.0) + 11.0), size: titleSize) + let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width if usernameSize.width == 0.0 { subtitleFrame = CGRect(origin: CGPoint(x: floor((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize) @@ -2593,7 +2589,10 @@ final class PeerInfoHeaderNode: ASDisplayNode { let titleLockOffset: CGFloat = 7.0 + singleTitleLockOffset let titleMaxLockOffset: CGFloat = 7.0 - let titleCollapseOffset = titleFrame.midY - statusBarHeight - titleLockOffset + var titleCollapseOffset = titleFrame.midY - statusBarHeight - titleLockOffset + if case .regular = metrics.widthClass { + titleCollapseOffset -= 7.0 + } let titleOffset = -min(titleCollapseOffset, contentOffset) let titleCollapseFraction = max(0.0, min(1.0, contentOffset / titleCollapseOffset)) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index f2a94e1547..f62f9508d6 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -6997,7 +6997,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } let headerInset = sectionInset - var headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, transition: transition, additive: additive) + var headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, transition: transition, additive: additive) if !self.isSettings && !self.state.isEditing { headerHeight += 71.0 } @@ -7357,7 +7357,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } let headerInset = sectionInset - let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, transition: transition, additive: additive) + let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, transition: transition, additive: additive) } let paneAreaExpansionDistance: CGFloat = 32.0 @@ -8312,7 +8312,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig } let headerInset = sectionInset - topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, paneContainerY: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, notificationSettings: self.screenNode.data?.notificationSettings, statusData: self.screenNode.data?.status, panelStatusData: (nil, nil, nil), isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, transition: transition, additive: false) + topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, paneContainerY: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, notificationSettings: self.screenNode.data?.notificationSettings, statusData: self.screenNode.data?.status, panelStatusData: (nil, nil, nil), isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, metrics: layout.metrics, transition: transition, additive: false) } let titleScale = (fraction * previousTitleNode.bounds.height + (1.0 - fraction) * self.headerNode.titleNodeRawContainer.bounds.height) / previousTitleNode.bounds.height diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 78c73bde49..26d8124ec5 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -129,6 +129,8 @@ public final class TelegramRootController: NavigationController { self.accountSettingsController = accountSettingsController self.rootTabController = tabBarController self.pushViewController(tabBarController, animated: false) + + let _ = getUserLimits(postbox: self.context.account.postbox).start() } public func updateRootControllers(showCallsTab: Bool) { diff --git a/submodules/TranslateUI/Sources/TranslateScreen.swift b/submodules/TranslateUI/Sources/TranslateScreen.swift index ab3aabde13..5c7ff4bf52 100644 --- a/submodules/TranslateUI/Sources/TranslateScreen.swift +++ b/submodules/TranslateUI/Sources/TranslateScreen.swift @@ -723,6 +723,7 @@ public class TranslateScreen: ViewController { isVisible: self.currentIsVisible, theme: self.theme ?? self.presentationData.theme, strings: self.presentationData.strings, + dateTimeFormat: self.presentationData.dateTimeFormat, controller: { [weak self] in return self?.controller }