Various improvements

This commit is contained in:
Ilya Laktyushin
2025-11-15 01:14:50 +04:00
parent 1c3b749ede
commit 82addaa434
5 changed files with 100 additions and 69 deletions

View File

@@ -60,7 +60,7 @@ public final class GiftAuctionContext {
}
public struct MyState: Equatable {
public var isOutbid: Bool
public var isReturned: Bool
public var bidAmount: Int64?
public var bidDate: Int32?
public var minBidAmount: Int64?
@@ -132,7 +132,7 @@ public final class GiftAuctionContext {
private var currentVersion: Int32 {
var currentVersion: Int32 = 0
if case let .ongoing(version, _, _, _, _, _, _, _, _, _) = self.auctionState {
if case let .ongoing(version, _, _, _, _, _, _, _, _, _) = self.auctionState {
currentVersion = version
}
return currentVersion
@@ -180,7 +180,10 @@ public final class GiftAuctionContext {
}
func updateAuctionState(_ auctionState: GiftAuctionContext.State.AuctionState) {
self.auctionState = auctionState
if case let .ongoing(version, _, _, _, _, _, _, _, _, _) = auctionState, version < self.currentVersion {
} else {
self.auctionState = auctionState
}
self.pushState()
}
@@ -255,7 +258,7 @@ extension GiftAuctionContext.State.MyState {
init(apiAuctionUserState: Api.StarGiftAuctionUserState) {
switch apiAuctionUserState {
case let .starGiftAuctionUserState(flags, bidAmount, bidDate, minBidAmount, bidPeerId, acquiredCount):
self.isOutbid = (flags & (1 << 1)) != 0
self.isReturned = (flags & (1 << 1)) != 0
self.bidAmount = bidAmount
self.bidDate = bidDate
self.minBidAmount = minBidAmount

View File

@@ -362,13 +362,13 @@ private final class BadgeComponent: Component {
private final class PeerPlaceComponent: Component {
let theme: PresentationTheme
let color: UIColor
let place: Int32
let place: Int32?
let groupingSeparator: String
init(
theme: PresentationTheme,
color: UIColor,
place: Int32,
place: Int32?,
groupingSeparator: String
) {
self.theme = theme
@@ -414,9 +414,9 @@ private final class PeerPlaceComponent: Component {
let textColor: UIColor
let backgroundColors: [UIColor]?
if component.place < 4 {
if let place = component.place, place < 4 {
textColor = .white
switch component.place {
switch place {
case 1:
backgroundColors = [UIColor(rgb: 0xffa901), UIColor(rgb: 0xffcd3b)]
case 2:
@@ -446,11 +446,21 @@ private final class PeerPlaceComponent: Component {
let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - backgroundSize.width) * 0.5), y: floorToScreenPixels((availableSize.height - backgroundSize.height) * 0.5)), size: backgroundSize)
self.background.frame = backgroundFrame
var placeString: String
if let place = component.place {
placeString = presentationStringsFormattedNumber(place, component.groupingSeparator)
if place >= 100 {
placeString = "\(placeString)+"
}
} else {
placeString = ""
}
let labelSize = self.label.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: presentationStringsFormattedNumber(component.place, component.groupingSeparator), font: Font.regular(17.0), textColor: textColor)))),
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: placeString, font: Font.with(size: 17.0, traits: .monospacedNumbers), textColor: textColor)))),
environment: {},
containerSize: CGSize(width: 40.0, height: 40.0)
containerSize: CGSize(width: 60.0, height: 40.0)
)
let labelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - labelSize.width) * 0.5), y: floorToScreenPixels((availableSize.height - labelSize.height) * 0.5)), size: labelSize)
if let labelView = self.label.view {
@@ -477,6 +487,7 @@ private final class PeerComponent: Component {
enum Status {
case winning
case outbid
case returned
}
let context: AccountContext
@@ -596,7 +607,7 @@ private final class PeerComponent: Component {
switch component.status {
case .winning:
color = component.theme.list.itemDisclosureActions.constructive.fillColor
case .outbid:
case .outbid, .returned:
color = component.theme.list.itemDestructiveColor
default:
break
@@ -606,7 +617,7 @@ private final class PeerComponent: Component {
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let placeSize = self.place.update(
transition: .immediate,
component: AnyComponent(PeerPlaceComponent(theme: component.theme, color: color, place: component.place, groupingSeparator: presentationData.dateTimeFormat.groupingSeparator)),
component: AnyComponent(PeerPlaceComponent(theme: component.theme, color: color, place: component.status == .returned ? nil : component.place, groupingSeparator: presentationData.dateTimeFormat.groupingSeparator)),
environment: {},
containerSize: CGSize(width: 40.0, height: 40.0)
)
@@ -1309,17 +1320,10 @@ private final class GiftAuctionBidScreenComponent: Component {
}
}
private var lastAcquiredGiftsReloadTime: Double?
private func reloadAcquiredGifts() {
private func loadAcquiredGifts() {
guard let component = self.component, case let .generic(gift) = component.gift else {
return
}
let currentTime = CFAbsoluteTimeGetCurrent()
if let lastAcquiredGiftsReloadTime = self.lastAcquiredGiftsReloadTime, currentTime < lastAcquiredGiftsReloadTime + 5 {
return
}
self.lastAcquiredGiftsReloadTime = currentTime
self.giftAuctionAcquiredGiftsDisposable.set((component.context.engine.payments.getGiftAuctionAcquiredGifts(giftId: gift.id)
|> deliverOnMainQueue).startStrict(next: { [weak self] acquiredGifts in
guard let self else {
@@ -1557,14 +1561,7 @@ private final class GiftAuctionBidScreenComponent: Component {
let signal = BotCheckoutController.InputData.fetch(context: component.context, source: source)
|> `catch` { error -> Signal<BotCheckoutController.InputData, SendBotPaymentFormError> in
switch error {
case .disallowedStarGifts:
return .fail(.disallowedStarGift)
case .starGiftsUserLimit:
return .fail(.starGiftUserLimit)
default:
return .fail(.generic)
}
return .fail(.generic)
}
|> mapToSignal { inputData -> Signal<SendBotPaymentResult, SendBotPaymentFormError> in
return component.context.engine.payments.sendStarsPaymentForm(formId: inputData.form.id, source: source)
@@ -1612,6 +1609,18 @@ private final class GiftAuctionBidScreenComponent: Component {
)
component.context.starsContext?.load(force: true)
}, error: { [weak self] _ in
guard let self else {
return
}
self.component?.context.starsContext?.load(force: true)
self.resetSliderValue()
Queue.mainQueue().after(0.1) {
self.isLoading = false
self.state?.updated()
}
})
}
@@ -1926,13 +1935,15 @@ private final class GiftAuctionBidScreenComponent: Component {
}
let context = component.context
let auctionContext = component.auctionContext
self.giftAuctionDisposable = (component.auctionContext.state
|> deliverOnMainQueue).start(next: { [weak self] state in
|> deliverOnMainQueue).start(next: { [weak self] auctionState in
guard let self else {
return
}
let isFirstTime = self.giftAuctionState == nil
self.giftAuctionState = state
let previousState = self.giftAuctionState
self.giftAuctionState = auctionState
var peerIds: [EnginePeer.Id] = []
var transition = ComponentTransition.spring(duration: 0.4)
@@ -1964,6 +1975,18 @@ private final class GiftAuctionBidScreenComponent: Component {
})
}
self.state?.updated(transition: transition)
if let acquiredCount = auctionState?.myState.acquiredCount, acquiredCount > (previousState?.myState.acquiredCount ?? 0) {
self.loadAcquiredGifts()
}
if case .finished = auctionState?.auctionState, let controller = self.environment?.controller() {
if let navigationController = controller.navigationController as? NavigationController {
controller.dismiss()
let auctionController = context.sharedContext.makeGiftAuctionViewScreen(context: context, auctionContext: auctionContext, completion: {})
navigationController.pushViewController(auctionController)
}
}
})
self.giftAuctionTimer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
@@ -1971,10 +1994,6 @@ private final class GiftAuctionBidScreenComponent: Component {
self?.state?.updated()
}, queue: Queue.mainQueue())
self.giftAuctionTimer?.start()
Queue.mainQueue().justDispatch {
self.reloadAcquiredGifts()
}
}
self.component = component
@@ -2224,7 +2243,7 @@ private final class GiftAuctionBidScreenComponent: Component {
myBidDate = currentDate
isBiddingUp = false
}
place = giftAuctionState.getPlace(myBid: myBidAmount, myBidDate: myBidDate) ?? 1
var bidTitle: String
@@ -2233,6 +2252,10 @@ private final class GiftAuctionBidScreenComponent: Component {
if isBiddingUp {
bidTitleColor = environment.theme.list.itemSecondaryTextColor
bidTitle = environment.strings.Gift_AuctionBid_BidPreview
} else if giftAuctionState.myState.isReturned {
bidTitle = environment.strings.Gift_AuctionBid_Outbid
bidTitleColor = environment.theme.list.itemDestructiveColor
bidStatus = .returned
} else if place > giftsPerRound {
bidTitle = environment.strings.Gift_AuctionBid_Outbid
bidTitleColor = environment.theme.list.itemDestructiveColor
@@ -2351,12 +2374,6 @@ private final class GiftAuctionBidScreenComponent: Component {
let minutes = Int(dropTimeout / 60)
let seconds = Int(dropTimeout % 60)
if dropTimeout == 0, place <= giftsPerRound {
Queue.mainQueue().after(1.5, {
self.reloadAcquiredGifts()
})
}
untilNextDropAnimatedItems.append(AnimatedTextComponent.Item(id: "m", content: .number(minutes, minDigits: 2)))
untilNextDropAnimatedItems.append(AnimatedTextComponent.Item(id: "colon", content: .text(":")))
untilNextDropAnimatedItems.append(AnimatedTextComponent.Item(id: "s", content: .number(seconds, minDigits: 2)))
@@ -2423,7 +2440,8 @@ private final class GiftAuctionBidScreenComponent: Component {
contentHeight += perkHeight
contentHeight += 24.0
if let giftAuctionAcquiredGifts = self.giftAuctionAcquiredGifts, giftAuctionAcquiredGifts.count > 0, case let .generic(gift) = component.gift {
let acquiredGiftsCount = self.giftAuctionState?.myState.acquiredCount ?? 0
if acquiredGiftsCount > 0, case let .generic(gift) = component.gift {
var myGiftsTransition = transition
let myGiftsSize = self.myGifts.update(
transition: .immediate,
@@ -2431,7 +2449,7 @@ private final class GiftAuctionBidScreenComponent: Component {
PlainButtonComponent(content: AnyComponent(
HStack([
AnyComponentWithIdentity(id: "count", component: AnyComponent(
MultilineTextComponent(text: .plain(NSAttributedString(string: presentationStringsFormattedNumber(Int32(giftAuctionAcquiredGifts.count), environment.dateTimeFormat.groupingSeparator), font: Font.regular(17.0), textColor: environment.theme.actionSheet.controlAccentColor)))
MultilineTextComponent(text: .plain(NSAttributedString(string: presentationStringsFormattedNumber(Int32(acquiredGiftsCount), environment.dateTimeFormat.groupingSeparator), font: Font.regular(17.0), textColor: environment.theme.actionSheet.controlAccentColor)))
)),
AnyComponentWithIdentity(id: "spacing", component: AnyComponent(
Rectangle(color: .clear, width: 8.0, height: 1.0)
@@ -2447,7 +2465,7 @@ private final class GiftAuctionBidScreenComponent: Component {
)
)),
AnyComponentWithIdentity(id: "text", component: AnyComponent(
MultilineTextComponent(text: .plain(NSAttributedString(string: " \(environment.strings.Gift_Auction_ItemsBought(Int32(giftAuctionAcquiredGifts.count)))", font: Font.regular(17.0), textColor: environment.theme.actionSheet.controlAccentColor)))
MultilineTextComponent(text: .plain(NSAttributedString(string: " \(environment.strings.Gift_Auction_ItemsBought(Int32(acquiredGiftsCount)))", font: Font.regular(17.0), textColor: environment.theme.actionSheet.controlAccentColor)))
)),
AnyComponentWithIdentity(id: "arrow", component: AnyComponent(
BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: environment.theme.actionSheet.controlAccentColor)

View File

@@ -272,7 +272,9 @@ private final class GiftAuctionCustomBidAlertContentNode: AlertContentNode {
if !hadValidLayout {
if let amountFieldView = self.amountField.view as? AmountFieldComponent.View {
amountFieldView.activateInput()
amountFieldView.selectAll()
Queue.mainQueue().justDispatch {
amountFieldView.selectAll()
}
}
}

View File

@@ -65,11 +65,11 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
private let getController: () -> ViewController?
private var disposable: Disposable?
private(set) var auctionState: GiftAuctionContext.State?
private(set) var giftAuctionState: GiftAuctionContext.State?
private var giftAuctionTimer: SwiftSignalKit.Timer?
fileprivate var giftAuctionAcquiredGifts: [GiftAuctionAcquiredGift] = []
private var giftAuctionAcquiredGiftsDisposable: Disposable?
private var giftAuctionAcquiredGiftsDisposable = MetaDisposable()
var cachedStarImage: (UIImage, PresentationTheme)?
@@ -90,23 +90,19 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
super.init()
self.disposable = (auctionContext.state
|> deliverOnMainQueue).start(next: { [weak self] state in
|> deliverOnMainQueue).start(next: { [weak self] auctionState in
guard let self else {
return
}
self.auctionState = state
let previousState = self.giftAuctionState
self.giftAuctionState = auctionState
self.updated()
})
self.giftAuctionAcquiredGiftsDisposable = (context.engine.payments.getGiftAuctionAcquiredGifts(giftId: auctionContext.gift.giftId)
|> deliverOnMainQueue).startStrict(next: { [weak self] acquiredGifts in
guard let self else {
return
if let acquiredCount = auctionState?.myState.acquiredCount, acquiredCount > (previousState?.myState.acquiredCount ?? 0) {
self.loadAcquiredGifts()
}
self.giftAuctionAcquiredGifts = acquiredGifts
self.updated(transition: .easeInOut(duration: 0.25))
})
self.giftAuctionTimer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
self?.updated()
}, queue: Queue.mainQueue())
@@ -115,10 +111,21 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
deinit {
self.disposable?.dispose()
self.giftAuctionAcquiredGiftsDisposable?.dispose()
self.giftAuctionAcquiredGiftsDisposable.dispose()
self.giftAuctionTimer?.invalidate()
}
func loadAcquiredGifts() {
self.giftAuctionAcquiredGiftsDisposable.set((self.context.engine.payments.getGiftAuctionAcquiredGifts(giftId: self.auctionContext.gift.giftId)
|> deliverOnMainQueue).startStrict(next: { [weak self] acquiredGifts in
guard let self else {
return
}
self.giftAuctionAcquiredGifts = acquiredGifts
self.updated(transition: .easeInOut(duration: 0.25))
}))
}
func showAttributeInfo(tag: Any, text: String) {
guard let controller = self.getController() as? GiftAuctionViewScreen else {
return
@@ -163,9 +170,6 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
self.dismiss(animated: true)
controller.completion()
// let bidController = self.context.sharedContext.makeGiftAuctionBidScreen(context: self.context, toPeerId: self.auctionContext.currentBidPeerId ?? self.toPeerId, auctionContext: self.auctionContext)
// navigationController.pushViewController(bidController)
}
func openPeer(_ peer: EnginePeer, dismiss: Bool = true) {
@@ -302,7 +306,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
var items: [ContextMenuItem] = []
if let auctionState = self.auctionState, case .ongoing = auctionState.auctionState {
if let auctionState = self.giftAuctionState, case .ongoing = auctionState.auctionState {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_Auction_Context_About, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor) }, action: { [weak controller] c, f in
f(.default)
@@ -445,7 +449,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
var isEnded = false
var tableItems: [TableComponent.Item] = []
if let auctionState = state.auctionState, case let .generic(gift) = component.auctionContext.gift {
if let auctionState = state.giftAuctionState, case let .generic(gift) = component.auctionContext.gift {
endTime = auctionState.endDate
if case .finished = auctionState.auctionState {
isEnded = true
@@ -637,14 +641,16 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
originY += table.size.height + 26.0
var hasAdditionalButtons = false
if state.giftAuctionAcquiredGifts.count > 0, case let .generic(gift) = component.auctionContext.gift {
let acquiredGiftsCount = state.giftAuctionState?.myState.acquiredCount ?? 0
if acquiredGiftsCount > 0, case let .generic(gift) = component.auctionContext.gift {
originY += 5.0
let acquiredButton = acquiredButton.update(
component: PlainButtonComponent(content: AnyComponent(
HStack([
AnyComponentWithIdentity(id: "count", component: AnyComponent(
MultilineTextComponent(text: .plain(NSAttributedString(string: presentationStringsFormattedNumber(Int32(state.giftAuctionAcquiredGifts.count), dateTimeFormat.groupingSeparator), font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
MultilineTextComponent(text: .plain(NSAttributedString(string: presentationStringsFormattedNumber(Int32(acquiredGiftsCount), dateTimeFormat.groupingSeparator), font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
)),
AnyComponentWithIdentity(id: "spacing", component: AnyComponent(
Rectangle(color: .clear, width: 8.0, height: 1.0)
@@ -660,7 +666,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
)
)),
AnyComponentWithIdentity(id: "text", component: AnyComponent(
MultilineTextComponent(text: .plain(NSAttributedString(string: " \(strings.Gift_Auction_ItemsBought(Int32(state.giftAuctionAcquiredGifts.count)))", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
MultilineTextComponent(text: .plain(NSAttributedString(string: " \(strings.Gift_Auction_ItemsBought(Int32(acquiredGiftsCount)))", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
)),
AnyComponentWithIdentity(id: "arrow", component: AnyComponent(
BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: theme.actionSheet.controlAccentColor)

View File

@@ -143,8 +143,10 @@ public final class SliderComponent: Component {
public func cancelGestures() {
if let sliderView = self.sliderView, let gestureRecognizers = sliderView.gestureRecognizers {
for gestureRecognizer in gestureRecognizers {
gestureRecognizer.isEnabled = false
gestureRecognizer.isEnabled = true
if gestureRecognizer.isEnabled {
gestureRecognizer.isEnabled = false
gestureRecognizer.isEnabled = true
}
}
}
}