Various fixes

This commit is contained in:
Ilya Laktyushin 2024-06-28 01:59:26 +04:00
parent d09c539298
commit 5659120fa9
13 changed files with 170 additions and 38 deletions

View File

@ -12466,3 +12466,9 @@ Sorry for the inconvenience.";
"WebApp.MinimizedTitleFormat" = "%1$@ & %2$@";
"WebApp.MinimizedTitle.Others_1" = "%@ Other";
"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";

View File

@ -153,6 +153,13 @@ open class NavigationController: UINavigationController, ContainableController,
open var minimizedContainer: MinimizedContainer? {
didSet {
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.minimizedContainer?.removeFromSupernode()
self.minimizedContainer = minimizedContainer

View File

@ -133,9 +133,10 @@ final class MediaPickerGridItemNode: GridItemNode {
private struct SelectionState: Equatable {
let selected: Bool
let index: 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()
var spoilerNode: SpoilerOverlayNode?
var priceNode: PriceNode?
@ -256,14 +257,16 @@ final class MediaPickerGridItemNode: GridItemNode {
if let interaction = self.interaction, let selectionState = interaction.selectionState {
let selected = selectionState.isIdentifierSelected(self.identifier)
var selectionIndex: Int?
if let selectableItem = self.selectableItem {
let index = selectionState.index(of: selectableItem)
if index != NSNotFound {
self.checkNode?.content = .counter(Int(index))
selectionIndex = Int(index)
}
}
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.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)
if animateSpoilerNode {
if animateSpoilerNode || self.priceNode != nil {
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 {
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 {

View File

@ -185,7 +185,7 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
self.didSetupSpoiler = true
}
if hasSpoiler || price != nil {
if hasSpoiler {
if self.spoilerNode == nil {
let spoilerNode = SpoilerOverlayNode(enableAnimations: self.enableAnimations)
self.insertSubnode(spoilerNode, aboveSubnode: self.imageNode)
@ -499,6 +499,8 @@ final class PriceNode: ASDisplayNode {
super.init()
self.isUserInteractionEnabled = false
self.addSubnode(self.backgroundNode)
self.backgroundNode.addSubnode(self.lockNode)
self.backgroundNode.addSubnode(self.iconNode)
@ -890,11 +892,24 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
self.reorderFeedback = HapticFeedback()
}
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?) {
if let reorderNode = self.reorderNode {
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 {
var targetNode: MediaPickerSelectedItemNode?
@ -910,11 +925,13 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
}
reorderNode.animateCompletion(completion: { [weak reorderNode] in
reorderNode?.removeFromSupernode()
completion()
})
self.reorderFeedback?.tap()
} else {
reorderNode.removeFromSupernode()
reorderNode.itemNode?.isHidden = false
completion()
}
}

View File

@ -22,6 +22,7 @@ public struct CachedChannelFlags: OptionSet {
public static let translationHidden = CachedChannelFlags(rawValue: 1 << 8)
public static let adsRestricted = CachedChannelFlags(rawValue: 1 << 9)
public static let canViewRevenue = CachedChannelFlags(rawValue: 1 << 10)
public static let paidMediaAllowed = CachedChannelFlags(rawValue: 1 << 11)
}
public struct CachedChannelParticipantsSummary: PostboxCoding, Equatable {

View File

@ -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 typealias Result = Int32?

View File

@ -589,6 +589,9 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
if (flags2 & Int32(1 << 12)) != 0 {
channelFlags.insert(.canViewRevenue)
}
if (flags2 & Int32(1 << 14)) != 0 {
channelFlags.insert(.paidMediaAllowed)
}
let sendAsPeerId = defaultSendAs?.peerId

View File

@ -2948,7 +2948,13 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
contentSize.height += totalContentNodesHeight
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 {
isLocked = true
} else if item.presentationData.isPreview {
isLocked = true
}
if isLocked {
let sizeAndApply = unlockButtonLayout(ChatMessageUnlockMediaNode.Arguments(
presentationData: item.presentationData,
strings: item.presentationData.strings,

View File

@ -1140,11 +1140,21 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
if let extendedMedia {
switch extendedMedia {
case let .preview(_, immediateThumbnailData, _):
let thumbnailMedia = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [], immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
media = thumbnailMedia
case let .full(fullMedia):
case let .preview(_, immediateThumbnailData, _):
let thumbnailMedia = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [], immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
media = thumbnailMedia
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
}
}
}
@ -1475,7 +1485,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
extendedMedia = paidContent.extendedMedia[selectedMediaIndex]
}
}
if let extendedMedia, case let .full(fullMedia) = extendedMedia {
if let extendedMedia, case let .full(fullMedia) = extendedMedia, !presentationData.isPreview {
isExtendedMedia = true
media = fullMedia
}
@ -2366,6 +2376,11 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
icon = .lock
}
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 }) {
displaySpoiler = true
} else if isSecretMedia {

View File

@ -193,7 +193,7 @@ public final class DrawingMessageRenderer {
mainRadius: presentationData.chatBubbleCorners.mainRadius,
auxiliaryRadius: presentationData.chatBubbleCorners.auxiliaryRadius,
mergeBubbleCorners: presentationData.chatBubbleCorners.mergeBubbleCorners,
hasTails: false
hasTails: !self.isLink
)
let avatarHeaderItem: ListViewItemHeader?

View File

@ -716,7 +716,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
itemFrame = effectiveItemFrame
itemTransform = effectiveItemTransform
itemNode.isCovered = index == self.items.count - 2
itemNode.isCovered = index <= self.items.count - 2
}
itemNode.bounds = CGRect(origin: .zero, size: itemFrame.size)

View File

@ -643,7 +643,7 @@ public final class StarsImageComponent: Component {
}
var totalLabelWidth: CGFloat = 0.0
let labelSpacing: CGFloat = 3.0
let labelSpacing: CGFloat = 4.0
let lockView: UIImageView
if let current = self.lockView {
lockView = current

View File

@ -129,6 +129,14 @@ private final class SheetContent: CombinedComponent {
minAmount = 1
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(
@ -142,7 +150,16 @@ private final class SheetContent: CombinedComponent {
contentSize.height += title.size.height
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(
component: MultilineTextComponent(
text: .plain(NSAttributedString(
@ -158,7 +175,7 @@ private final class SheetContent: CombinedComponent {
let balanceValue = balanceValue.update(
component: MultilineTextComponent(
text: .plain(NSAttributedString(
string: presentationStringsFormattedNumber(Int32(starsState.balances.availableBalance), environment.dateTimeFormat.groupingSeparator),
string: presentationStringsFormattedNumber(Int32(balance), environment.dateTimeFormat.groupingSeparator),
font: Font.semibold(16.0),
textColor: theme.list.itemPrimaryTextColor
)),
@ -185,17 +202,18 @@ private final class SheetContent: CombinedComponent {
)
}
let amountFont = Font.regular(13.0)
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
return (TelegramTextAttributes.URL, contents)
})
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)
}
let amountFooter: AnyComponent<Empty>?
if case .paidMedia = component.mode {
let amountFont = Font.regular(13.0)
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
return (TelegramTextAttributes.URL, contents)
})
switch component.mode {
case .paidMedia:
let amountInfoString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.Stars_PaidContent_AmountInfo, attributes: amountMarkdownAttributes, textAlignment: .natural))
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)
}
if let range = amountInfoString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 {
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: {})
}
))
} 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
}
@ -296,7 +320,7 @@ private final class SheetContent: CombinedComponent {
id: AnyHashable(0),
component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))
),
isEnabled: true,
isEnabled: (state.amount ?? 0) > 0,
displaysProgress: false,
action: { [weak state] in
if let controller = controller() as? StarsWithdrawScreen, let amount = state?.amount {
@ -328,33 +352,56 @@ private final class SheetContent: CombinedComponent {
final class State: ComponentState {
private let context: AccountContext
private let mode: StarsWithdrawScreen.Mode
fileprivate var amount: Int64?
fileprivate var balance: Int64?
private var stateDisposable: Disposable?
var cachedCloseImage: (UIImage, PresentationTheme)?
var cachedStarImage: (UIImage, PresentationTheme)?
var cachedChevronImage: (UIImage, PresentationTheme)?
init(
context: AccountContext,
amount: Int64?
mode: StarsWithdrawScreen.Mode
) {
self.context = context
self.mode = mode
var amount: Int64?
switch mode {
case let .withdraw(stats):
amount = stats.balances.availableBalance
case let .paidMedia(initialValue):
amount = initialValue
case .reaction:
amount = nil
}
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 {
var amount: Int64?
switch self.mode {
case let .withdraw(stats):
amount = stats.balances.availableBalance
case let .paidMedia(initialValue):
amount = initialValue
}
return State(context: self.context, amount: amount)
return State(context: self.context, mode: self.mode)
}
}
@ -449,6 +496,7 @@ public final class StarsWithdrawScreen: ViewControllerComponentContainer {
public enum Mode: Equatable {
case withdraw(StarsRevenueStats)
case paidMedia(Int64?)
case reaction(Int64?)
}
private let context: AccountContext
@ -638,12 +686,11 @@ private final class AmountFieldComponent: Component {
if let component = self.component {
let amount: Int64?
if !newText.isEmpty, let value = Int64(newText) {
if !newText.isEmpty, let value = Int64(normalizeArabicNumeralString(newText, type: .western)) {
amount = value
} else {
amount = nil
}
if let amount, let maxAmount = component.maxValue, amount > maxAmount {
textField.text = "\(maxAmount)"
self.textChanged(self.textField)