mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
d09c539298
commit
5659120fa9
@ -12466,3 +12466,9 @@ Sorry for the inconvenience.";
|
|||||||
"WebApp.MinimizedTitleFormat" = "%1$@ & %2$@";
|
"WebApp.MinimizedTitleFormat" = "%1$@ & %2$@";
|
||||||
"WebApp.MinimizedTitle.Others_1" = "%@ Other";
|
"WebApp.MinimizedTitle.Others_1" = "%@ Other";
|
||||||
"WebApp.MinimizedTitle.Others_any" = "%@ Others";
|
"WebApp.MinimizedTitle.Others_any" = "%@ Others";
|
||||||
|
|
||||||
|
"Stars.SendStars.Title" = "Send Stars";
|
||||||
|
"Stars.SendStars.AmountTitle" = "ENTER AMOUNT";
|
||||||
|
"Stars.SendStars.AmountPlaceholder" = "Stars Amount";
|
||||||
|
"Stars.SendStars.AmountInfo" = "Send %@ or more to highlight your profile in the TOP 3 supporters of this message.";
|
||||||
|
"Stars.SendStars.SendStars" = "Confirm and Send";
|
||||||
|
@ -153,6 +153,13 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||||||
open var minimizedContainer: MinimizedContainer? {
|
open var minimizedContainer: MinimizedContainer? {
|
||||||
didSet {
|
didSet {
|
||||||
self.minimizedContainer?.navigationController = self
|
self.minimizedContainer?.navigationController = self
|
||||||
|
self.minimizedContainer?.willMaximize = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.isMaximizing = true
|
||||||
|
self.updateContainersNonReentrant(transition: .animated(duration: 0.4, curve: .spring))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1576,7 +1583,6 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||||||
self.updateContainersNonReentrant(transition: .animated(duration: 0.4, curve: .spring))
|
self.updateContainersNonReentrant(transition: .animated(duration: 0.4, curve: .spring))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
self.minimizedContainer?.removeFromSupernode()
|
self.minimizedContainer?.removeFromSupernode()
|
||||||
self.minimizedContainer = minimizedContainer
|
self.minimizedContainer = minimizedContainer
|
||||||
|
|
||||||
|
@ -133,9 +133,10 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
private struct SelectionState: Equatable {
|
private struct SelectionState: Equatable {
|
||||||
let selected: Bool
|
let selected: Bool
|
||||||
|
let index: Int?
|
||||||
let count: Int
|
let count: Int
|
||||||
}
|
}
|
||||||
private let selectionPromise = ValuePromise<SelectionState>(SelectionState(selected: false, count: 0))
|
private let selectionPromise = ValuePromise<SelectionState>(SelectionState(selected: false, index: nil, count: 0))
|
||||||
private let spoilerDisposable = MetaDisposable()
|
private let spoilerDisposable = MetaDisposable()
|
||||||
var spoilerNode: SpoilerOverlayNode?
|
var spoilerNode: SpoilerOverlayNode?
|
||||||
var priceNode: PriceNode?
|
var priceNode: PriceNode?
|
||||||
@ -256,14 +257,16 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
if let interaction = self.interaction, let selectionState = interaction.selectionState {
|
if let interaction = self.interaction, let selectionState = interaction.selectionState {
|
||||||
let selected = selectionState.isIdentifierSelected(self.identifier)
|
let selected = selectionState.isIdentifierSelected(self.identifier)
|
||||||
|
var selectionIndex: Int?
|
||||||
if let selectableItem = self.selectableItem {
|
if let selectableItem = self.selectableItem {
|
||||||
let index = selectionState.index(of: selectableItem)
|
let index = selectionState.index(of: selectableItem)
|
||||||
if index != NSNotFound {
|
if index != NSNotFound {
|
||||||
self.checkNode?.content = .counter(Int(index))
|
self.checkNode?.content = .counter(Int(index))
|
||||||
|
selectionIndex = Int(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.checkNode?.setSelected(selected, animated: animated)
|
self.checkNode?.setSelected(selected, animated: animated)
|
||||||
self.selectionPromise.set(SelectionState(selected: selected, count: selectionState.selectedItems().count))
|
self.selectionPromise.set(SelectionState(selected: selected, index: selectionIndex, count: selectionState.selectedItems().count))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,7 +295,7 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
self.durationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
self.durationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
self.draftNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
self.draftNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
self.priceNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
self.priceNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
if animateSpoilerNode {
|
if animateSpoilerNode || self.priceNode != nil {
|
||||||
self.spoilerNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
self.spoilerNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -573,7 +576,7 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.updateHasSpoiler(hasSpoiler, price: selectionState.selected ? price : nil, isSingle: selectionState.count == 1)
|
strongSelf.updateHasSpoiler(hasSpoiler, price: selectionState.selected ? price : nil, isSingle: selectionState.count == 1 || selectionState.index == 1)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if self.currentDraftState != nil {
|
if self.currentDraftState != nil {
|
||||||
|
@ -185,7 +185,7 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
|
|||||||
self.didSetupSpoiler = true
|
self.didSetupSpoiler = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasSpoiler || price != nil {
|
if hasSpoiler {
|
||||||
if self.spoilerNode == nil {
|
if self.spoilerNode == nil {
|
||||||
let spoilerNode = SpoilerOverlayNode(enableAnimations: self.enableAnimations)
|
let spoilerNode = SpoilerOverlayNode(enableAnimations: self.enableAnimations)
|
||||||
self.insertSubnode(spoilerNode, aboveSubnode: self.imageNode)
|
self.insertSubnode(spoilerNode, aboveSubnode: self.imageNode)
|
||||||
@ -499,6 +499,8 @@ final class PriceNode: ASDisplayNode {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.isUserInteractionEnabled = false
|
||||||
|
|
||||||
self.addSubnode(self.backgroundNode)
|
self.addSubnode(self.backgroundNode)
|
||||||
self.backgroundNode.addSubnode(self.lockNode)
|
self.backgroundNode.addSubnode(self.lockNode)
|
||||||
self.backgroundNode.addSubnode(self.iconNode)
|
self.backgroundNode.addSubnode(self.iconNode)
|
||||||
@ -890,12 +892,25 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
|
|||||||
self.reorderFeedback = HapticFeedback()
|
self.reorderFeedback = HapticFeedback()
|
||||||
}
|
}
|
||||||
self.reorderFeedback?.impact()
|
self.reorderFeedback?.impact()
|
||||||
|
|
||||||
|
let priceTransition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .easeInOut)
|
||||||
|
for (_, node) in self.priceNodes {
|
||||||
|
priceTransition.updateAlpha(node: node, alpha: 0.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func endReordering(point: CGPoint?) {
|
private func endReordering(point: CGPoint?) {
|
||||||
if let reorderNode = self.reorderNode {
|
if let reorderNode = self.reorderNode {
|
||||||
self.reorderNode = nil
|
self.reorderNode = nil
|
||||||
|
|
||||||
|
let completion = {
|
||||||
|
let priceTransition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .easeInOut)
|
||||||
|
for (_, node) in self.priceNodes {
|
||||||
|
node.supernode?.view.bringSubviewToFront(node.view)
|
||||||
|
priceTransition.updateAlpha(node: node, alpha: 1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let itemNode = reorderNode.itemNode, let point = point {
|
if let itemNode = reorderNode.itemNode, let point = point {
|
||||||
var targetNode: MediaPickerSelectedItemNode?
|
var targetNode: MediaPickerSelectedItemNode?
|
||||||
for (_, node) in self.itemNodes {
|
for (_, node) in self.itemNodes {
|
||||||
@ -910,11 +925,13 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
|
|||||||
}
|
}
|
||||||
reorderNode.animateCompletion(completion: { [weak reorderNode] in
|
reorderNode.animateCompletion(completion: { [weak reorderNode] in
|
||||||
reorderNode?.removeFromSupernode()
|
reorderNode?.removeFromSupernode()
|
||||||
|
completion()
|
||||||
})
|
})
|
||||||
self.reorderFeedback?.tap()
|
self.reorderFeedback?.tap()
|
||||||
} else {
|
} else {
|
||||||
reorderNode.removeFromSupernode()
|
reorderNode.removeFromSupernode()
|
||||||
reorderNode.itemNode?.isHidden = false
|
reorderNode.itemNode?.isHidden = false
|
||||||
|
completion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ public struct CachedChannelFlags: OptionSet {
|
|||||||
public static let translationHidden = CachedChannelFlags(rawValue: 1 << 8)
|
public static let translationHidden = CachedChannelFlags(rawValue: 1 << 8)
|
||||||
public static let adsRestricted = CachedChannelFlags(rawValue: 1 << 9)
|
public static let adsRestricted = CachedChannelFlags(rawValue: 1 << 9)
|
||||||
public static let canViewRevenue = CachedChannelFlags(rawValue: 1 << 10)
|
public static let canViewRevenue = CachedChannelFlags(rawValue: 1 << 10)
|
||||||
|
public static let paidMediaAllowed = CachedChannelFlags(rawValue: 1 << 11)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct CachedChannelParticipantsSummary: PostboxCoding, Equatable {
|
public struct CachedChannelParticipantsSummary: PostboxCoding, Equatable {
|
||||||
|
@ -1096,6 +1096,34 @@ public extension TelegramEngine.EngineData.Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct PaidMediaAllowed: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
|
||||||
|
public typealias Result = Bool
|
||||||
|
|
||||||
|
fileprivate var id: EnginePeer.Id
|
||||||
|
public var mapKey: EnginePeer.Id {
|
||||||
|
return self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(id: EnginePeer.Id) {
|
||||||
|
self.id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
var key: PostboxViewKey {
|
||||||
|
return .cachedPeerData(peerId: self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extract(view: PostboxView) -> Result {
|
||||||
|
guard let view = view as? CachedPeerDataView else {
|
||||||
|
preconditionFailure()
|
||||||
|
}
|
||||||
|
if let cachedData = view.cachedPeerData as? CachedChannelData {
|
||||||
|
return cachedData.flags.contains(.paidMediaAllowed)
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct BoostsToUnrestrict: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
|
public struct BoostsToUnrestrict: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
|
||||||
public typealias Result = Int32?
|
public typealias Result = Int32?
|
||||||
|
|
||||||
|
@ -589,6 +589,9 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
|
|||||||
if (flags2 & Int32(1 << 12)) != 0 {
|
if (flags2 & Int32(1 << 12)) != 0 {
|
||||||
channelFlags.insert(.canViewRevenue)
|
channelFlags.insert(.canViewRevenue)
|
||||||
}
|
}
|
||||||
|
if (flags2 & Int32(1 << 14)) != 0 {
|
||||||
|
channelFlags.insert(.paidMediaAllowed)
|
||||||
|
}
|
||||||
|
|
||||||
let sendAsPeerId = defaultSendAs?.peerId
|
let sendAsPeerId = defaultSendAs?.peerId
|
||||||
|
|
||||||
|
@ -2948,7 +2948,13 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
contentSize.height += totalContentNodesHeight
|
contentSize.height += totalContentNodesHeight
|
||||||
|
|
||||||
if let paidContent = item.message.media.first(where: { $0 is TelegramMediaPaidContent }) as? TelegramMediaPaidContent, let media = paidContent.extendedMedia.first {
|
if let paidContent = item.message.media.first(where: { $0 is TelegramMediaPaidContent }) as? TelegramMediaPaidContent, let media = paidContent.extendedMedia.first {
|
||||||
|
var isLocked = false
|
||||||
if case .preview = media {
|
if case .preview = media {
|
||||||
|
isLocked = true
|
||||||
|
} else if item.presentationData.isPreview {
|
||||||
|
isLocked = true
|
||||||
|
}
|
||||||
|
if isLocked {
|
||||||
let sizeAndApply = unlockButtonLayout(ChatMessageUnlockMediaNode.Arguments(
|
let sizeAndApply = unlockButtonLayout(ChatMessageUnlockMediaNode.Arguments(
|
||||||
presentationData: item.presentationData,
|
presentationData: item.presentationData,
|
||||||
strings: item.presentationData.strings,
|
strings: item.presentationData.strings,
|
||||||
|
@ -1144,9 +1144,19 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
let thumbnailMedia = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [], immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
|
let thumbnailMedia = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [], immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
|
||||||
media = thumbnailMedia
|
media = thumbnailMedia
|
||||||
case let .full(fullMedia):
|
case let .full(fullMedia):
|
||||||
|
if presentationData.isPreview {
|
||||||
|
if let image = fullMedia as? TelegramMediaImage {
|
||||||
|
let thumbnailMedia = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [], immediateThumbnailData: image.immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
|
||||||
|
media = thumbnailMedia
|
||||||
|
} else if let video = fullMedia as? TelegramMediaFile {
|
||||||
|
let thumbnailMedia = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [], immediateThumbnailData: video.immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
|
||||||
|
media = thumbnailMedia
|
||||||
|
}
|
||||||
|
} else {
|
||||||
media = fullMedia
|
media = fullMedia
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let story = media as? TelegramMediaStory {
|
if let story = media as? TelegramMediaStory {
|
||||||
isStory = true
|
isStory = true
|
||||||
@ -1475,7 +1485,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
extendedMedia = paidContent.extendedMedia[selectedMediaIndex]
|
extendedMedia = paidContent.extendedMedia[selectedMediaIndex]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let extendedMedia, case let .full(fullMedia) = extendedMedia {
|
if let extendedMedia, case let .full(fullMedia) = extendedMedia, !presentationData.isPreview {
|
||||||
isExtendedMedia = true
|
isExtendedMedia = true
|
||||||
media = fullMedia
|
media = fullMedia
|
||||||
}
|
}
|
||||||
@ -2366,6 +2376,11 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
icon = .lock
|
icon = .lock
|
||||||
}
|
}
|
||||||
displaySpoiler = true
|
displaySpoiler = true
|
||||||
|
} else if let _ = extendedMedia, isPreview {
|
||||||
|
if let invoice, invoice.currency != "XTR" {
|
||||||
|
icon = .lock
|
||||||
|
}
|
||||||
|
displaySpoiler = true
|
||||||
} else if message.attributes.contains(where: { $0 is MediaSpoilerMessageAttribute }) {
|
} else if message.attributes.contains(where: { $0 is MediaSpoilerMessageAttribute }) {
|
||||||
displaySpoiler = true
|
displaySpoiler = true
|
||||||
} else if isSecretMedia {
|
} else if isSecretMedia {
|
||||||
|
@ -193,7 +193,7 @@ public final class DrawingMessageRenderer {
|
|||||||
mainRadius: presentationData.chatBubbleCorners.mainRadius,
|
mainRadius: presentationData.chatBubbleCorners.mainRadius,
|
||||||
auxiliaryRadius: presentationData.chatBubbleCorners.auxiliaryRadius,
|
auxiliaryRadius: presentationData.chatBubbleCorners.auxiliaryRadius,
|
||||||
mergeBubbleCorners: presentationData.chatBubbleCorners.mergeBubbleCorners,
|
mergeBubbleCorners: presentationData.chatBubbleCorners.mergeBubbleCorners,
|
||||||
hasTails: false
|
hasTails: !self.isLink
|
||||||
)
|
)
|
||||||
|
|
||||||
let avatarHeaderItem: ListViewItemHeader?
|
let avatarHeaderItem: ListViewItemHeader?
|
||||||
|
@ -716,7 +716,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
|||||||
itemFrame = effectiveItemFrame
|
itemFrame = effectiveItemFrame
|
||||||
itemTransform = effectiveItemTransform
|
itemTransform = effectiveItemTransform
|
||||||
|
|
||||||
itemNode.isCovered = index == self.items.count - 2
|
itemNode.isCovered = index <= self.items.count - 2
|
||||||
}
|
}
|
||||||
|
|
||||||
itemNode.bounds = CGRect(origin: .zero, size: itemFrame.size)
|
itemNode.bounds = CGRect(origin: .zero, size: itemFrame.size)
|
||||||
|
@ -643,7 +643,7 @@ public final class StarsImageComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var totalLabelWidth: CGFloat = 0.0
|
var totalLabelWidth: CGFloat = 0.0
|
||||||
let labelSpacing: CGFloat = 3.0
|
let labelSpacing: CGFloat = 4.0
|
||||||
let lockView: UIImageView
|
let lockView: UIImageView
|
||||||
if let current = self.lockView {
|
if let current = self.lockView {
|
||||||
lockView = current
|
lockView = current
|
||||||
|
@ -129,6 +129,14 @@ private final class SheetContent: CombinedComponent {
|
|||||||
|
|
||||||
minAmount = 1
|
minAmount = 1
|
||||||
maxAmount = configuration.maxPaidMediaAmount
|
maxAmount = configuration.maxPaidMediaAmount
|
||||||
|
case .reaction:
|
||||||
|
titleString = environment.strings.Stars_SendStars_Title
|
||||||
|
amountTitle = environment.strings.Stars_SendStars_AmountTitle
|
||||||
|
amountPlaceholder = environment.strings.Stars_SendStars_AmountPlaceholder
|
||||||
|
|
||||||
|
minAmount = 1
|
||||||
|
//TODO:
|
||||||
|
maxAmount = configuration.maxPaidMediaAmount
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = title.update(
|
let title = title.update(
|
||||||
@ -142,7 +150,16 @@ private final class SheetContent: CombinedComponent {
|
|||||||
contentSize.height += title.size.height
|
contentSize.height += title.size.height
|
||||||
contentSize.height += 40.0
|
contentSize.height += 40.0
|
||||||
|
|
||||||
if case let .withdraw(starsState) = component.mode {
|
let balance: Int64?
|
||||||
|
if case .reaction = component.mode {
|
||||||
|
balance = state.balance
|
||||||
|
} else if case let .withdraw(starsState) = component.mode {
|
||||||
|
balance = starsState.balances.availableBalance
|
||||||
|
} else {
|
||||||
|
balance = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let balance {
|
||||||
let balanceTitle = balanceTitle.update(
|
let balanceTitle = balanceTitle.update(
|
||||||
component: MultilineTextComponent(
|
component: MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
@ -158,7 +175,7 @@ private final class SheetContent: CombinedComponent {
|
|||||||
let balanceValue = balanceValue.update(
|
let balanceValue = balanceValue.update(
|
||||||
component: MultilineTextComponent(
|
component: MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: presentationStringsFormattedNumber(Int32(starsState.balances.availableBalance), environment.dateTimeFormat.groupingSeparator),
|
string: presentationStringsFormattedNumber(Int32(balance), environment.dateTimeFormat.groupingSeparator),
|
||||||
font: Font.semibold(16.0),
|
font: Font.semibold(16.0),
|
||||||
textColor: theme.list.itemPrimaryTextColor
|
textColor: theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -185,17 +202,18 @@ private final class SheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let amountFooter: AnyComponent<Empty>?
|
|
||||||
if case .paidMedia = component.mode {
|
|
||||||
let amountFont = Font.regular(13.0)
|
let amountFont = Font.regular(13.0)
|
||||||
let amountTextColor = theme.list.freeTextColor
|
let amountTextColor = theme.list.freeTextColor
|
||||||
let amountMarkdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: amountFont, textColor: amountTextColor), bold: MarkdownAttributeSet(font: amountFont, textColor: amountTextColor), link: MarkdownAttributeSet(font: amountFont, textColor: theme.list.itemAccentColor), linkAttribute: { contents in
|
let amountMarkdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: amountFont, textColor: amountTextColor), bold: MarkdownAttributeSet(font: amountFont, textColor: amountTextColor), link: MarkdownAttributeSet(font: amountFont, textColor: theme.list.itemAccentColor), linkAttribute: { contents in
|
||||||
return (TelegramTextAttributes.URL, contents)
|
return (TelegramTextAttributes.URL, contents)
|
||||||
})
|
})
|
||||||
let amountInfoString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.Stars_PaidContent_AmountInfo, attributes: amountMarkdownAttributes, textAlignment: .natural))
|
|
||||||
if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== environment.theme {
|
if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== environment.theme {
|
||||||
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Contact List/SubtitleArrow"), color: environment.theme.list.itemAccentColor)!, environment.theme)
|
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Contact List/SubtitleArrow"), color: environment.theme.list.itemAccentColor)!, environment.theme)
|
||||||
}
|
}
|
||||||
|
let amountFooter: AnyComponent<Empty>?
|
||||||
|
switch component.mode {
|
||||||
|
case .paidMedia:
|
||||||
|
let amountInfoString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.Stars_PaidContent_AmountInfo, attributes: amountMarkdownAttributes, textAlignment: .natural))
|
||||||
if let range = amountInfoString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 {
|
if let range = amountInfoString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 {
|
||||||
amountInfoString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: amountInfoString.string))
|
amountInfoString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: amountInfoString.string))
|
||||||
}
|
}
|
||||||
@ -214,7 +232,13 @@ private final class SheetContent: CombinedComponent {
|
|||||||
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Stars_PaidContent_AmountInfo_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Stars_PaidContent_AmountInfo_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
} else {
|
case let .reaction(starsToTop):
|
||||||
|
let amountInfoString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.Stars_SendStars_AmountInfo("\(starsToTop ?? 0)").string, attributes: amountMarkdownAttributes, textAlignment: .natural))
|
||||||
|
amountFooter = AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(amountInfoString),
|
||||||
|
maximumNumberOfLines: 0
|
||||||
|
))
|
||||||
|
default:
|
||||||
amountFooter = nil
|
amountFooter = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,7 +320,7 @@ private final class SheetContent: CombinedComponent {
|
|||||||
id: AnyHashable(0),
|
id: AnyHashable(0),
|
||||||
component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))
|
component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))
|
||||||
),
|
),
|
||||||
isEnabled: true,
|
isEnabled: (state.amount ?? 0) > 0,
|
||||||
displaysProgress: false,
|
displaysProgress: false,
|
||||||
action: { [weak state] in
|
action: { [weak state] in
|
||||||
if let controller = controller() as? StarsWithdrawScreen, let amount = state?.amount {
|
if let controller = controller() as? StarsWithdrawScreen, let amount = state?.amount {
|
||||||
@ -328,33 +352,56 @@ private final class SheetContent: CombinedComponent {
|
|||||||
|
|
||||||
final class State: ComponentState {
|
final class State: ComponentState {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
|
private let mode: StarsWithdrawScreen.Mode
|
||||||
|
|
||||||
fileprivate var amount: Int64?
|
fileprivate var amount: Int64?
|
||||||
|
|
||||||
|
fileprivate var balance: Int64?
|
||||||
|
private var stateDisposable: Disposable?
|
||||||
|
|
||||||
var cachedCloseImage: (UIImage, PresentationTheme)?
|
var cachedCloseImage: (UIImage, PresentationTheme)?
|
||||||
var cachedStarImage: (UIImage, PresentationTheme)?
|
var cachedStarImage: (UIImage, PresentationTheme)?
|
||||||
var cachedChevronImage: (UIImage, PresentationTheme)?
|
var cachedChevronImage: (UIImage, PresentationTheme)?
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
amount: Int64?
|
mode: StarsWithdrawScreen.Mode
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.amount = amount
|
self.mode = mode
|
||||||
|
|
||||||
super.init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeState() -> State {
|
|
||||||
var amount: Int64?
|
var amount: Int64?
|
||||||
switch self.mode {
|
switch mode {
|
||||||
case let .withdraw(stats):
|
case let .withdraw(stats):
|
||||||
amount = stats.balances.availableBalance
|
amount = stats.balances.availableBalance
|
||||||
case let .paidMedia(initialValue):
|
case let .paidMedia(initialValue):
|
||||||
amount = initialValue
|
amount = initialValue
|
||||||
|
case .reaction:
|
||||||
|
amount = nil
|
||||||
}
|
}
|
||||||
return State(context: self.context, amount: amount)
|
|
||||||
|
self.amount = amount
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
if case .reaction = self.mode, let starsContext = context.starsContext {
|
||||||
|
self.stateDisposable = (starsContext.state
|
||||||
|
|> deliverOnMainQueue).startStrict(next: { [weak self] state in
|
||||||
|
if let self, let balance = state?.balance {
|
||||||
|
self.balance = balance
|
||||||
|
self.updated()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.stateDisposable?.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeState() -> State {
|
||||||
|
return State(context: self.context, mode: self.mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,6 +496,7 @@ public final class StarsWithdrawScreen: ViewControllerComponentContainer {
|
|||||||
public enum Mode: Equatable {
|
public enum Mode: Equatable {
|
||||||
case withdraw(StarsRevenueStats)
|
case withdraw(StarsRevenueStats)
|
||||||
case paidMedia(Int64?)
|
case paidMedia(Int64?)
|
||||||
|
case reaction(Int64?)
|
||||||
}
|
}
|
||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
@ -638,12 +686,11 @@ private final class AmountFieldComponent: Component {
|
|||||||
|
|
||||||
if let component = self.component {
|
if let component = self.component {
|
||||||
let amount: Int64?
|
let amount: Int64?
|
||||||
if !newText.isEmpty, let value = Int64(newText) {
|
if !newText.isEmpty, let value = Int64(normalizeArabicNumeralString(newText, type: .western)) {
|
||||||
amount = value
|
amount = value
|
||||||
} else {
|
} else {
|
||||||
amount = nil
|
amount = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if let amount, let maxAmount = component.maxValue, amount > maxAmount {
|
if let amount, let maxAmount = component.maxValue, amount > maxAmount {
|
||||||
textField.text = "\(maxAmount)"
|
textField.text = "\(maxAmount)"
|
||||||
self.textChanged(self.textField)
|
self.textChanged(self.textField)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user