Group boosts

This commit is contained in:
Ilya Laktyushin 2024-02-09 05:23:52 +04:00
parent 43204554cd
commit b4ca0637a6
6 changed files with 212 additions and 104 deletions

View File

@ -10222,6 +10222,10 @@ Sorry for the inconvenience.";
"ReassignBoost.Boosts_any" = "%@ boosts are reassigned";
"ReassignBoost.OtherChannels_1" = "%@ other channel";
"ReassignBoost.OtherChannels_any" = "%@ other channels";
"ReassignBoost.OtherGroups_1" = "%@ other group";
"ReassignBoost.OtherGroups_any" = "%@ other groups";
"ReassignBoost.OtherGroupsAndChannels_1" = "%@ other group and channel";
"ReassignBoost.OtherGroupsAndChannels_any" = "%@ other groups and channels";
"ChannelBoost.MoreBoosts.Title" = "More Boosts Needed";
"ChannelBoost.MoreBoosts.Text" = "To boost **%1$@** again, gift **Telegram Premium** to a friend and get **%2$@** additional boosts.";
@ -11247,7 +11251,7 @@ Sorry for the inconvenience.";
"Chat.Giveaway.Message.Group.Participants" = "All members of this group:";
"Chat.Giveaway.Message.Group.ParticipantsNew" = "All users who join this group after this date:";
"Chat.Giveaway.Message.Group.ParticipantsMany" = "All subscribers of the channels below:";
"Chat.Giveaway.Message.Group.ParticipantsMany" = "All members of the groups below:";
"Chat.Giveaway.Message.Group.ParticipantsNewMany" = "All users who join the groups below after this date:";
"Story.Privacy.KeepOnGroupPage" = "Post to Group Page";
@ -11260,3 +11264,15 @@ Sorry for the inconvenience.";
"BoostGift.Members.MaximumReached" = "You can select maximum %@ members.";
"Chat.ErrorCantBoost" = "Sorry, you can't boost this group or channel.";
"ChannelBoost.Boost" = "Boost";
"ChannelBoost.Copy" = "Copy";
"Chat.Giveaway.Info.OtherGroups_1" = "**%@** other listed group";
"Chat.Giveaway.Info.OtherGroups_any" = "**%@** other listed groups";
"Chat.Giveaway.Info.OtherGroupsAndChannels_1" = "**%@** other listed group and channel";
"Chat.Giveaway.Info.OtherGroupsAndChannels_any" = "**%@** other listed groups and channels";
"Chat.Giveaway.Info.OtherChannelsAndGroups_1" = "**%@** other listed channel and group";
"Chat.Giveaway.Info.OtherChannelsAndGroups_any" = "**%@** other listed channels and groups";

View File

@ -134,6 +134,7 @@ final class BadgeLabelView: UIView {
} else {
itemTransition = transition.withAnimation(.none)
itemView = StackView()
itemView.color = self.color
self.itemViews[i] = itemView
self.addSubview(itemView)
}

View File

