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