Various improvements

This commit is contained in:
Ilya Laktyushin 2024-11-12 02:15:21 +04:00
parent f4aaf08e8c
commit e7f6d60b73
9 changed files with 154 additions and 102 deletions

View File

@ -13223,3 +13223,6 @@ Sorry for the inconvenience.";
"Gift.Send.SendShort" = "Send"; "Gift.Send.SendShort" = "Send";
"Premium.EmojiStatus.Proceed" = "Unlock Emoji Statuses"; "Premium.EmojiStatus.Proceed" = "Unlock Emoji Statuses";
"Stars.Transaction.Subscription.CancelledByBot" = "Your subscription was cancelled by the bot.";
"Stars.Transaction.Subscription.CancelledByBusiness" = "Your subscription was cancelled by the business.";

View File

@ -568,6 +568,9 @@ private extension StarsContext.State.Subscription {
if (apiFlags & (1 << 2)) != 0 { if (apiFlags & (1 << 2)) != 0 {
flags.insert(.missingBalance) flags.insert(.missingBalance)
} }
if (apiFlags & (1 << 7)) != 0 {
flags.insert(.isCancelledByBot)
}
self.init(flags: flags, id: id, peer: EnginePeer(peer), untilDate: untilDate, pricing: StarsSubscriptionPricing(apiStarsSubscriptionPricing: pricing), inviteHash: inviteHash, title: title, photo: photo.flatMap(TelegramMediaWebFile.init), invoiceSlug: invoiceSlug) self.init(flags: flags, id: id, peer: EnginePeer(peer), untilDate: untilDate, pricing: StarsSubscriptionPricing(apiStarsSubscriptionPricing: pricing), inviteHash: inviteHash, title: title, photo: photo.flatMap(TelegramMediaWebFile.init), invoiceSlug: invoiceSlug)
} }
} }
@ -719,6 +722,7 @@ public final class StarsContext {
public static let isCancelled = Flags(rawValue: 1 << 0) public static let isCancelled = Flags(rawValue: 1 << 0)
public static let canRefulfill = Flags(rawValue: 1 << 1) public static let canRefulfill = Flags(rawValue: 1 << 1)
public static let missingBalance = Flags(rawValue: 1 << 2) public static let missingBalance = Flags(rawValue: 1 << 2)
public static let isCancelledByBot = Flags(rawValue: 1 << 3)
} }
public let flags: Flags public let flags: Flags

View File

@ -149,6 +149,8 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
messageWithCaptionToAdd = (message, itemAttributes) messageWithCaptionToAdd = (message, itemAttributes)
} }
result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media, neighborSpacing: .default))) result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media, neighborSpacing: .default)))
} else if let _ = media as? TelegramMediaWebFile {
result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media, neighborSpacing: .default)))
} else if let story = media as? TelegramMediaStory { } else if let story = media as? TelegramMediaStory {
if story.isMention { if story.isMention {
if let storyItem = message.associatedStories[story.storyId], storyItem.data.isEmpty { if let storyItem = message.associatedStories[story.storyId], storyItem.data.isEmpty {

View File

@ -1920,6 +1920,8 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
imageDimensions = dimensions.cgSize imageDimensions = dimensions.cgSize
} else if let image = media as? TelegramMediaWebFile, let dimensions = image.dimensions { } else if let image = media as? TelegramMediaWebFile, let dimensions = image.dimensions {
imageDimensions = dimensions.cgSize imageDimensions = dimensions.cgSize
} else if let file = media as? TelegramMediaWebFile, let dimensions = file.dimensions {
imageDimensions = dimensions.cgSize
} }
if let imageDimensions = imageDimensions { if let imageDimensions = imageDimensions {

View File

@ -183,8 +183,7 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
} else if let invoice = media as? TelegramMediaInvoice { } else if let invoice = media as? TelegramMediaInvoice {
selectedMedia = invoice selectedMedia = invoice
extendedMedia = invoice.extendedMedia extendedMedia = invoice.extendedMedia
} } else if let paidContent = media as? TelegramMediaPaidContent {
else if let paidContent = media as? TelegramMediaPaidContent {
selectedMedia = paidContent selectedMedia = paidContent
if case let .mosaic(_, _, index) = preparePosition, let index { if case let .mosaic(_, _, index) = preparePosition, let index {
extendedMedia = paidContent.extendedMedia[index] extendedMedia = paidContent.extendedMedia[index]
@ -192,6 +191,11 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
} else { } else {
extendedMedia = paidContent.extendedMedia.first extendedMedia = paidContent.extendedMedia.first
} }
} else if let webFile = media as? TelegramMediaWebFile {
selectedMedia = webFile
if item.presentationData.isPreview {
automaticDownload = .full
}
} }
} }
} }