@ -70,21 +70,38 @@ public func presentGiveawayInfoController(
onlyNewSubscribers = true
}
var channelsCount: Int32 = 1
if let giveaway {
channelsCount = Int32(giveaway.channelPeerIds.count)
} else if let giveawayResults {
channelsCount = 1 + giveawayResults.additionalChannelsCount
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let author = message.forwardInfo?.author ?? message.author?._asPeer()
var isGroup = false
if let channel = author as? TelegramChannel, case .group = channel.info {
isGroup = true
}
var groupsAndChannels = false
var channelsCount: Int32 = 1
if let giveaway {
channelsCount = Int32(giveaway.channelPeerIds.count)
var channelCount = 0
var groupCount = 0
for peerId in giveaway.channelPeerIds {
if let peer = message.peers[peerId] as? TelegramChannel {
switch peer.info {
case .broadcast:
channelCount += 1
case .group:
groupCount += 1
}
}
}
if groupCount > 0 && channelCount > 0 {
groupsAndChannels = true
}
} else if let giveawayResults {
channelsCount = 1 + giveawayResults.additionalChannelsCount
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var peerName = ""
if let channel = author as? TelegramChannel {
peerName = EnginePeer(channel).compactDisplayTitle
@ -132,18 +149,43 @@ public func presentGiveawayInfoController(
}
}
var otherText: String = ""
if channelsCount > 1 {
if isGroup {
if groupsAndChannels {
if channelsCount == 2 {
otherText = presentationData.strings.Chat_Giveaway_Info_OtherChannels(Int32(channelsCount - 1))
} else {
otherText = presentationData.strings.Chat_Giveaway_Info_OtherGroupsAndChannels(Int32(channelsCount - 1))
}
} else {
otherText = presentationData.strings.Chat_Giveaway_Info_OtherGroups(Int32(channelsCount - 1))
}
} else {
if groupsAndChannels {
if channelsCount == 2 {
otherText = presentationData.strings.Chat_Giveaway_Info_OtherGroups(Int32(channelsCount - 1))
} else {
otherText = presentationData.strings.Chat_Giveaway_Info_OtherChannelsAndGroups(Int32(channelsCount - 1))
}
} else {
otherText = presentationData.strings.Chat_Giveaway_Info_OtherChannels(Int32(channelsCount - 1))
}
}
}
let ending: String
if onlyNewSubscribers {
let randomUsers = presentationData.strings.Chat_Giveaway_Info_RandomUsers(quantity)
if channelsCount > 1 {
ending = presentationData.strings.Chat_Giveaway_Info_OngoingNewMany(untilDate, randomUsers, peerName, presentationData.strings.Chat_Giveaway_Info_OtherChannels(Int32(channelsCount - 1)), startDate).string
ending = presentationData.strings.Chat_Giveaway_Info_OngoingNewMany(untilDate, randomUsers, peerName, otherText, startDate).string
} else {
ending = presentationData.strings.Chat_Giveaway_Info_OngoingNew(untilDate, randomUsers, peerName, startDate).string
}
} else {
let randomSubscribers = isGroup ? presentationData.strings.Chat_Giveaway_Info_Group_RandomMembers(quantity) : presentationData.strings.Chat_Giveaway_Info_RandomSubscribers(quantity)
if channelsCount > 1 {
ending = presentationData.strings.Chat_Giveaway_Info_OngoingMany(untilDate, randomSubscribers, peerName, presentationData.strings.Chat_Giveaway_Info_OtherChannels(Int32(channelsCount - 1))).string
ending = presentationData.strings.Chat_Giveaway_Info_OngoingMany(untilDate, randomSubscribers, peerName, otherText).string
} else {
ending = presentationData.strings.Chat_Giveaway_Info_Ongoing(untilDate, randomSubscribers, peerName).string
}
@ -153,7 +195,7 @@ public func presentGiveawayInfoController(
switch status {
case .notQualified:
if channelsCount > 1 {
participation = presentationData.strings.Chat_Giveaway_Info_NotQualifiedMany(peerName, presentationData.strings.Chat_Giveaway_Info_OtherChannels(Int32(channelsCount - 1)), untilDate).string
participation = presentationData.strings.Chat_Giveaway_Info_NotQualifiedMany(peerName, otherText, untilDate).string
} else {
participation = presentationData.strings.Chat_Giveaway_Info_NotQualified(peerName, untilDate).string
}
@ -177,7 +219,7 @@ public func presentGiveawayInfoController(
}
case .participating:
if channelsCount > 1 {
participation = presentationData.strings.Chat_Giveaway_Info_ParticipatingMany(peerName, presentationData.strings.Chat_Giveaway_Info_OtherChannels(Int32(channelsCount - 1))).string
participation = presentationData.strings.Chat_Giveaway_Info_ParticipatingMany(peerName, otherText).string
} else {
participation = presentationData.strings.Chat_Giveaway_Info_Participating(peerName).string
}

View File

@ -918,10 +918,9 @@ private final class SheetContent: CombinedComponent {
)
contentSize.height += linkButton.size.height + 16.0
//TODO:localize
let boostButton = boostButton.update(
component: SolidRoundedButtonComponent(
title: "Boost",
title: strings.ChannelBoost_Boost,
theme: SolidRoundedButtonComponent.Theme(
backgroundColor: .black,
backgroundColors: buttonGradientColors,
@ -945,7 +944,7 @@ private final class SheetContent: CombinedComponent {
let copyButton = copyButton.update(
component: SolidRoundedButtonComponent(
title: "Copy",
title: strings.ChannelBoost_Copy,
theme: SolidRoundedButtonComponent.Theme(
backgroundColor: .black,
backgroundColors: buttonGradientColors,
@ -1185,6 +1184,7 @@ private final class SheetContent: CombinedComponent {
private final class BoostLevelsContainerComponent: CombinedComponent {
class ExternalState {
var isGroup: Bool = false
var contentHeight: CGFloat = 0.0
}
let context: AccountContext
@ -1201,6 +1201,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
let openStats: (() -> Void)?
let openGift: (() -> Void)?
let openPeer: ((EnginePeer) -> Void)?
let updated: () -> Void
init(
context: AccountContext,
@ -1216,7 +1217,8 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
dismiss: @escaping () -> Void,
openStats: (() -> Void)?,
openGift: (() -> Void)?,
openPeer: ((EnginePeer) -> Void)?
openPeer: ((EnginePeer) -> Void)?,
updated: @escaping () -> Void
) {
self.context = context
self.theme = theme
@ -1232,6 +1234,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
self.openStats = openStats
self.openGift = openGift
self.openPeer = openPeer
self.updated = updated
}
static func ==(lhs: BoostLevelsContainerComponent, rhs: BoostLevelsContainerComponent) -> Bool {
@ -1264,7 +1267,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
private var disposable: Disposable?
private(set) var peer: EnginePeer?
init(context: AccountContext, peerId: EnginePeer.Id) {
init(context: AccountContext, peerId: EnginePeer.Id, updated: @escaping () -> Void) {
super.init()
self.disposable = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
@ -1274,6 +1277,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
}
self.peer = peer
self.updated()
updated()
})
}
@ -1283,7 +1287,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
}
func makeState() -> State {
return State(context: self.context, peerId: self.peerId)
return State(context: self.context, peerId: self.peerId, updated: self.updated)
}
static var body: Body {
@ -1295,6 +1299,8 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
let statsButton = Child(Button.self)
let closeButton = Child(Button.self)
let externalScrollState = ScrollComponent<Empty>.ExternalState()
return { context in
let state = context.state
@ -1337,6 +1343,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
openPeer: component.openPeer
)
),
externalState: externalScrollState,
contentInsets: UIEdgeInsets(top: topInset, left: 0.0, bottom: 0.0, right: 0.0),
contentOffsetUpdated: { [weak state] topContentOffset, _ in
state?.topContentOffset = topContentOffset
@ -1349,6 +1356,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
availableSize: context.availableSize,
transition: context.transition
)
component.externalState.contentHeight = externalScrollState.contentHeight
let background = background.update(
component: Rectangle(color: theme.overallDarkAppearance ? theme.list.blocksBackgroundColor : theme.list.plainBackgroundColor),
@ -1563,11 +1571,11 @@ public class PremiumBoostLevelsScreen: ViewController {
let footerContainerView: UIView
let footerView: ComponentHostView<Empty>
private let externalState = BoostLevelsContainerComponent.ExternalState()
private let containerExternalState = BoostLevelsContainerComponent.ExternalState()
private(set) var isExpanded = false
private var panGestureRecognizer: UIPanGestureRecognizer?
private var panGestureArguments: (topInset: CGFloat, offset: CGFloat, scrollView: UIScrollView?, listNode: ListView?)?
private var panGestureArguments: (topInset: CGFloat, offset: CGFloat, scrollView: UIScrollView?)?
private let hapticFeedback = HapticFeedback()
@ -1633,7 +1641,7 @@ public class PremiumBoostLevelsScreen: ViewController {
self.updatedState.set(.single(InternalBoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: boosts + 1)))
if let (replacedBoosts, inChannels) = controller.replacedBoosts {
if let (replacedBoosts, sourcePeers) = controller.replacedBoosts {
currentMyBoostCount += 1
self.boostState = initialState.displayData(myBoostCount: myBoostCount, currentMyBoostCount: 1)
@ -1643,7 +1651,29 @@ public class PremiumBoostLevelsScreen: ViewController {
Queue.mainQueue().after(0.3) {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let undoController = UndoOverlayController(presentationData: presentationData, content: .image(image: generateTintedImage(image: UIImage(bundleImageName: "Premium/BoostReplaceIcon"), color: .white)!, title: nil, text: presentationData.strings.ReassignBoost_Success(presentationData.strings.ReassignBoost_Boosts(replacedBoosts), presentationData.strings.ReassignBoost_OtherChannels(inChannels)).string, round: false, undoText: nil), elevatedLayout: false, position: .top, action: { _ in return true })
var groupCount: Int32 = 0
var channelCount: Int32 = 0
for peer in sourcePeers {
if case let .channel(channel) = peer {
switch channel.info {
case .broadcast:
channelCount += 1
case .group:
groupCount += 1
}
}
}
let otherText: String
if channelCount > 0 && groupCount == 0 {
otherText = presentationData.strings.ReassignBoost_OtherChannels(channelCount)
} else if groupCount > 0 && channelCount == 0 {
otherText = presentationData.strings.ReassignBoost_OtherGroups(groupCount)
} else {
otherText = presentationData.strings.ReassignBoost_OtherGroupsAndChannels(Int32(sourcePeers.count))
}
let text = presentationData.strings.ReassignBoost_Success(presentationData.strings.ReassignBoost_Boosts(replacedBoosts), otherText).string
let undoController = UndoOverlayController(presentationData: presentationData, content: .image(image: generateTintedImage(image: UIImage(bundleImageName: "Premium/BoostReplaceIcon"), color: .white)!, title: nil, text: text, round: false, undoText: nil), elevatedLayout: false, position: .top, action: { _ in return true })
controller.present(undoController, in: .current)
}
}
@ -1732,31 +1762,10 @@ public class PremiumBoostLevelsScreen: ViewController {
self.currentLayout = layout
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 if let dismissOffset = self.dismissOffset, !dismissOffset.isZero {
topInset = edgeTopInset * dismissOffset
} 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)
var containerTopInset: CGFloat = 0.0
let clipFrame: CGRect
if layout.metrics.widthClass == .compact {
self.dim.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.25)
@ -1778,7 +1787,7 @@ public class PremiumBoostLevelsScreen: ViewController {
clipFrame = CGRect(origin: CGPoint(), size: layout.size)
} else {
let coveredByModalTransition: CGFloat = 0.0
var containerTopInset: CGFloat = 10.0
containerTopInset = 10.0
if let statusBarHeight = layout.statusBarHeight {
containerTopInset += statusBarHeight
}
@ -1806,19 +1815,45 @@ public class PremiumBoostLevelsScreen: ViewController {
transition.setFrame(view: self.containerView, frame: clipFrame)
var effectiveExpanded = self.isExpanded
if case .regular = layout.metrics.widthClass {
effectiveExpanded = true
}
self.updated(transition: transition)
let contentHeight = self.containerExternalState.contentHeight
if contentHeight > 0.0 && contentHeight < 400.0, let view = self.footerView.componentView as? FooterComponent.View {
view.backgroundView.alpha = 0.0
view.separator.opacity = 0.0
}
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 if let dismissOffset = self.dismissOffset, !dismissOffset.isZero {
topInset = edgeTopInset * dismissOffset
} 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)
var footerHeight: CGFloat = 8.0 + 50.0
footerHeight += layout.intrinsicInsets.bottom > 0.0 ? layout.intrinsicInsets.bottom + 5.0 : 8.0
let modalProgress = isLandscape ? 0.0 : (1.0 - topInset / self.defaultTopInset)
self.controller?.updateModalStyleOverlayTransitionFactor(modalProgress, transition: transition.containedViewLayoutTransition)
let footerHeight = self.footerHeight
let convertedFooterFrame = self.view.convert(CGRect(origin: CGPoint(x: clipFrame.minX, y: clipFrame.maxY - footerHeight), size: CGSize(width: clipFrame.width, height: footerHeight)), to: self.containerView)
transition.setFrame(view: self.footerContainerView, frame: convertedFooterFrame)
self.updated(transition: transition)
}
private var boostState: InternalBoostState.DisplayData?
func updated(transition: Transition) {
guard let controller = self.controller, let layout = self.currentLayout else {
guard let controller = self.controller else {
return
}
let contentSize = self.contentView.update(
@ -1828,7 +1863,7 @@ public class PremiumBoostLevelsScreen: ViewController {
context: controller.context,
theme: self.presentationData.theme,
strings: self.presentationData.strings,
externalState: self.externalState,
externalState: self.containerExternalState,
peerId: controller.peerId,
mode: controller.mode,
status: controller.status,
@ -1867,7 +1902,10 @@ public class PremiumBoostLevelsScreen: ViewController {
},
openStats: controller.openStats,
openGift: controller.openGift,
openPeer: controller.openPeer
openPeer: controller.openPeer,
updated: { [weak self] in
self?.controller?.requestLayout(transition: .immediate)
}
)
),
environment: {},
@ -1875,14 +1913,13 @@ public class PremiumBoostLevelsScreen: ViewController {
)
self.contentView.frame = CGRect(origin: .zero, size: contentSize)
var footerHeight: CGFloat = 8.0 + 50.0
footerHeight += layout.intrinsicInsets.bottom > 0.0 ? layout.intrinsicInsets.bottom + 5.0 : 8.0
let footerHeight = self.footerHeight
let actionTitle: String
if self.currentMyBoostCount > 0 {
actionTitle = self.presentationData.strings.ChannelBoost_BoostAgain
} else {
actionTitle = self.externalState.isGroup ? self.presentationData.strings.GroupBoost_BoostGroup : self.presentationData.strings.ChannelBoost_BoostChannel
actionTitle = self.containerExternalState.isGroup ? self.presentationData.strings.GroupBoost_BoostGroup : self.presentationData.strings.ChannelBoost_BoostChannel
}
let footerSize = self.footerView.update(
@ -1924,6 +1961,15 @@ public class PremiumBoostLevelsScreen: ViewController {
}
}
private var footerHeight: CGFloat {
guard let layout = self.currentLayout else {
return 58.0
}
var footerHeight: CGFloat = 8.0 + 50.0
footerHeight += layout.intrinsicInsets.bottom > 0.0 ? layout.intrinsicInsets.bottom + 5.0 : 8.0
return footerHeight
}
private var defaultTopInset: CGFloat {
guard let layout = self.currentLayout else {
return 210.0
@ -1933,16 +1979,27 @@ public class PremiumBoostLevelsScreen: ViewController {
let bottomInset: CGFloat = layout.intrinsicInsets.bottom > 0.0 ? layout.intrinsicInsets.bottom + 5.0 : bottomPanelPadding
let panelHeight: CGFloat = bottomPanelPadding + 50.0 + bottomInset + 28.0
return layout.size.height - layout.size.width - 128.0 - panelHeight
var defaultTopInset = layout.size.height - layout.size.width - 128.0 - panelHeight
let containerTopInset = 10.0 + (layout.statusBarHeight ?? 0.0)
let contentHeight = self.containerExternalState.contentHeight
let footerHeight = self.footerHeight
if contentHeight > 0.0 {
let delta = (layout.size.height - defaultTopInset - containerTopInset) - contentHeight - footerHeight - 16.0
if delta > 0.0 {
defaultTopInset += delta
}
}
return defaultTopInset
} else {
return 210.0
}
}
private func findVerticalScrollView(view: UIView?) -> (UIScrollView, ListView?)? {
private func findVerticalScrollView(view: UIView?) -> UIScrollView? {
if let view = view {
if let view = view as? UIScrollView, view.contentSize.height > view.contentSize.width {
return (view, nil)
return view
}
return findVerticalScrollView(view: view.superview)
} else {
@ -1963,12 +2020,13 @@ public class PremiumBoostLevelsScreen: ViewController {
let point = recognizer.location(in: self.view)
let currentHitView = self.hitTest(point, with: nil)
var scrollViewAndListNode = self.findVerticalScrollView(view: currentHitView)
if scrollViewAndListNode?.0.frame.height == self.frame.width {
scrollViewAndListNode = nil
var scrollView = self.findVerticalScrollView(view: currentHitView)
if scrollView?.frame.height == self.frame.width {
scrollView = nil
}
if scrollView?.isDescendant(of: self.view) == false {
scrollView = nil
}
let scrollView = scrollViewAndListNode?.0
let listNode = scrollViewAndListNode?.1
let topInset: CGFloat
if self.isExpanded {
@ -1977,25 +2035,19 @@ public class PremiumBoostLevelsScreen: ViewController {
topInset = edgeTopInset
}
self.panGestureArguments = (topInset, 0.0, scrollView, listNode)
self.panGestureArguments = (topInset, 0.0, scrollView)
case .changed:
guard let (topInset, panOffset, scrollView, listNode) = self.panGestureArguments else {
guard let (topInset, panOffset, scrollView) = 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 {
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 {
@ -2012,7 +2064,7 @@ public class PremiumBoostLevelsScreen: ViewController {
translation = max(0.0, translation)
}
self.panGestureArguments = (topInset, translation, scrollView, listNode)
self.panGestureArguments = (topInset, translation, scrollView)
if !self.isExpanded {
if currentOffset > 0.0, let scrollView = scrollView {
@ -2031,23 +2083,18 @@ public class PremiumBoostLevelsScreen: ViewController {
self.containerLayoutUpdated(layout: layout, transition: .immediate)
case .ended:
guard let (currentTopInset, panOffset, scrollView, listNode) = self.panGestureArguments else {
guard let (currentTopInset, panOffset, scrollView) = 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 {
if contentOffset > 0.1 {
velocity = CGPoint()
}
}
@ -2072,9 +2119,7 @@ public class PremiumBoostLevelsScreen: ViewController {
} 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 {
if let scrollView = scrollView {
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false)
}
@ -2089,21 +2134,13 @@ public class PremiumBoostLevelsScreen: ViewController {
self.containerLayoutUpdated(layout: layout, transition: Transition(.animated(duration: 0.3, curve: .easeInOut)))
}
} else if scrollView != nil, (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, transition: Transition(transition))
} else {
if let listNode = listNode {
listNode.scroller.setContentOffset(CGPoint(), animated: false)
} else if let scrollView = scrollView {
if let scrollView = scrollView {
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: false)
}
@ -2205,11 +2242,15 @@ public class PremiumBoostLevelsScreen: ViewController {
var dismissReplaceImpl: (() -> Void)?
let replaceController = ReplaceBoostScreen(context: context, peerId: peerId, myBoostStatus: myBoostStatus, replaceBoosts: { slots in
var channelIds = Set<EnginePeer.Id>()
var sourcePeerIds = Set<EnginePeer.Id>()
var sourcePeers: [EnginePeer] = []
for boost in myBoostStatus.boosts {
if slots.contains(boost.slot) {
if let peer = boost.peer {
channelIds.insert(peer.id)
if !sourcePeerIds.contains(peer.id) {
sourcePeerIds.insert(peer.id)
sourcePeers.append(peer)
}
}
}
}
@ -2228,7 +2269,7 @@ public class PremiumBoostLevelsScreen: ViewController {
mode: mode,
status: status,
myBoostStatus: myBoostStatus,
replacedBoosts: (Int32(slots.count), Int32(channelIds.count)),
replacedBoosts: (Int32(slots.count), sourcePeers),
openStats: nil, openGift: nil, openPeer: openPeer, forceDark: forceDark
)
if let navigationController {
@ -2379,7 +2420,7 @@ public class PremiumBoostLevelsScreen: ViewController {
private let mode: Mode
private let status: ChannelBoostStatus?
private let myBoostStatus: MyBoostStatus?
private let replacedBoosts: (Int32, Int32)?
private let replacedBoosts: (Int32, [EnginePeer])?
private let openStats: (() -> Void)?
private let openGift: (() -> Void)?
private let openPeer: ((EnginePeer) -> Void)?
@ -2395,7 +2436,7 @@ public class PremiumBoostLevelsScreen: ViewController {
mode: Mode,
status: ChannelBoostStatus?,
myBoostStatus: MyBoostStatus? = nil,
replacedBoosts: (Int32, Int32)? = nil,
replacedBoosts: (Int32, [EnginePeer])? = nil,
openStats: (() -> Void)? = nil,
openGift: (() -> Void)? = nil,
openPeer: ((EnginePeer) -> Void)? = nil,
@ -2495,8 +2536,8 @@ private final class FooterComponent: Component {
}
final class View: UIView {
private let backgroundView: BlurredBackgroundView
private let separator = SimpleLayer()
let backgroundView: BlurredBackgroundView
let separator = SimpleLayer()
private let button = ComponentView<Empty>()

View File

@ -22,7 +22,12 @@ final class ScrollChildEnvironment: Equatable {
final class ScrollComponent<ChildEnvironment: Equatable>: Component {
typealias EnvironmentType = ChildEnvironment
class ExternalState {
var contentHeight: CGFloat = 0.0
}
let content: AnyComponent<(ChildEnvironment, ScrollChildEnvironment)>
let externalState: ExternalState?
let contentInsets: UIEdgeInsets
let contentOffsetUpdated: (_ top: CGFloat, _ bottom: CGFloat) -> Void
let contentOffsetWillCommit: (UnsafeMutablePointer<CGPoint>) -> Void
@ -30,12 +35,14 @@ final class ScrollComponent<ChildEnvironment: Equatable>: Component {
public init(
content: AnyComponent<(ChildEnvironment, ScrollChildEnvironment)>,
externalState: ExternalState? = nil,
contentInsets: UIEdgeInsets,
contentOffsetUpdated: @escaping (_ top: CGFloat, _ bottom: CGFloat) -> Void,
contentOffsetWillCommit: @escaping (UnsafeMutablePointer<CGPoint>) -> Void,
resetScroll: ActionSlot<Void> = ActionSlot()
) {
self.content = content
self.externalState = externalState
self.contentInsets = contentInsets
self.contentOffsetUpdated = contentOffsetUpdated
self.contentOffsetWillCommit = contentOffsetWillCommit
@ -121,6 +128,7 @@ final class ScrollComponent<ChildEnvironment: Equatable>: Component {
if self.scrollIndicatorInsets != component.contentInsets {
self.scrollIndicatorInsets = component.contentInsets
}
component.externalState?.contentHeight = contentSize.height
self.component = component

View File

@ -752,7 +752,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor)
var constrainedWidth = size.width / 2.0 - 56.0
var constrainedWidth = size.width - leftInset - rightInset - 32.0 - joinButtonSize.width - 60.0
if isScheduled {
constrainedWidth = size.width - 100.0
}