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
ac1f276d15
commit
b5dcba8bae
@ -7559,6 +7559,7 @@ Sorry for the inconvenience.";
|
||||
"Group.Username.RemoveExistingUsernamesFinalInfo" = "You have reserved too many public links. Try revoking the link from an older group or channel, or create a private one instead.";
|
||||
|
||||
"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.TooManyCommunitiesNoPremiumText" = "You are a member of **%@** groups and channels. Please leave some before joining a new one. We are working to let you increase this limit in the future.";
|
||||
"OldChannels.TooManyCommunitiesFinalText" = "You are a member of **%@** groups and channels. Please leave some before joining a new one.";
|
||||
"OldChannels.LeaveCommunities_1" = "Leave %@ Community";
|
||||
"OldChannels.LeaveCommunities_any" = "Leave %@ Communities";
|
||||
|
@ -862,6 +862,7 @@ open class ItemListControllerNode: ASDisplayNode {
|
||||
updateFooterItem = true
|
||||
}
|
||||
if updateFooterItem {
|
||||
let hadFooter = self.footerItem != nil
|
||||
self.footerItem = transition.footerItem
|
||||
if let footerItem = transition.footerItem {
|
||||
let updatedNode = footerItem.node(current: self.footerItemNode)
|
||||
@ -870,13 +871,27 @@ open class ItemListControllerNode: ASDisplayNode {
|
||||
}
|
||||
if self.footerItemNode !== updatedNode {
|
||||
self.footerItemNode = updatedNode
|
||||
|
||||
let footerHeight: CGFloat
|
||||
if let validLayout = self.validLayout {
|
||||
let _ = updatedNode.updateLayout(layout: validLayout.0, transition: .immediate)
|
||||
footerHeight = updatedNode.updateLayout(layout: validLayout.0, transition: .immediate)
|
||||
} else {
|
||||
footerHeight = 100.0
|
||||
}
|
||||
self.addSubnode(updatedNode)
|
||||
|
||||
if !hadFooter && !transition.firstTime {
|
||||
updatedNode.layer.animatePosition(from: CGPoint(x: 0.0, y: footerHeight), to: .zero, duration: 0.25, additive: true)
|
||||
}
|
||||
}
|
||||
} else if let footerItemNode = self.footerItemNode {
|
||||
footerItemNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak footerItemNode] _ in
|
||||
let footerHeight: CGFloat
|
||||
if let validLayout = self.validLayout {
|
||||
footerHeight = footerItemNode.updateLayout(layout: validLayout.0, transition: .immediate)
|
||||
} else {
|
||||
footerHeight = 100.0
|
||||
}
|
||||
footerItemNode.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: footerHeight), duration: 0.25, removeOnCompletion: false, additive: true, completion: { [weak footerItemNode] _ in
|
||||
footerItemNode?.removeFromSupernode()
|
||||
})
|
||||
self.footerItemNode = nil
|
||||
|
@ -89,7 +89,7 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
||||
|
||||
case publicLinkHeader(PresentationTheme, String)
|
||||
case publicLinkAvailability(PresentationTheme, String, Bool)
|
||||
case linksLimitInfo(PresentationTheme, String, Int32, Int32)
|
||||
case linksLimitInfo(PresentationTheme, String, Int32, Int32, Int32, Bool)
|
||||
case editablePublicLink(PresentationTheme, PresentationStrings, String, String)
|
||||
case privateLinkHeader(PresentationTheme, String)
|
||||
case privateLink(PresentationTheme, ExportedInvitation?, [EnginePeer], Int32, Bool)
|
||||
@ -228,12 +228,12 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .linksLimitInfo(lhsTheme, lhsText, lhsLimit, lhsPremiumLimit):
|
||||
if case let .linksLimitInfo(rhsTheme, rhsText, rhsLimit, rhsPremiumLimit) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsLimit == rhsLimit, lhsPremiumLimit == rhsPremiumLimit {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .linksLimitInfo(lhsTheme, lhsText, lhsCount, lhsLimit, lhsPremiumLimit, lhsIsPremiumDisabled):
|
||||
if case let .linksLimitInfo(rhsTheme, rhsText, rhsCount, rhsLimit, rhsPremiumLimit, rhsIsPremiumDisabled) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsCount == rhsCount, lhsLimit == rhsLimit, lhsPremiumLimit == rhsPremiumLimit, lhsIsPremiumDisabled == rhsIsPremiumDisabled {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .privateLinkHeader(lhsTheme, lhsTitle):
|
||||
if case let .privateLinkHeader(rhsTheme, rhsTitle) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle {
|
||||
return true
|
||||
@ -394,8 +394,8 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
||||
let attr = NSMutableAttributedString(string: text, textColor: value ? theme.list.freeTextColor : theme.list.freeTextErrorColor)
|
||||
attr.addAttribute(.font, value: Font.regular(13), range: NSMakeRange(0, attr.length))
|
||||
return ItemListActivityTextItem(displayActivity: value, presentationData: presentationData, text: attr, sectionId: self.section)
|
||||
case let .linksLimitInfo(theme, text, limit, premiumLimit):
|
||||
return IncreaseLimitHeaderItem(theme: theme, strings: presentationData.strings, icon: .link, count: limit, premiumCount: premiumLimit, text: text, sectionId: self.section)
|
||||
case let .linksLimitInfo(theme, text, count, limit, premiumLimit, isPremiumDisabled):
|
||||
return IncreaseLimitHeaderItem(theme: theme, strings: presentationData.strings, icon: .link, count: count, limit: limit, premiumCount: premiumLimit, text: text, isPremiumDisabled: isPremiumDisabled, sectionId: self.section)
|
||||
case let .privateLinkHeader(_, title):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
|
||||
case let .privateLink(_, invite, peers, importersCount, displayImporters):
|
||||
@ -728,9 +728,9 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
||||
displayAvailability = publicChannelsToRevoke != nil && !(publicChannelsToRevoke!.isEmpty)
|
||||
}
|
||||
|
||||
if displayAvailability {
|
||||
if !"".isEmpty && displayAvailability {
|
||||
if let publicChannelsToRevoke = publicChannelsToRevoke {
|
||||
entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(20)").string, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount))
|
||||
// entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(20)").string, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount))
|
||||
|
||||
var index: Int32 = 0
|
||||
for peer in publicChannelsToRevoke.sorted(by: { lhs, rhs in
|
||||
@ -859,7 +859,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
||||
switch mode {
|
||||
case .revokeNames:
|
||||
if let publicChannelsToRevoke = publicChannelsToRevoke {
|
||||
entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(20)").string, 10, 20))
|
||||
// entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(20)").string, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount))
|
||||
|
||||
entries.append(.publicLinkAvailability(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesInfo, false))
|
||||
var index: Int32 = 0
|
||||
@ -922,7 +922,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
||||
|
||||
if displayAvailability {
|
||||
if let publicChannelsToRevoke = publicChannelsToRevoke {
|
||||
entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(20)").string, 10, 20))
|
||||
// entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(20)").string, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount))
|
||||
|
||||
entries.append(.publicLinkAvailability(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesInfo, false))
|
||||
var index: Int32 = 0
|
||||
@ -1352,7 +1352,8 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
|
||||
let signal = combineLatest(
|
||||
presentationData,
|
||||
statePromise.get() |> deliverOnMainQueue,
|
||||
peerView, peersDisablingAddressNameAssignment.get() |> deliverOnMainQueue,
|
||||
peerView,
|
||||
peersDisablingAddressNameAssignment.get() |> deliverOnMainQueue,
|
||||
importersContext,
|
||||
importersState.get(),
|
||||
context.engine.data.get(
|
||||
|
@ -20,17 +20,21 @@ class IncreaseLimitHeaderItem: ListViewItem, ItemListItem {
|
||||
let strings: PresentationStrings
|
||||
let icon: Icon
|
||||
let count: Int32
|
||||
let limit: Int32
|
||||
let premiumCount: Int32
|
||||
let text: String
|
||||
let isPremiumDisabled: Bool
|
||||
let sectionId: ItemListSectionId
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, icon: Icon, count: Int32, premiumCount: Int32, text: String, sectionId: ItemListSectionId) {
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, icon: Icon, count: Int32, limit: Int32, premiumCount: Int32, text: String, isPremiumDisabled: Bool, sectionId: ItemListSectionId) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.icon = icon
|
||||
self.count = count
|
||||
self.limit = limit
|
||||
self.premiumCount = premiumCount
|
||||
self.text = text
|
||||
self.isPremiumDisabled = isPremiumDisabled
|
||||
self.sectionId = sectionId
|
||||
}
|
||||
|
||||
@ -76,7 +80,7 @@ private let textFont = Font.regular(15.0)
|
||||
private let boldTextFont = Font.semibold(15.0)
|
||||
|
||||
class IncreaseLimitHeaderItemNode: ListViewItemNode {
|
||||
private var hostView: ComponentHostView<Empty>
|
||||
private var hostView: ComponentHostView<Empty>?
|
||||
private let titleNode: TextNode
|
||||
private let textNode: TextNode
|
||||
|
||||
@ -92,9 +96,7 @@ class IncreaseLimitHeaderItemNode: ListViewItemNode {
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.contentMode = .left
|
||||
self.textNode.contentsScale = UIScreen.main.scale
|
||||
|
||||
self.hostView = ComponentHostView<Empty>()
|
||||
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
@ -104,7 +106,9 @@ class IncreaseLimitHeaderItemNode: ListViewItemNode {
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.view.addSubview(self.hostView)
|
||||
let hostView = ComponentHostView<Empty>()
|
||||
self.hostView = hostView
|
||||
self.view.addSubview(hostView)
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: IncreaseLimitHeaderItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
@ -142,34 +146,46 @@ class IncreaseLimitHeaderItemNode: ListViewItemNode {
|
||||
badgeIconName = "Premium/Link"
|
||||
}
|
||||
|
||||
let size = strongSelf.hostView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(PremiumLimitDisplayComponent(
|
||||
inactiveColor: item.theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.5),
|
||||
activeColors: [
|
||||
UIColor(rgb: 0x0077ff),
|
||||
UIColor(rgb: 0x6b93ff),
|
||||
UIColor(rgb: 0x8878ff),
|
||||
UIColor(rgb: 0xe46ace)
|
||||
],
|
||||
inactiveTitle: item.strings.Premium_Free,
|
||||
inactiveValue: "",
|
||||
inactiveTitleColor: item.theme.list.itemPrimaryTextColor,
|
||||
activeTitle: item.strings.Premium_Premium,
|
||||
activeValue: "\(item.premiumCount)",
|
||||
activeTitleColor: .white,
|
||||
badgeIconName: badgeIconName,
|
||||
badgeText: "\(item.count)",
|
||||
badgePosition: CGFloat(item.count) / CGFloat(item.premiumCount),
|
||||
isPremiumDisabled: false
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: layout.size.width - params.leftInset - params.rightInset, height: 200.0)
|
||||
)
|
||||
strongSelf.hostView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - size.width) / 2.0), y: -30.0), size: size)
|
||||
let gradientColors: [UIColor]
|
||||
if item.isPremiumDisabled {
|
||||
gradientColors = [
|
||||
UIColor(rgb: 0x007afe),
|
||||
UIColor(rgb: 0x5494ff)
|
||||
]
|
||||
} else {
|
||||
gradientColors = [
|
||||
UIColor(rgb: 0x0077ff),
|
||||
UIColor(rgb: 0x6b93ff),
|
||||
UIColor(rgb: 0x8878ff),
|
||||
UIColor(rgb: 0xe46ace)
|
||||
]
|
||||
}
|
||||
|
||||
let _ = textApply()
|
||||
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - textLayout.size.width) / 2.0), y: size.height + textSpacing), size: textLayout.size)
|
||||
if let hostView = strongSelf.hostView {
|
||||
let size = hostView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(PremiumLimitDisplayComponent(
|
||||
inactiveColor: item.theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.5),
|
||||
activeColors: gradientColors,
|
||||
inactiveTitle: item.strings.Premium_Free,
|
||||
inactiveValue: item.count > item.limit ? "\(item.limit)" : "",
|
||||
inactiveTitleColor: item.theme.list.itemPrimaryTextColor,
|
||||
activeTitle: item.strings.Premium_Premium,
|
||||
activeValue: item.count >= item.premiumCount ? "" : "\(item.premiumCount)",
|
||||
activeTitleColor: .white,
|
||||
badgeIconName: badgeIconName,
|
||||
badgeText: "\(item.count)",
|
||||
badgePosition: CGFloat(item.count) / CGFloat(item.premiumCount),
|
||||
isPremiumDisabled: item.isPremiumDisabled
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: layout.size.width - params.leftInset - params.rightInset, height: 200.0)
|
||||
)
|
||||
hostView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - size.width) / 2.0), y: -30.0), size: size)
|
||||
|
||||
let _ = textApply()
|
||||
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - textLayout.size.width) / 2.0), y: size.height + textSpacing), size: textLayout.size)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ private enum OldChannelsEntryId: Hashable {
|
||||
}
|
||||
|
||||
private enum OldChannelsEntry: ItemListNodeEntry {
|
||||
case info(Int32, Int32, String)
|
||||
case info(Int32, Int32, Int32, String, Bool)
|
||||
case peersHeader(String)
|
||||
case peer(Int, InactiveChannel, Bool)
|
||||
|
||||
@ -110,8 +110,8 @@ private enum OldChannelsEntry: ItemListNodeEntry {
|
||||
|
||||
static func ==(lhs: OldChannelsEntry, rhs: OldChannelsEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .info(count, premiumCount, text):
|
||||
if case .info(count, premiumCount, text) = rhs {
|
||||
case let .info(count, limit, premiumLimit, text, isPremiumDisabled):
|
||||
if case .info(count, limit, premiumLimit, text, isPremiumDisabled) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -168,8 +168,8 @@ private enum OldChannelsEntry: ItemListNodeEntry {
|
||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||
let arguments = arguments as! OldChannelsItemArguments
|
||||
switch self {
|
||||
case let .info(count, premiumCount, text):
|
||||
return IncreaseLimitHeaderItem(theme: presentationData.theme, strings: presentationData.strings, icon: .group, count: count, premiumCount: premiumCount, text: text, sectionId: self.section)
|
||||
case let .info(count, limit, premiumLimit, text, isPremiumDisabled):
|
||||
return IncreaseLimitHeaderItem(theme: presentationData.theme, strings: presentationData.strings, icon: .group, count: count, limit: limit, premiumCount: premiumLimit, text: text, isPremiumDisabled: isPremiumDisabled, sectionId: self.section)
|
||||
case let .peersHeader(title):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
|
||||
case let .peer(_, peer, selected):
|
||||
@ -185,10 +185,24 @@ private struct OldChannelsState: Equatable {
|
||||
var isSearching: Bool = false
|
||||
}
|
||||
|
||||
private func oldChannelsEntries(presentationData: PresentationData, state: OldChannelsState, limit: Int32, premiumLimit: Int32, peers: [InactiveChannel]?, intent: OldChannelsControllerIntent) -> [OldChannelsEntry] {
|
||||
private func oldChannelsEntries(presentationData: PresentationData, state: OldChannelsState, isPremium: Bool, isPremiumDisabled: Bool, limit: Int32, premiumLimit: Int32, peers: [InactiveChannel]?, intent: OldChannelsControllerIntent) -> [OldChannelsEntry] {
|
||||
var entries: [OldChannelsEntry] = []
|
||||
|
||||
entries.append(.info(limit, premiumLimit, presentationData.strings.OldChannels_TooManyCommunitiesText("\(limit)", "\(premiumLimit)").string))
|
||||
|
||||
let count = max(limit, Int32(peers?.count ?? 0))
|
||||
var text: String?
|
||||
if count >= premiumLimit {
|
||||
text = presentationData.strings.OldChannels_TooManyCommunitiesFinalText("\(premiumLimit)").string
|
||||
} else if count >= limit {
|
||||
if isPremiumDisabled {
|
||||
text = presentationData.strings.OldChannels_TooManyCommunitiesNoPremiumText("\(count)").string
|
||||
} else {
|
||||
text = presentationData.strings.OldChannels_TooManyCommunitiesText("\(count)", "\(premiumLimit)").string
|
||||
}
|
||||
}
|
||||
|
||||
if let text = text {
|
||||
entries.append(.info(count, limit, premiumLimit, text, isPremiumDisabled))
|
||||
}
|
||||
|
||||
if let peers = peers, !peers.isEmpty {
|
||||
entries.append(.peersHeader(presentationData.strings.OldChannels_ChannelsHeader))
|
||||
@ -265,8 +279,6 @@ public func oldChannelsController(context: AccountContext, updatedPresentationDa
|
||||
|
||||
var previousPeersWereEmpty = true
|
||||
|
||||
|
||||
|
||||
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
|
||||
let signal = combineLatest(
|
||||
queue: Queue.mainQueue(),
|
||||
@ -283,7 +295,8 @@ public func oldChannelsController(context: AccountContext, updatedPresentationDa
|
||||
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
||||
dismissImpl?()
|
||||
})
|
||||
let (_, limits, premiumLimits) = limits
|
||||
let (accountPeer, limits, premiumLimits) = limits
|
||||
let isPremium = accountPeer?.isPremium ?? false
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.OldChannels_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
|
||||
@ -322,6 +335,7 @@ public func oldChannelsController(context: AccountContext, updatedPresentationDa
|
||||
buttonText = presentationData.strings.Premium_IncreaseLimit
|
||||
colorful = true
|
||||
}
|
||||
|
||||
let footerItem: IncreaseLimitFooterItem?
|
||||
if state.isSearching && state.selectedPeers.count == 0 {
|
||||
footerItem = nil
|
||||
@ -336,7 +350,9 @@ public func oldChannelsController(context: AccountContext, updatedPresentationDa
|
||||
})
|
||||
}
|
||||
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: oldChannelsEntries(presentationData: presentationData, state: state, limit: limits.maxChannelsCount, premiumLimit: premiumLimits.maxChannelsCount, peers: peers, intent: intent), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, footerItem: footerItem, initialScrollToItem: ListViewScrollToItem(index: 0, position: .top(-navigationBarSearchContentHeight), animated: false, curve: .Default(duration: 0.0), directionHint: .Up), crossfadeState: peersAreEmptyUpdated, animateChanges: false)
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: oldChannelsEntries(presentationData: presentationData, state: state, isPremium: isPremium, isPremiumDisabled: premiumConfiguration.isPremiumDisabled, limit: limits.maxChannelsCount, premiumLimit: premiumLimits.maxChannelsCount, peers: peers, intent: intent), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, footerItem: footerItem, initialScrollToItem: ListViewScrollToItem(index: 0, position: .top(-navigationBarSearchContentHeight), animated: false, curve: .Default(duration: 0.0), directionHint: .Up), crossfadeState: peersAreEmptyUpdated, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
||||
private let gloss: Bool
|
||||
|
||||
private let buttonBackgroundNode: ASImageNode
|
||||
private var buttonBackgroundAnimationView: UIImageView?
|
||||
|
||||
private var shimmerView: ShimmerEffectForegroundView?
|
||||
private var borderView: UIView?
|
||||
@ -173,6 +174,8 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
||||
self.buttonBackgroundNode = ASImageNode()
|
||||
self.buttonBackgroundNode.displaysAsynchronously = false
|
||||
self.buttonBackgroundNode.clipsToBounds = true
|
||||
|
||||
self.buttonBackgroundNode.backgroundColor = theme.backgroundColor
|
||||
if theme.backgroundColors.count > 1 {
|
||||
self.buttonBackgroundNode.backgroundColor = nil
|
||||
|
||||
@ -182,9 +185,12 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
||||
locations.append(delta * CGFloat(i))
|
||||
}
|
||||
self.buttonBackgroundNode.image = generateGradientImage(size: CGSize(width: 200.0, height: height), colors: theme.backgroundColors, locations: locations, direction: .horizontal)
|
||||
} else {
|
||||
self.buttonBackgroundNode.backgroundColor = theme.backgroundColor
|
||||
|
||||
let buttonBackgroundAnimationView = UIImageView()
|
||||
buttonBackgroundAnimationView.image = generateGradientImage(size: CGSize(width: 200.0, height: height), colors: theme.backgroundColors, locations: locations, direction: .horizontal)
|
||||
self.buttonBackgroundAnimationView = buttonBackgroundAnimationView
|
||||
}
|
||||
|
||||
self.buttonBackgroundNode.cornerRadius = cornerRadius
|
||||
|
||||
self.buttonNode = HighlightTrackingButtonNode()
|
||||
@ -244,34 +250,92 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
||||
self.buttonBackgroundNode.layer.cornerCurve = .continuous
|
||||
}
|
||||
|
||||
if let buttonBackgroundAnimationView = self.buttonBackgroundAnimationView {
|
||||
self.buttonBackgroundNode.view.addSubview(buttonBackgroundAnimationView)
|
||||
}
|
||||
|
||||
self.setupGloss()
|
||||
}
|
||||
|
||||
private func setupGloss() {
|
||||
if self.gloss {
|
||||
let shimmerView = ShimmerEffectForegroundView()
|
||||
self.shimmerView = shimmerView
|
||||
|
||||
if #available(iOS 13.0, *) {
|
||||
shimmerView.layer.cornerCurve = .continuous
|
||||
shimmerView.layer.cornerRadius = self.buttonCornerRadius
|
||||
if self.shimmerView == nil {
|
||||
let shimmerView = ShimmerEffectForegroundView()
|
||||
self.shimmerView = shimmerView
|
||||
|
||||
if #available(iOS 13.0, *) {
|
||||
shimmerView.layer.cornerCurve = .continuous
|
||||
shimmerView.layer.cornerRadius = self.buttonCornerRadius
|
||||
}
|
||||
|
||||
let borderView = UIView()
|
||||
borderView.isUserInteractionEnabled = false
|
||||
self.borderView = borderView
|
||||
|
||||
let borderMaskView = UIView()
|
||||
borderMaskView.layer.borderWidth = 1.0 + UIScreenPixel
|
||||
borderMaskView.layer.borderColor = UIColor.white.cgColor
|
||||
borderMaskView.layer.cornerRadius = self.buttonCornerRadius
|
||||
borderView.mask = borderMaskView
|
||||
self.borderMaskView = borderMaskView
|
||||
|
||||
let borderShimmerView = ShimmerEffectForegroundView()
|
||||
self.borderShimmerView = borderShimmerView
|
||||
borderView.addSubview(borderShimmerView)
|
||||
|
||||
self.view.insertSubview(shimmerView, belowSubview: self.buttonNode.view)
|
||||
self.view.insertSubview(borderView, belowSubview: self.buttonNode.view)
|
||||
|
||||
self.updateShimmerParameters()
|
||||
|
||||
if let width = self.validLayout {
|
||||
_ = self.updateLayout(width: width, transition: .immediate)
|
||||
}
|
||||
}
|
||||
} else if self.shimmerView != nil {
|
||||
self.shimmerView?.removeFromSuperview()
|
||||
self.borderView?.removeFromSuperview()
|
||||
self.borderMaskView?.removeFromSuperview()
|
||||
self.borderShimmerView?.removeFromSuperview()
|
||||
|
||||
let borderView = UIView()
|
||||
borderView.isUserInteractionEnabled = false
|
||||
self.borderView = borderView
|
||||
self.shimmerView = nil
|
||||
self.borderView = nil
|
||||
self.borderMaskView = nil
|
||||
self.borderShimmerView = nil
|
||||
}
|
||||
}
|
||||
|
||||
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 -= buttonBackgroundAnimationView.frame.width * 0.35
|
||||
}
|
||||
buttonBackgroundAnimationView.center = CGPoint(x: newValue, y: buttonBackgroundAnimationView.bounds.size.height / 2.0)
|
||||
|
||||
let borderMaskView = UIView()
|
||||
borderMaskView.layer.borderWidth = 1.0 + UIScreenPixel
|
||||
borderMaskView.layer.borderColor = UIColor.white.cgColor
|
||||
borderMaskView.layer.cornerRadius = self.buttonCornerRadius
|
||||
borderView.mask = borderMaskView
|
||||
self.borderMaskView = borderMaskView
|
||||
CATransaction.begin()
|
||||
|
||||
let borderShimmerView = ShimmerEffectForegroundView()
|
||||
self.borderShimmerView = borderShimmerView
|
||||
borderView.addSubview(borderShimmerView)
|
||||
let animation = CABasicAnimation(keyPath: "position.x")
|
||||
animation.duration = 4.5
|
||||
animation.fromValue = previousValue
|
||||
animation.toValue = newValue
|
||||
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
||||
|
||||
self.view.insertSubview(shimmerView, belowSubview: self.buttonNode.view)
|
||||
self.view.insertSubview(borderView, belowSubview: self.buttonNode.view)
|
||||
|
||||
self.updateShimmerParameters()
|
||||
CATransaction.setCompletionBlock { [weak self] in
|
||||
// if let isCurrentlyInHierarchy = self?.isCurrentlyInHierarchy, isCurrentlyInHierarchy {
|
||||
self?.setupGradientAnimations()
|
||||
// }
|
||||
}
|
||||
|
||||
buttonBackgroundAnimationView.layer.add(animation, forKey: "movement")
|
||||
CATransaction.commit()
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,6 +418,8 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
||||
animationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
|
||||
self.buttonBackgroundNode.backgroundColor = theme.backgroundColor
|
||||
if theme.backgroundColors.count > 1 {
|
||||
self.buttonBackgroundNode.backgroundColor = nil
|
||||
|
||||
@ -363,9 +429,17 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
||||
locations.append(delta * CGFloat(i))
|
||||
}
|
||||
self.buttonBackgroundNode.image = generateGradientImage(size: CGSize(width: 200.0, height: self.buttonHeight), colors: theme.backgroundColors, locations: locations, direction: .horizontal)
|
||||
|
||||
if self.buttonBackgroundAnimationView == nil {
|
||||
let buttonBackgroundAnimationView = UIImageView()
|
||||
self.buttonBackgroundAnimationView = buttonBackgroundAnimationView
|
||||
self.buttonBackgroundNode.view.addSubview(buttonBackgroundAnimationView)
|
||||
}
|
||||
|
||||
self.buttonBackgroundAnimationView?.image = self.buttonBackgroundNode.image
|
||||
} else {
|
||||
self.buttonBackgroundNode.backgroundColor = theme.backgroundColor
|
||||
self.buttonBackgroundNode.image = nil
|
||||
self.buttonBackgroundAnimationView?.image = nil
|
||||
}
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.title ?? "", font: self.font == .bold ? Font.semibold(self.fontSize) : Font.regular(self.fontSize), textColor: theme.foregroundColor)
|
||||
@ -407,6 +481,14 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
||||
borderShimmerView.updateAbsoluteRect(CGRect(origin: CGPoint(x: width * 4.0, y: 0.0), size: buttonSize), within: CGSize(width: width * 9.0, height: buttonHeight))
|
||||
}
|
||||
|
||||
if let buttonBackgroundAnimationView = self.buttonBackgroundAnimationView {
|
||||
buttonBackgroundAnimationView.bounds = CGRect(origin: CGPoint(), size: CGSize(width: buttonSize.width * 2.4, height: buttonSize.height))
|
||||
if buttonBackgroundAnimationView.layer.animation(forKey: "movement") == nil {
|
||||
buttonBackgroundAnimationView.center = CGPoint(x: buttonSize.width * 2.4 / 2.0 - buttonBackgroundAnimationView.frame.width * 0.35, y: buttonSize.height / 2.0)
|
||||
}
|
||||
self.setupGradientAnimations()
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.buttonNode, frame: buttonFrame)
|
||||
|
||||
if self.title != self.titleNode.attributedText?.string {
|
||||
@ -663,7 +745,7 @@ public final class SolidRoundedButtonView: UIView {
|
||||
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)
|
||||
buttonBackgroundAnimationView.image = self.buttonBackgroundNode.image
|
||||
self.buttonBackgroundNode.addSubview(buttonBackgroundAnimationView)
|
||||
self.buttonBackgroundAnimationView = buttonBackgroundAnimationView
|
||||
}
|
||||
@ -723,6 +805,10 @@ public final class SolidRoundedButtonView: UIView {
|
||||
self.setupGloss()
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func setupGloss() {
|
||||
if self.gloss {
|
||||
@ -772,10 +858,6 @@ public final class SolidRoundedButtonView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func setupGradientAnimations() {
|
||||
guard let buttonBackgroundAnimationView = self.buttonBackgroundAnimationView else {
|
||||
return
|
||||
@ -943,8 +1025,10 @@ public final class SolidRoundedButtonView: UIView {
|
||||
locations.append(delta * CGFloat(i))
|
||||
}
|
||||
self.buttonBackgroundNode.image = generateGradientImage(size: CGSize(width: 200.0, height: self.buttonHeight), colors: theme.backgroundColors, locations: locations, direction: .horizontal)
|
||||
self.buttonBackgroundAnimationView?.image = self.buttonBackgroundNode.image
|
||||
} else {
|
||||
self.buttonBackgroundNode.image = nil
|
||||
self.buttonBackgroundAnimationView?.image = nil
|
||||
}
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.title ?? "", font: self.font == .bold ? Font.semibold(self.fontSize) : Font.regular(self.fontSize), textColor: theme.foregroundColor)
|
||||
|
Loading…
x
Reference in New Issue
Block a user