View File

@ -15,18 +15,20 @@ import MultilineTextComponent
public final class StarsAvatarComponent: Component { public final class StarsAvatarComponent: Component {
let context: AccountContext let context: AccountContext
let theme: PresentationTheme let theme: PresentationTheme
let peer: StarsContext.State.Transaction.Peer let peer: StarsContext.State.Transaction.Peer?
let photo: TelegramMediaWebFile? let photo: TelegramMediaWebFile?
let media: [Media] let media: [Media]
let backgroundColor: UIColor let backgroundColor: UIColor
let size: CGSize?
public init(context: AccountContext, theme: PresentationTheme, peer: StarsContext.State.Transaction.Peer, photo: TelegramMediaWebFile?, media: [Media], backgroundColor: UIColor) { public init(context: AccountContext, theme: PresentationTheme, peer: StarsContext.State.Transaction.Peer?, photo: TelegramMediaWebFile?, media: [Media], backgroundColor: UIColor, size: CGSize? = nil) {
self.context = context self.context = context
self.theme = theme self.theme = theme
self.peer = peer self.peer = peer
self.photo = photo self.photo = photo
self.media = media self.media = media
self.backgroundColor = backgroundColor self.backgroundColor = backgroundColor
self.size = size
} }
public static func ==(lhs: StarsAvatarComponent, rhs: StarsAvatarComponent) -> Bool { public static func ==(lhs: StarsAvatarComponent, rhs: StarsAvatarComponent) -> Bool {
@ -48,6 +50,9 @@ public final class StarsAvatarComponent: Component {
if lhs.backgroundColor != rhs.backgroundColor { if lhs.backgroundColor != rhs.backgroundColor {
return false return false
} }
if lhs.size != rhs.size {
return false
}
return true return true
} }
@ -88,14 +93,13 @@ public final class StarsAvatarComponent: Component {
self.component = component self.component = component
self.state = state self.state = state
let size = CGSize(width: 40.0, height: 40.0) let size = component.size ?? CGSize(width: 40.0, height: 40.0)
var iconInset: CGFloat = 3.0 var iconInset: CGFloat = 3.0
var iconOffset: CGFloat = 0.0 var iconOffset: CGFloat = 0.0
var dimensions = size var dimensions = size
switch component.peer { var didSetup = false
case let .peer(peer):
if !component.media.isEmpty { if !component.media.isEmpty {
let imageNode: TransformImageNode let imageNode: TransformImageNode
var isFirstTime = false var isFirstTime = false
@ -180,6 +184,7 @@ public final class StarsAvatarComponent: Component {
self.backgroundView.isHidden = true self.backgroundView.isHidden = true
self.iconView.isHidden = true self.iconView.isHidden = true
self.avatarNode.isHidden = true self.avatarNode.isHidden = true
didSetup = true
} else if let photo = component.photo { } else if let photo = component.photo {
let imageNode: TransformImageNode let imageNode: TransformImageNode
if let current = self.imageNode { if let current = self.imageNode {
@ -195,12 +200,19 @@ public final class StarsAvatarComponent: Component {
} }
imageNode.frame = CGRect(origin: .zero, size: size) imageNode.frame = CGRect(origin: .zero, size: size)
imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(radius: 8.0), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets(), emptyColor: component.theme.list.mediaPlaceholderColor))() imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(radius: floor(size.width / 5.0)), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets(), emptyColor: component.theme.list.mediaPlaceholderColor))()
self.backgroundView.isHidden = true self.backgroundView.isHidden = true
self.iconView.isHidden = true self.iconView.isHidden = true
self.avatarNode.isHidden = true self.avatarNode.isHidden = true
} else { didSetup = true
}
switch component.peer {
case .none:
break
case let .peer(peer):
if !didSetup {
self.avatarNode.setPeer( self.avatarNode.setPeer(
context: component.context, context: component.context,
theme: component.theme, theme: component.theme,

View File

@ -226,6 +226,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
var isSubscriber = false var isSubscriber = false
var isSubscriptionFee = false var isSubscriptionFee = false
var isBotSubscription = false var isBotSubscription = false
var isBusinessSubscription = false
var isCancelled = false var isCancelled = false
var isReaction = false var isReaction = false
var giveawayMessageId: MessageId? var giveawayMessageId: MessageId?
@ -259,8 +260,12 @@ private final class StarsTransactionSheetContent: CombinedComponent {
transactionPeer = .peer(peer) transactionPeer = .peer(peer)
isSubscriber = true isSubscriber = true
case let .subscription(subscription): case let .subscription(subscription):
if case let .user(user) = subscription.peer, user.botInfo != nil { if case let .user(user) = subscription.peer {
if user.botInfo != nil {
isBotSubscription = true isBotSubscription = true
} else {
isBusinessSubscription = true
}
} }
if let title = subscription.title { if let title = subscription.title {
titleText = title titleText = title
@ -318,18 +323,30 @@ private final class StarsTransactionSheetContent: CombinedComponent {
} }
isCancelled = true isCancelled = true
} else { } else {
if subscription.flags.contains(.isCancelled) { if subscription.flags.contains(.isCancelledByBot) {
if case let .user(user) = subscription.peer, user.botInfo == nil {
statusText = strings.Stars_Transaction_Subscription_CancelledByBusiness
} else {
statusText = strings.Stars_Transaction_Subscription_CancelledByBot
}
statusIsDestructive = true
buttonText = strings.Common_OK
isCancelled = true
} else if subscription.flags.contains(.isCancelled) {
statusText = strings.Stars_Transaction_Subscription_Cancelled statusText = strings.Stars_Transaction_Subscription_Cancelled
statusIsDestructive = true statusIsDestructive = true
if date > Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) { if date > Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) {
buttonText = strings.Stars_Transaction_Subscription_Renew buttonText = strings.Stars_Transaction_Subscription_Renew
} else { } else {
if let _ = subscription.inviteHash, !isKicked { if let _ = subscription.invoiceSlug {
buttonText = strings.Stars_Transaction_Subscription_Renew
} else if let _ = subscription.inviteHash, !isKicked {
buttonText = strings.Stars_Transaction_Subscription_JoinAgainChannel buttonText = strings.Stars_Transaction_Subscription_JoinAgainChannel
} else { } else {
buttonText = strings.Common_OK buttonText = strings.Common_OK
} }
} }
isCancelled = true
} else { } else {
statusText = strings.Stars_Transaction_Subscription_Active(stringForMediumDate(timestamp: subscription.untilDate, strings: strings, dateTimeFormat: dateTimeFormat, withTime: false)).string statusText = strings.Stars_Transaction_Subscription_Active(stringForMediumDate(timestamp: subscription.untilDate, strings: strings, dateTimeFormat: dateTimeFormat, withTime: false)).string
cancelButtonText = strings.Stars_Transaction_Subscription_Cancel cancelButtonText = strings.Stars_Transaction_Subscription_Cancel
@ -710,6 +727,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
if isBotSubscription { if isBotSubscription {
//TODO:localize //TODO:localize
title = "Bot" title = "Bot"
} else if isBusinessSubscription {
title = "Business"
} else { } else {
title = strings.Stars_Transaction_Subscription_Subscription title = strings.Stars_Transaction_Subscription_Subscription
} }

View File

@ -708,10 +708,10 @@ final class StarsTransactionsScreenComponent: Component {
)) ))
)) ))
var nameGroupComponent: AnyComponent<Empty> var nameGroupComponent: AnyComponent<Empty>
if let _ = subscription.photo { if let photo = subscription.photo {
nameGroupComponent = AnyComponent( nameGroupComponent = AnyComponent(
HStack([ HStack([
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(RoundedRectangle(color: .lightGray, cornerRadius: 3.0, size: CGSize(width: 19.0, height: 19.0)))), AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(StarsAvatarComponent(context: component.context, theme: environment.theme, peer: nil, photo: photo, media: [], backgroundColor: .clear, size: CGSize(width: 19.0, height: 19.0)))),
AnyComponentWithIdentity(id: AnyHashable(1), component: nameComponent) AnyComponentWithIdentity(id: AnyHashable(1), component: nameComponent)
], spacing: 6.0) ], spacing: 6.0)
) )
@ -1055,6 +1055,10 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
let _ = self.context.engine.payments.fulfillStarsSubscription(peerId: context.account.peerId, subscriptionId: subscription.id).startStandalone() let _ = self.context.engine.payments.fulfillStarsSubscription(peerId: context.account.peerId, subscriptionId: subscription.id).startStandalone()
updated = true updated = true
} }
if let _ = subscription.inviteHash, !subscription.flags.contains(.isCancelledByBot) {
let _ = self.context.engine.payments.fulfillStarsSubscription(peerId: context.account.peerId, subscriptionId: subscription.id).startStandalone()
updated = true
}
if !updated { if !updated {
if subscription.flags.contains(.isCancelled) { if subscription.flags.contains(.isCancelled) {
self.subscriptionsContext.updateSubscription(id: subscription.id, cancel: false) self.subscriptionsContext.updateSubscription(id: subscription.id, cancel: false)
@ -1065,6 +1069,8 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
} else { } else {
if let inviteHash = subscription.inviteHash { if let inviteHash = subscription.inviteHash {
self.context.sharedContext.handleTextLinkAction(context: self.context, peerId: nil, navigateDisposable: self.navigateDisposable, controller: self, action: .tap, itemLink: .url(url: "https://t.me/+\(inviteHash)", concealed: false)) self.context.sharedContext.handleTextLinkAction(context: self.context, peerId: nil, navigateDisposable: self.navigateDisposable, controller: self, action: .tap, itemLink: .url(url: "https://t.me/+\(inviteHash)", concealed: false))
} else if let invoiceSlug = subscription.invoiceSlug {
self.context.sharedContext.handleTextLinkAction(context: self.context, peerId: nil, navigateDisposable: self.navigateDisposable, controller: self, action: .tap, itemLink: .url(url: "https://t.me/$\(invoiceSlug)", concealed: false))
} }
} }
}) })

