mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Group boosts
This commit is contained in:
parent
43204554cd
commit
b4ca0637a6
@ -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";
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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>()
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user