mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
b997987108
commit
5ded9f78a6
@ -7549,18 +7549,18 @@ Sorry for the inconvenience.";
|
|||||||
"Chat.MultipleTypingMore" = "%@ and %@ others";
|
"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.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.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.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.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.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_1" = "Leave %@ Community";
|
||||||
"OldChannels.LeaveCommunities_any" = "Leave %@ Communities";
|
"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.";
|
"Premium.LimitReached" = "Limit Reached";
|
||||||
"Stickers.FaveLimitReplaceOlder" = "Replace Older Sticker";
|
"Premium.IncreaseLimit" = "Increase Limit";
|
||||||
"Stickers.FaveLimitIncrease" = "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**.";
|
||||||
|
@ -331,7 +331,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
|||||||
return nil
|
return nil
|
||||||
}, action: nil as ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?)))
|
}, 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
|
return nil
|
||||||
}, action: { _, f in
|
}, action: { _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
|
@ -2,21 +2,35 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
public final class RoundedRectangle: Component {
|
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 let cornerRadius: CGFloat
|
||||||
|
public let gradientDirection: GradientDirection
|
||||||
public init(color: UIColor, cornerRadius: CGFloat) {
|
|
||||||
self.color = color
|
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.cornerRadius = cornerRadius
|
||||||
|
self.gradientDirection = gradientDirection
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: RoundedRectangle, rhs: RoundedRectangle) -> Bool {
|
public static func ==(lhs: RoundedRectangle, rhs: RoundedRectangle) -> Bool {
|
||||||
if !lhs.color.isEqual(rhs.color) {
|
if lhs.colors != rhs.colors {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.cornerRadius != rhs.cornerRadius {
|
if lhs.cornerRadius != rhs.cornerRadius {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.gradientDirection != rhs.gradientDirection {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,14 +39,37 @@ public final class RoundedRectangle: Component {
|
|||||||
|
|
||||||
func update(component: RoundedRectangle, availableSize: CGSize, transition: Transition) -> CGSize {
|
func update(component: RoundedRectangle, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||||
if self.component != component {
|
if self.component != component {
|
||||||
let imageSize = CGSize(width: component.cornerRadius * 2.0, height: component.cornerRadius * 2.0)
|
if component.colors.count == 1, let color = component.colors.first {
|
||||||
UIGraphicsBeginImageContextWithOptions(imageSize, false, 0.0)
|
let imageSize = CGSize(width: component.cornerRadius * 2.0, height: component.cornerRadius * 2.0)
|
||||||
if let context = UIGraphicsGetCurrentContext() {
|
UIGraphicsBeginImageContextWithOptions(imageSize, false, 0.0)
|
||||||
context.setFillColor(component.color.cgColor)
|
if let context = UIGraphicsGetCurrentContext() {
|
||||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: imageSize))
|
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
|
return availableSize
|
||||||
|
@ -66,6 +66,7 @@ open class ViewControllerComponentContainer: ViewController {
|
|||||||
public let isVisible: Bool
|
public let isVisible: Bool
|
||||||
public let theme: PresentationTheme
|
public let theme: PresentationTheme
|
||||||
public let strings: PresentationStrings
|
public let strings: PresentationStrings
|
||||||
|
public let dateTimeFormat: PresentationDateTimeFormat
|
||||||
public let controller: () -> ViewController?
|
public let controller: () -> ViewController?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
@ -75,6 +76,7 @@ open class ViewControllerComponentContainer: ViewController {
|
|||||||
isVisible: Bool,
|
isVisible: Bool,
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
strings: PresentationStrings,
|
strings: PresentationStrings,
|
||||||
|
dateTimeFormat: PresentationDateTimeFormat,
|
||||||
controller: @escaping () -> ViewController?
|
controller: @escaping () -> ViewController?
|
||||||
) {
|
) {
|
||||||
self.statusBarHeight = statusBarHeight
|
self.statusBarHeight = statusBarHeight
|
||||||
@ -83,6 +85,7 @@ open class ViewControllerComponentContainer: ViewController {
|
|||||||
self.isVisible = isVisible
|
self.isVisible = isVisible
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
|
self.dateTimeFormat = dateTimeFormat
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +112,9 @@ open class ViewControllerComponentContainer: ViewController {
|
|||||||
if lhs.strings !== rhs.strings {
|
if lhs.strings !== rhs.strings {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.dateTimeFormat != rhs.dateTimeFormat {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -149,6 +155,7 @@ open class ViewControllerComponentContainer: ViewController {
|
|||||||
isVisible: self.currentIsVisible,
|
isVisible: self.currentIsVisible,
|
||||||
theme: self.theme ?? self.presentationData.theme,
|
theme: self.theme ?? self.presentationData.theme,
|
||||||
strings: self.presentationData.strings,
|
strings: self.presentationData.strings,
|
||||||
|
dateTimeFormat: self.presentationData.dateTimeFormat,
|
||||||
controller: { [weak self] in
|
controller: { [weak self] in
|
||||||
return self?.controller
|
return self?.controller
|
||||||
}
|
}
|
||||||
|
@ -696,7 +696,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||||||
if let _ = (view.cachedData as? CachedChannelData)?.peerGeoLocation {
|
if let _ = (view.cachedData as? CachedChannelData)?.peerGeoLocation {
|
||||||
} else {
|
} else {
|
||||||
switch mode {
|
switch mode {
|
||||||
case .privateLink:
|
case .privateLink, .revokeNames:
|
||||||
break
|
break
|
||||||
case .initialSetup, .generic:
|
case .initialSetup, .generic:
|
||||||
entries.append(.typeHeader(presentationData.theme, isGroup ? presentationData.strings.Group_Setup_TypeHeader.uppercased() : presentationData.strings.Channel_Edit_LinkItem.uppercased()))
|
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))
|
entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Channel_Username_CreatePublicLinkHelp))
|
||||||
}
|
}
|
||||||
switch mode {
|
switch mode {
|
||||||
case .initialSetup:
|
case .initialSetup, .revokeNames:
|
||||||
break
|
break
|
||||||
case .generic, .privateLink:
|
case .generic, .privateLink:
|
||||||
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
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))
|
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Channel_Username_CreatePrivateLinkHelp))
|
||||||
}
|
}
|
||||||
switch mode {
|
switch mode {
|
||||||
case .initialSetup:
|
case .initialSetup, .revokeNames:
|
||||||
break
|
break
|
||||||
case .generic, .privateLink:
|
case .generic, .privateLink:
|
||||||
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
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)))
|
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 {
|
} else if let peer = view.peers[view.peerId] as? TelegramGroup {
|
||||||
switch mode {
|
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:
|
case .privateLink:
|
||||||
let invite = (view.cachedData as? CachedGroupData)?.exportedInvitation
|
let invite = (view.cachedData as? CachedGroupData)?.exportedInvitation
|
||||||
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_InviteLink.uppercased()))
|
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(.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))
|
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.GroupInfo_InviteLink_Help))
|
||||||
switch mode {
|
switch mode {
|
||||||
case .initialSetup:
|
case .initialSetup, .revokeNames:
|
||||||
break
|
break
|
||||||
case .generic, .privateLink:
|
case .generic, .privateLink:
|
||||||
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
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(.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))
|
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePrivateLinkHelp))
|
||||||
switch mode {
|
switch mode {
|
||||||
case .initialSetup:
|
case .initialSetup, .revokeNames:
|
||||||
break
|
break
|
||||||
case .generic, .privateLink:
|
case .generic, .privateLink:
|
||||||
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||||
@ -1057,6 +1078,7 @@ public enum ChannelVisibilityControllerMode {
|
|||||||
case initialSetup
|
case initialSetup
|
||||||
case generic
|
case generic
|
||||||
case privateLink
|
case privateLink
|
||||||
|
case revokeNames
|
||||||
}
|
}
|
||||||
|
|
||||||
public func channelVisibilityController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, mode: ChannelVisibilityControllerMode, upgradedToSupergroup: @escaping (PeerId, @escaping () -> Void) -> Void, onDismissRemoveController: ViewController? = nil) -> ViewController {
|
public func channelVisibilityController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = 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 footerItem: ItemListControllerFooterItem?
|
||||||
|
|
||||||
var rightNavigationButton: ItemListNavigationButton?
|
var rightNavigationButton: ItemListNavigationButton?
|
||||||
if let peer = peer as? TelegramChannel {
|
if case .revokeNames = mode {
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rightNavigationButton = ItemListNavigationButton(content: .text(mode == .initialSetup ? presentationData.strings.Common_Next : presentationData.strings.Common_Done), style: state.updatingAddressName ? .activity : .bold, enabled: doneEnabled, action: {
|
} else {
|
||||||
var updatedAddressNameValue: String?
|
if let peer = peer as? TelegramChannel {
|
||||||
updateState { state in
|
var doneEnabled = true
|
||||||
updatedAddressNameValue = updatedAddressName(mode: mode, state: state, peer: peer, cachedData: view.cachedData)
|
if let selectedType = state.selectedType {
|
||||||
return state
|
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 {
|
rightNavigationButton = ItemListNavigationButton(content: .text(mode == .initialSetup ? presentationData.strings.Common_Next : presentationData.strings.Common_Done), style: state.updatingAddressName ? .activity : .bold, enabled: doneEnabled, action: {
|
||||||
toggleCopyProtectionDisposable.set(context.engine.peers.toggleMessageCopyProtection(peerId: peerId, enabled: !updatedCopyProtection).start())
|
var updatedAddressNameValue: String?
|
||||||
}
|
updateState { state in
|
||||||
|
updatedAddressNameValue = updatedAddressName(mode: mode, state: state, peer: peer, cachedData: view.cachedData)
|
||||||
if let updatedJoinToSend = state.joinToSend {
|
return state
|
||||||
toggleJoinToSendDisposable.set(context.engine.peers.toggleChannelJoinToSend(peerId: peerId, enabled: updatedJoinToSend == .members).start())
|
}
|
||||||
}
|
|
||||||
|
if let updatedCopyProtection = state.forwardingEnabled {
|
||||||
if let updatedApproveMembers = state.approveMembers {
|
toggleCopyProtectionDisposable.set(context.engine.peers.toggleMessageCopyProtection(peerId: peerId, enabled: !updatedCopyProtection).start())
|
||||||
toggleRequestToJoinDisposable.set(context.engine.peers.toggleChannelJoinRequest(peerId: peerId, enabled: updatedApproveMembers).start())
|
}
|
||||||
}
|
|
||||||
|
if let updatedJoinToSend = state.joinToSend {
|
||||||
if let updatedAddressNameValue = updatedAddressNameValue {
|
toggleJoinToSendDisposable.set(context.engine.peers.toggleChannelJoinToSend(peerId: peerId, enabled: updatedJoinToSend == .members).start())
|
||||||
let invokeAction: () -> Void = {
|
}
|
||||||
updateState { state in
|
|
||||||
return state.withUpdatedUpdatingAddressName(true)
|
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))
|
_ = (ApplicationSpecificNotice.getSetPublicChannelLink(accountManager: context.sharedContext.accountManager) |> deliverOnMainQueue).start(next: { showAlert in
|
||||||
|> deliverOnMainQueue).start(error: { _ in
|
if showAlert {
|
||||||
updateState { state in
|
let text: String
|
||||||
return state.withUpdatedUpdatingAddressName(false)
|
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)
|
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)
|
||||||
}, completed: {
|
} else {
|
||||||
updateState { state in
|
invokeAction()
|
||||||
return state.withUpdatedUpdatingAddressName(false)
|
}
|
||||||
|
})
|
||||||
|
} 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 {
|
} else {
|
||||||
case .initialSetup:
|
doneEnabled = !(peer.addressName?.isEmpty ?? true)
|
||||||
nextImpl?()
|
}
|
||||||
case .generic, .privateLink:
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<PeerId?, ConvertGroupToSupergroupError> in
|
||||||
|
return context.engine.peers.updateAddressName(domain: .peer(upgradedPeerId), name: updatedAddressNameValue.isEmpty ? nil : updatedAddressNameValue)
|
||||||
|
|> `catch` { _ -> Signal<Void, NoError> in
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
|> mapToSignal { _ -> Signal<PeerId?, NoError> 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?()
|
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 {
|
||||||
_ = (ApplicationSpecificNotice.getSetPublicChannelLink(accountManager: context.sharedContext.accountManager) |> deliverOnMainQueue).start(next: { showAlert in
|
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)
|
||||||
if showAlert {
|
|
||||||
let text: String
|
|
||||||
if case .broadcast = peer.info {
|
|
||||||
text = presentationData.strings.Channel_Edit_PrivatePublicLinkAlert
|
|
||||||
} else {
|
} 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 {
|
} else {
|
||||||
invokeAction()
|
switch mode {
|
||||||
}
|
case .initialSetup:
|
||||||
})
|
nextImpl?()
|
||||||
} else {
|
case .generic, .privateLink, .revokeNames:
|
||||||
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<PeerId?, ConvertGroupToSupergroupError> in
|
|
||||||
return context.engine.peers.updateAddressName(domain: .peer(upgradedPeerId), name: updatedAddressNameValue.isEmpty ? nil : updatedAddressNameValue)
|
|
||||||
|> `catch` { _ -> Signal<Void, NoError> in
|
|
||||||
return .complete()
|
|
||||||
}
|
|
||||||
|> mapToSignal { _ -> Signal<PeerId?, NoError> 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?()
|
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 {
|
if state.revokingPeerId != nil {
|
||||||
@ -1560,7 +1586,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
|
|||||||
switch mode {
|
switch mode {
|
||||||
case .initialSetup:
|
case .initialSetup:
|
||||||
leftNavigationButton = nil
|
leftNavigationButton = nil
|
||||||
case .generic, .privateLink:
|
case .generic, .privateLink, .revokeNames:
|
||||||
leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
})
|
})
|
||||||
@ -1602,7 +1628,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
|
|||||||
}
|
}
|
||||||
|
|
||||||
if hasNamesToRevoke && selectedType == .publicChannel {
|
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 {
|
if let hadNamesToRevoke = hadNamesToRevoke {
|
||||||
@ -1611,16 +1637,19 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
|
|||||||
}
|
}
|
||||||
|
|
||||||
let title: String
|
let title: String
|
||||||
if case .privateLink = mode {
|
switch mode {
|
||||||
title = presentationData.strings.GroupInfo_InviteLink_Title
|
case .generic, .initialSetup:
|
||||||
} else {
|
if let cachedChannelData = view.cachedData as? CachedChannelData, cachedChannelData.peerGeoLocation != nil {
|
||||||
if let cachedChannelData = view.cachedData as? CachedChannelData, cachedChannelData.peerGeoLocation != nil {
|
title = presentationData.strings.Group_PublicLink_Title
|
||||||
title = presentationData.strings.Group_PublicLink_Title
|
} else {
|
||||||
} else {
|
title = isGroup ? presentationData.strings.GroupInfo_GroupType : presentationData.strings.Channel_TypeSetup_Title
|
||||||
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)
|
let entries = channelVisibilityControllerEntries(presentationData: presentationData, mode: mode, view: view, publicChannelsToRevoke: publicChannelsToRevoke, importers: importers, state: state)
|
||||||
|
|
||||||
var focusItemTag: ItemListItemTag?
|
var focusItemTag: ItemListItemTag?
|
||||||
|
@ -309,7 +309,7 @@ public func oldChannelsController(context: AccountContext, updatedPresentationDa
|
|||||||
buttonText = presentationData.strings.OldChannels_LeaveCommunities(Int32(state.selectedPeers.count))
|
buttonText = presentationData.strings.OldChannels_LeaveCommunities(Int32(state.selectedPeers.count))
|
||||||
colorful = false
|
colorful = false
|
||||||
} else {
|
} else {
|
||||||
buttonText = presentationData.strings.OldChannels_IncreaseLimit
|
buttonText = presentationData.strings.Premium_IncreaseLimit
|
||||||
colorful = true
|
colorful = true
|
||||||
}
|
}
|
||||||
let footerItem = IncreaseLimitFooterItem(theme: presentationData.theme, title: buttonText, colorful: colorful, action: {
|
let footerItem = IncreaseLimitFooterItem(theme: presentationData.theme, title: buttonText, colorful: colorful, action: {
|
||||||
|
@ -40,20 +40,45 @@ private final class LimitScreenComponent: CombinedComponent {
|
|||||||
|
|
||||||
final class State: ComponentState {
|
final class State: ComponentState {
|
||||||
private let context: AccountContext
|
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()
|
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 {
|
func makeState() -> State {
|
||||||
return State(context: self.context)
|
return State(context: self.context, subject: self.subject)
|
||||||
}
|
}
|
||||||
|
|
||||||
static var body: Body {
|
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 title = Child(MultilineTextComponent.self)
|
||||||
let text = Child(MultilineTextComponent.self)
|
let text = Child(MultilineTextComponent.self)
|
||||||
|
|
||||||
@ -66,22 +91,84 @@ private final class LimitScreenComponent: CombinedComponent {
|
|||||||
let theme = environment.theme
|
let theme = environment.theme
|
||||||
let strings = environment.strings
|
let strings = environment.strings
|
||||||
|
|
||||||
|
let state = context.state
|
||||||
|
let subject = component.subject
|
||||||
|
|
||||||
let topInset: CGFloat = 34.0 + 38.0
|
let topInset: CGFloat = 34.0 + 38.0
|
||||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||||
let textSideInset: CGFloat = 24.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(
|
component: BundleIconComponent(
|
||||||
name: "Premium/Tmp",
|
name: iconName,
|
||||||
tintColor: nil
|
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
|
transition: .immediate
|
||||||
)
|
)
|
||||||
|
|
||||||
let title = title.update(
|
let title = title.update(
|
||||||
component: MultilineTextComponent(
|
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,
|
horizontalAlignment: .center,
|
||||||
maximumNumberOfLines: 1
|
maximumNumberOfLines: 1
|
||||||
),
|
),
|
||||||
@ -89,19 +176,10 @@ private final class LimitScreenComponent: CombinedComponent {
|
|||||||
transition: .immediate
|
transition: .immediate
|
||||||
)
|
)
|
||||||
|
|
||||||
let textFont = Font.regular(15.0)
|
let textFont = Font.regular(16.0)
|
||||||
let boldTextFont = Font.semibold(15.0)
|
let boldTextFont = Font.semibold(16.0)
|
||||||
|
|
||||||
let textColor = theme.actionSheet.secondaryTextColor
|
let textColor = theme.actionSheet.primaryTextColor
|
||||||
let string: String
|
|
||||||
switch component.subject {
|
|
||||||
case .chatsInFolder:
|
|
||||||
string = ""
|
|
||||||
case .folders:
|
|
||||||
string = ""
|
|
||||||
case .pins:
|
|
||||||
string = strings.DialogList_ExtendedPinLimitError("\(5)", "\(10)").string
|
|
||||||
}
|
|
||||||
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
|
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
|
return nil
|
||||||
}))
|
}))
|
||||||
@ -111,7 +189,7 @@ private final class LimitScreenComponent: CombinedComponent {
|
|||||||
text: attributedText,
|
text: attributedText,
|
||||||
horizontalAlignment: .center,
|
horizontalAlignment: .center,
|
||||||
maximumNumberOfLines: 0,
|
maximumNumberOfLines: 0,
|
||||||
lineSpacing: 0.1
|
lineSpacing: 0.2
|
||||||
),
|
),
|
||||||
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
@ -119,7 +197,7 @@ private final class LimitScreenComponent: CombinedComponent {
|
|||||||
|
|
||||||
let button = button.update(
|
let button = button.update(
|
||||||
component: SolidRoundedButtonComponent(
|
component: SolidRoundedButtonComponent(
|
||||||
title: "Increase Limit",
|
title: strings.Premium_IncreaseLimit,
|
||||||
theme: SolidRoundedButtonComponent.Theme(
|
theme: SolidRoundedButtonComponent.Theme(
|
||||||
backgroundColor: .black,
|
backgroundColor: .black,
|
||||||
backgroundColors: [UIColor(rgb: 0x407af0), UIColor(rgb: 0x9551e8), UIColor(rgb: 0xbf499a), UIColor(rgb: 0xf17b30)],
|
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
|
let width = context.availableSize.width
|
||||||
|
|
||||||
context.add(icon
|
let badgeFrame = CGRect(origin: CGPoint(x: floor((context.availableSize.width - badgeBackground.size.width) / 2.0), y: 33.0), size: badgeBackground.size)
|
||||||
.position(CGPoint(x: width / 2.0, y: 57.0))
|
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
|
context.add(title
|
||||||
.position(CGPoint(x: width / 2.0, y: topInset + 39.0))
|
.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))
|
.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
|
return contentSize
|
||||||
}
|
}
|
||||||
@ -182,7 +271,7 @@ private final class LimitScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class LimitScreen: ViewController {
|
public class LimitScreen: ViewController {
|
||||||
final class Node: ViewControllerTracingNode, UIScrollViewDelegate, UIGestureRecognizerDelegate {
|
final class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate {
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private weak var controller: LimitScreen?
|
private weak var controller: LimitScreen?
|
||||||
|
|
||||||
@ -192,12 +281,9 @@ public class LimitScreen: ViewController {
|
|||||||
let dim: ASDisplayNode
|
let dim: ASDisplayNode
|
||||||
let wrappingView: UIView
|
let wrappingView: UIView
|
||||||
let containerView: UIView
|
let containerView: UIView
|
||||||
let scrollView: UIScrollView
|
|
||||||
let hostView: ComponentHostView<ViewControllerComponentContainer.Environment>
|
let hostView: ComponentHostView<ViewControllerComponentContainer.Environment>
|
||||||
|
|
||||||
private(set) var isExpanded = false
|
|
||||||
private var panGestureRecognizer: UIPanGestureRecognizer?
|
private var panGestureRecognizer: UIPanGestureRecognizer?
|
||||||
private var panGestureArguments: (topInset: CGFloat, offset: CGFloat, scrollView: UIScrollView?, listNode: ListView?)?
|
|
||||||
|
|
||||||
private var currentIsVisible: Bool = false
|
private var currentIsVisible: Bool = false
|
||||||
private var currentLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
|
private var currentLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
|
||||||
@ -218,14 +304,10 @@ public class LimitScreen: ViewController {
|
|||||||
|
|
||||||
self.wrappingView = UIView()
|
self.wrappingView = UIView()
|
||||||
self.containerView = UIView()
|
self.containerView = UIView()
|
||||||
self.scrollView = UIScrollView()
|
|
||||||
self.hostView = ComponentHostView()
|
self.hostView = ComponentHostView()
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.scrollView.delegate = self
|
|
||||||
self.scrollView.showsVerticalScrollIndicator = false
|
|
||||||
|
|
||||||
self.containerView.clipsToBounds = true
|
self.containerView.clipsToBounds = true
|
||||||
self.containerView.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemBackgroundColor
|
self.containerView.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemBackgroundColor
|
||||||
|
|
||||||
@ -233,8 +315,7 @@ public class LimitScreen: ViewController {
|
|||||||
|
|
||||||
self.view.addSubview(self.wrappingView)
|
self.view.addSubview(self.wrappingView)
|
||||||
self.wrappingView.addSubview(self.containerView)
|
self.wrappingView.addSubview(self.containerView)
|
||||||
self.containerView.addSubview(self.scrollView)
|
self.containerView.addSubview(self.hostView)
|
||||||
self.scrollView.addSubview(self.hostView)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
@ -248,8 +329,6 @@ public class LimitScreen: ViewController {
|
|||||||
self.wrappingView.addGestureRecognizer(panRecognizer)
|
self.wrappingView.addGestureRecognizer(panRecognizer)
|
||||||
|
|
||||||
self.dim.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
self.dim.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
||||||
|
|
||||||
self.controller?.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
|
@objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||||
@ -262,16 +341,16 @@ public class LimitScreen: ViewController {
|
|||||||
if let (layout, _) = self.currentLayout {
|
if let (layout, _) = self.currentLayout {
|
||||||
if case .regular = layout.metrics.widthClass {
|
if case .regular = layout.metrics.widthClass {
|
||||||
return false
|
return false
|
||||||
|
} else {
|
||||||
|
let location = gestureRecognizer.location(in: self.containerView)
|
||||||
|
if !self.hostView.frame.contains(location) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
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 {
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer {
|
if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer {
|
||||||
return true
|
return true
|
||||||
@ -303,10 +382,6 @@ public class LimitScreen: ViewController {
|
|||||||
})
|
})
|
||||||
let alphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
|
let alphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
|
||||||
alphaTransition.updateAlpha(node: self.dim, alpha: 0.0)
|
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) {
|
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))
|
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 isLandscape = layout.orientation == .landscape
|
||||||
let edgeTopInset = isLandscape ? 0.0 : self.defaultTopInset
|
|
||||||
let topInset: CGFloat
|
transition.setFrame(view: self.wrappingView, frame: CGRect(origin: CGPoint(), size: layout.size), completion: nil)
|
||||||
if let (panInitialTopInset, panOffset, _, _) = self.panGestureArguments {
|
|
||||||
if effectiveExpanded {
|
var clipFrame: CGRect
|
||||||
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
|
|
||||||
if layout.metrics.widthClass == .compact {
|
if layout.metrics.widthClass == .compact {
|
||||||
self.dim.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.25)
|
self.dim.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.25)
|
||||||
if isLandscape {
|
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)
|
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)
|
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(
|
let environment = ViewControllerComponentContainer.Environment(
|
||||||
statusBarHeight: 0.0,
|
statusBarHeight: 0.0,
|
||||||
navigationHeight: navigationHeight,
|
navigationHeight: navigationHeight,
|
||||||
@ -397,23 +451,28 @@ public class LimitScreen: ViewController {
|
|||||||
isVisible: self.currentIsVisible,
|
isVisible: self.currentIsVisible,
|
||||||
theme: self.theme ?? self.presentationData.theme,
|
theme: self.theme ?? self.presentationData.theme,
|
||||||
strings: self.presentationData.strings,
|
strings: self.presentationData.strings,
|
||||||
|
dateTimeFormat: self.presentationData.dateTimeFormat,
|
||||||
controller: { [weak self] in
|
controller: { [weak self] in
|
||||||
return self?.controller
|
return self?.controller
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
var contentSize = self.hostView.update(
|
let contentSize = self.hostView.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: self.component,
|
component: self.component,
|
||||||
environment: {
|
environment: {
|
||||||
environment
|
environment
|
||||||
},
|
},
|
||||||
forceUpdate: true,
|
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)
|
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
|
private var didPlayAppearAnimation = false
|
||||||
@ -433,194 +492,29 @@ public class LimitScreen: ViewController {
|
|||||||
self.animateIn()
|
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) {
|
@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 {
|
switch recognizer.state {
|
||||||
case .began:
|
case .began:
|
||||||
let point = recognizer.location(in: self.view)
|
break
|
||||||
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)
|
|
||||||
case .changed:
|
case .changed:
|
||||||
guard let (topInset, panOffset, scrollView, listNode) = self.panGestureArguments else {
|
let translation = recognizer.translation(in: self.view).y
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var bounds = self.bounds
|
var bounds = self.bounds
|
||||||
if self.isExpanded {
|
bounds.origin.y = -translation
|
||||||
bounds.origin.y = -max(0.0, translation - edgeTopInset)
|
|
||||||
} else {
|
|
||||||
bounds.origin.y = -translation
|
|
||||||
}
|
|
||||||
bounds.origin.y = min(0.0, bounds.origin.y)
|
bounds.origin.y = min(0.0, bounds.origin.y)
|
||||||
self.bounds = bounds
|
self.bounds = bounds
|
||||||
|
|
||||||
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate)
|
|
||||||
case .ended:
|
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
|
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
|
var bounds = self.bounds
|
||||||
if self.isExpanded {
|
bounds.origin.y = -translation
|
||||||
bounds.origin.y = -max(0.0, translation - edgeTopInset)
|
|
||||||
} else {
|
|
||||||
bounds.origin.y = -translation
|
|
||||||
}
|
|
||||||
bounds.origin.y = min(0.0, bounds.origin.y)
|
bounds.origin.y = min(0.0, bounds.origin.y)
|
||||||
|
|
||||||
scrollView?.bounces = true
|
if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) {
|
||||||
|
|
||||||
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) {
|
|
||||||
self.controller?.dismiss(animated: true, completion: nil)
|
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 {
|
} 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
|
var bounds = self.bounds
|
||||||
let previousBounds = bounds
|
let previousBounds = bounds
|
||||||
bounds.origin.y = 0.0
|
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)
|
self.layer.animateBounds(from: previousBounds, to: self.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||||
}
|
}
|
||||||
case .cancelled:
|
case .cancelled:
|
||||||
self.panGestureArguments = nil
|
var bounds = self.bounds
|
||||||
|
let previousBounds = bounds
|
||||||
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(.animated(duration: 0.3, curve: .easeInOut)))
|
bounds.origin.y = 0.0
|
||||||
|
self.bounds = bounds
|
||||||
|
self.layer.animateBounds(from: previousBounds, to: self.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||||
default:
|
default:
|
||||||
break
|
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 {
|
var node: Node {
|
||||||
@ -667,6 +551,7 @@ public class LimitScreen: ViewController {
|
|||||||
case folders
|
case folders
|
||||||
case chatsInFolder
|
case chatsInFolder
|
||||||
case pins
|
case pins
|
||||||
|
case files
|
||||||
}
|
}
|
||||||
|
|
||||||
public convenience init(context: AccountContext, subject: Subject) {
|
public convenience init(context: AccountContext, subject: Subject) {
|
||||||
@ -678,7 +563,7 @@ public class LimitScreen: ViewController {
|
|||||||
self.component = AnyComponent(component)
|
self.component = AnyComponent(component)
|
||||||
self.theme = nil
|
self.theme = nil
|
||||||
|
|
||||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: context.sharedContext.currentPresentationData.with { $0 }))
|
super.init(navigationBarPresentationData: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init(coder aDecoder: NSCoder) {
|
required public init(coder aDecoder: NSCoder) {
|
||||||
@ -718,37 +603,12 @@ public class LimitScreen: ViewController {
|
|||||||
|
|
||||||
self.node.updateIsVisible(isVisible: false)
|
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) {
|
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
self.currentLayout = layout
|
self.currentLayout = layout
|
||||||
super.containerLayoutUpdated(layout, transition: transition)
|
super.containerLayoutUpdated(layout, transition: transition)
|
||||||
|
|
||||||
let navigationHeight: CGFloat = 56.0
|
let navigationHeight: CGFloat = 56.0
|
||||||
|
|
||||||
self.node.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(transition))
|
self.node.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(transition))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
submodules/PremiumUI/Sources/PremiumIntroScreen.swift
Normal file
1
submodules/PremiumUI/Sources/PremiumIntroScreen.swift
Normal file
@ -0,0 +1 @@
|
|||||||
|
import Foundation
|
@ -434,6 +434,7 @@ public final class SolidRoundedButtonView: UIView {
|
|||||||
private var fontSize: CGFloat
|
private var fontSize: CGFloat
|
||||||
|
|
||||||
private let buttonBackgroundNode: UIImageView
|
private let buttonBackgroundNode: UIImageView
|
||||||
|
private var buttonBackgroundAnimationView: UIImageView?
|
||||||
private let buttonGlossView: SolidRoundedButtonGlossView?
|
private let buttonGlossView: SolidRoundedButtonGlossView?
|
||||||
private let buttonNode: HighlightTrackingButton
|
private let buttonNode: HighlightTrackingButton
|
||||||
private let titleNode: ImmediateTextView
|
private let titleNode: ImmediateTextView
|
||||||
@ -506,6 +507,11 @@ public final class SolidRoundedButtonView: UIView {
|
|||||||
locations.append(delta * CGFloat(i))
|
locations.append(delta * CGFloat(i))
|
||||||
}
|
}
|
||||||
self.buttonBackgroundNode.image = generateGradientImage(size: CGSize(width: 200.0, height: height), colors: theme.backgroundColors, locations: locations, direction: .horizontal)
|
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 {
|
} else {
|
||||||
self.buttonBackgroundNode.backgroundColor = theme.backgroundColor
|
self.buttonBackgroundNode.backgroundColor = theme.backgroundColor
|
||||||
}
|
}
|
||||||
@ -575,6 +581,42 @@ public final class SolidRoundedButtonView: UIView {
|
|||||||
fatalError("init(coder:) has not been implemented")
|
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() {
|
public func transitionToProgress() {
|
||||||
guard self.progressNode == nil else {
|
guard self.progressNode == nil else {
|
||||||
return
|
return
|
||||||
@ -659,6 +701,12 @@ public final class SolidRoundedButtonView: UIView {
|
|||||||
let buttonSize = CGSize(width: width, height: self.buttonHeight)
|
let buttonSize = CGSize(width: width, height: self.buttonHeight)
|
||||||
let buttonFrame = CGRect(origin: CGPoint(), size: buttonSize)
|
let buttonFrame = CGRect(origin: CGPoint(), size: buttonSize)
|
||||||
transition.updateFrame(view: self.buttonBackgroundNode, frame: buttonFrame)
|
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 {
|
if let buttonGlossView = self.buttonGlossView {
|
||||||
transition.updateFrame(view: buttonGlossView, frame: buttonFrame)
|
transition.updateFrame(view: buttonGlossView, frame: buttonFrame)
|
||||||
}
|
}
|
||||||
|
@ -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<Never, NoError> {
|
||||||
|
return postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|
||||||
|
|> mapToSignal { preferencesView -> Signal<Never, NoError> in
|
||||||
|
let appConfiguration: AppConfiguration = preferencesView.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) ?? .defaultValue
|
||||||
|
let configuration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: false)
|
||||||
|
print(configuration)
|
||||||
|
return .never()
|
||||||
|
}
|
||||||
|
}
|
@ -46,6 +46,41 @@ public enum EngineConfiguration {
|
|||||||
self.maxMessageRevokeIntervalInPrivateChats = maxMessageRevokeIntervalInPrivateChats
|
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 {
|
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 {
|
public extension TelegramEngine.EngineData.Item {
|
||||||
enum Configuration {
|
enum Configuration {
|
||||||
public struct Limits: TelegramEngineDataItem, PostboxViewDataItem {
|
public struct Limits: TelegramEngineDataItem, PostboxViewDataItem {
|
||||||
@ -89,5 +139,28 @@ public extension TelegramEngine.EngineData.Item {
|
|||||||
return EngineConfiguration.Limits(limitsConfiguration)
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -491,7 +491,7 @@ public extension TelegramEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func toggleItemPinned(location: TogglePeerChatPinnedLocation, itemId: PinnedItemId) -> Signal<TogglePeerChatPinnedResult, NoError> {
|
public func toggleItemPinned(location: TogglePeerChatPinnedLocation, itemId: PinnedItemId) -> Signal<TogglePeerChatPinnedResult, NoError> {
|
||||||
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> {
|
public func getPinnedItemIds(location: TogglePeerChatPinnedLocation) -> Signal<[PinnedItemId], NoError> {
|
||||||
|
@ -13,8 +13,10 @@ public enum TogglePeerChatPinnedResult {
|
|||||||
case limitExceeded(Int)
|
case limitExceeded(Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_toggleItemPinned(postbox: Postbox, location: TogglePeerChatPinnedLocation, itemId: PinnedItemId) -> Signal<TogglePeerChatPinnedResult, NoError> {
|
func _internal_toggleItemPinned(postbox: Postbox, accountPeerId: PeerId, location: TogglePeerChatPinnedLocation, itemId: PinnedItemId) -> Signal<TogglePeerChatPinnedResult, NoError> {
|
||||||
return postbox.transaction { transaction -> TogglePeerChatPinnedResult in
|
return postbox.transaction { transaction -> TogglePeerChatPinnedResult in
|
||||||
|
let isPremium = transaction.getPeer(accountPeerId)?.isPremium ?? false
|
||||||
|
|
||||||
switch location {
|
switch location {
|
||||||
case let .group(groupId):
|
case let .group(groupId):
|
||||||
var itemIds = transaction.getPinnedItemIds(groupId: groupId)
|
var itemIds = transaction.getPinnedItemIds(groupId: groupId)
|
||||||
@ -37,10 +39,13 @@ func _internal_toggleItemPinned(postbox: Postbox, location: TogglePeerChatPinned
|
|||||||
additionalCount = 1
|
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 limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration)?.get(LimitsConfiguration.self) ?? LimitsConfiguration.defaultValue
|
||||||
|
let userLimitsConfiguration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: isPremium)
|
||||||
|
|
||||||
let limitCount: Int
|
let limitCount: Int
|
||||||
if case .root = groupId {
|
if case .root = groupId {
|
||||||
limitCount = Int(limitsConfiguration.maxPinnedChatCount)
|
limitCount = Int(userLimitsConfiguration.maxPinnedChatCount)
|
||||||
} else {
|
} else {
|
||||||
limitCount = Int(limitsConfiguration.maxArchivedPinnedChatCount)
|
limitCount = Int(limitsConfiguration.maxArchivedPinnedChatCount)
|
||||||
}
|
}
|
||||||
|
@ -2252,7 +2252,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
|
|
||||||
private var initializedCredibilityIcon = false
|
private var initializedCredibilityIcon = false
|
||||||
private var currentPanelStatusData: PeerInfoStatusData?
|
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.state = state
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.avatarListNode.listContainerNode.peer = peer
|
self.avatarListNode.listContainerNode.peer = peer
|
||||||
@ -2567,18 +2567,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
|
|
||||||
if self.isAvatarExpanded {
|
if self.isAvatarExpanded {
|
||||||
let minTitleSize = CGSize(width: titleSize.width * 0.7, height: titleSize.height * 0.7)
|
let minTitleSize = CGSize(width: titleSize.width * 0.7, height: titleSize.height * 0.7)
|
||||||
let minTitleFrame: CGRect
|
let minTitleFrame = CGRect(origin: CGPoint(x: 16.0, y: expandedAvatarHeight - 58.0 - UIScreenPixel + (subtitleSize.height.isZero ? 10.0 : 0.0)), size: minTitleSize)
|
||||||
|
|
||||||
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)
|
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)
|
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)
|
usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize)
|
||||||
} else {
|
} 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 = 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)
|
||||||
|
|
||||||
titleFrame = titleFrame.offsetBy(dx: 0.0, dy: 11.0)
|
|
||||||
|
|
||||||
let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width
|
let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width
|
||||||
if usernameSize.width == 0.0 {
|
if usernameSize.width == 0.0 {
|
||||||
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
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 titleLockOffset: CGFloat = 7.0 + singleTitleLockOffset
|
||||||
let titleMaxLockOffset: CGFloat = 7.0
|
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 titleOffset = -min(titleCollapseOffset, contentOffset)
|
||||||
let titleCollapseFraction = max(0.0, min(1.0, contentOffset / titleCollapseOffset))
|
let titleCollapseFraction = max(0.0, min(1.0, contentOffset / titleCollapseOffset))
|
||||||
|
|
||||||
|
@ -6997,7 +6997,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
let headerInset = sectionInset
|
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 {
|
if !self.isSettings && !self.state.isEditing {
|
||||||
headerHeight += 71.0
|
headerHeight += 71.0
|
||||||
}
|
}
|
||||||
@ -7357,7 +7357,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
let headerInset = sectionInset
|
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
|
let paneAreaExpansionDistance: CGFloat = 32.0
|
||||||
@ -8312,7 +8312,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig
|
|||||||
}
|
}
|
||||||
let headerInset = sectionInset
|
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
|
let titleScale = (fraction * previousTitleNode.bounds.height + (1.0 - fraction) * self.headerNode.titleNodeRawContainer.bounds.height) / previousTitleNode.bounds.height
|
||||||
|
@ -129,6 +129,8 @@ public final class TelegramRootController: NavigationController {
|
|||||||
self.accountSettingsController = accountSettingsController
|
self.accountSettingsController = accountSettingsController
|
||||||
self.rootTabController = tabBarController
|
self.rootTabController = tabBarController
|
||||||
self.pushViewController(tabBarController, animated: false)
|
self.pushViewController(tabBarController, animated: false)
|
||||||
|
|
||||||
|
let _ = getUserLimits(postbox: self.context.account.postbox).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateRootControllers(showCallsTab: Bool) {
|
public func updateRootControllers(showCallsTab: Bool) {
|
||||||
|
@ -723,6 +723,7 @@ public class TranslateScreen: ViewController {
|
|||||||
isVisible: self.currentIsVisible,
|
isVisible: self.currentIsVisible,
|
||||||
theme: self.theme ?? self.presentationData.theme,
|
theme: self.theme ?? self.presentationData.theme,
|
||||||
strings: self.presentationData.strings,
|
strings: self.presentationData.strings,
|
||||||
|
dateTimeFormat: self.presentationData.dateTimeFormat,
|
||||||
controller: { [weak self] in
|
controller: { [weak self] in
|
||||||
return self?.controller
|
return self?.controller
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user