View File

@ -100,7 +100,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: EnginePeer.Id?, n
let sourceLocation = InstantPageSourceLocation(userLocation: peerId.flatMap(MediaResourceUserLocation.peer) ?? .other, peerType: .group) let sourceLocation = InstantPageSourceLocation(userLocation: peerId.flatMap(MediaResourceUserLocation.peer) ?? .other, peerType: .group)
let browserController = context.sharedContext.makeInstantPageController(context: context, webPage: webPage, anchor: anchor, sourceLocation: sourceLocation) let browserController = context.sharedContext.makeInstantPageController(context: context, webPage: webPage, anchor: anchor, sourceLocation: sourceLocation)
(controller.navigationController as? NavigationController)?.pushViewController(browserController, animated: true) (controller.navigationController as? NavigationController)?.pushViewController(browserController, animated: true)
case .boost, .chatFolder, .join: case .boost, .chatFolder, .join, .invoice:
if let navigationController = controller.navigationController as? NavigationController { if let navigationController = controller.navigationController as? NavigationController {
openResolvedUrlImpl(result, context: context, urlContext: peerId.flatMap { .chat(peerId: $0, message: nil, updatedPresentationData: nil) } ?? .generic, navigationController: navigationController, forceExternal: false, forceUpdate: false, openPeer: { peer, navigateToPeer in openResolvedUrlImpl(result, context: context, urlContext: peerId.flatMap { .chat(peerId: $0, message: nil, updatedPresentationData: nil) } ?? .generic, navigationController: navigationController, forceExternal: false, forceUpdate: false, openPeer: { peer, navigateToPeer in
openResolvedPeerImpl(peer, navigateToPeer) openResolvedPeerImpl(peer, navigateToPeer)