Merge commit '0ae2f9b53a2b0c1d369492dfb9c2bb36f6ea6e8d' into beta

This commit is contained in:
Isaac 2025-05-14 01:02:21 +08:00
commit a47dd6ff9e
11 changed files with 115 additions and 58 deletions
submodules
MediaPickerUI/Sources
PremiumUI/Sources
TelegramCore/Sources
Network
TelegramEngine/Payments
TelegramUI
Components
CameraScreen/Sources
Gifts
PeerInfo/PeerInfoScreen/Sources
Stars/StarsWithdrawalScreen/Sources
Sources

@ -528,29 +528,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
if case let .assets(_, mode) = controller.subject, [.wallpaper, .story, .addImage, .cover, .createSticker, .createAvatar].contains(mode) {
} else {
let selectionGesture = MediaPickerGridSelectionGesture<TGMediaSelectableItem>()
selectionGesture.delegate = self.wrappedGestureRecognizerDelegate
selectionGesture.began = { [weak self] in
self?.controller?.cancelPanGesture()
}
selectionGesture.updateIsScrollEnabled = { [weak self] isEnabled in
self?.gridNode.scrollView.isScrollEnabled = isEnabled
}
selectionGesture.itemAt = { [weak self] point in
if let self, let itemNode = self.gridNode.itemNodeAtPoint(point) as? MediaPickerGridItemNode, let selectableItem = itemNode.selectableItem {
return (selectableItem, self.controller?.interaction?.selectionState?.isIdentifierSelected(selectableItem.uniqueIdentifier) ?? false)
} else {
return nil
}
}
selectionGesture.updateSelection = { [weak self] asset, selected in
if let strongSelf = self {
strongSelf.controller?.interaction?.selectionState?.setItem(asset, selected: selected, animated: true, sender: nil)
}
}
selectionGesture.sideInset = 44.0
self.gridNode.view.addGestureRecognizer(selectionGesture)
self.selectionGesture = selectionGesture
self.setupSelectionGesture()
}
if let controller = self.controller, case let .assets(collection, _) = controller.subject, collection != nil {
@ -713,6 +691,35 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
}
}
func setupSelectionGesture() {
guard self.selectionGesture == nil else {
return
}
let selectionGesture = MediaPickerGridSelectionGesture<TGMediaSelectableItem>()
selectionGesture.delegate = self.wrappedGestureRecognizerDelegate
selectionGesture.began = { [weak self] in
self?.controller?.cancelPanGesture()
}
selectionGesture.updateIsScrollEnabled = { [weak self] isEnabled in
self?.gridNode.scrollView.isScrollEnabled = isEnabled
}
selectionGesture.itemAt = { [weak self] point in
if let self, let itemNode = self.gridNode.itemNodeAtPoint(point) as? MediaPickerGridItemNode, let selectableItem = itemNode.selectableItem {
return (selectableItem, self.controller?.interaction?.selectionState?.isIdentifierSelected(selectableItem.uniqueIdentifier) ?? false)
} else {
return nil
}
}
selectionGesture.updateSelection = { [weak self] asset, selected in
if let strongSelf = self {
strongSelf.controller?.interaction?.selectionState?.setItem(asset, selected: selected, animated: true, sender: nil)
}
}
selectionGesture.sideInset = 44.0
self.gridNode.view.addGestureRecognizer(selectionGesture)
self.selectionGesture = selectionGesture
}
@objc private func cameraTapped() {
guard let camera = self.modernCamera, let previewView = self.modernCameraView else {
return
@ -2352,9 +2359,6 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
let transition = ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut)
var moreIsVisible = false
if case let .assets(_, mode) = self.subject, [.story, .createSticker].contains(mode) {
if count == 1 {
self.requestAttachmentMenuExpansion()
}
moreIsVisible = true
} else if case let .media(media) = self.subject {
self.titleView.title = media.count == 1 ? self.presentationData.strings.Attachment_Pasteboard : self.presentationData.strings.Attachment_SelectedMedia(count)
@ -2618,6 +2622,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
self.navigationItem.setRightBarButton(nil, animated: true)
self.explicitMultipleSelection = true
self.controllerNode.setupSelectionGesture()
self.requestAttachmentMenuExpansion()
if let state = self.controllerNode.state {
self.controllerNode.updateState(state)

@ -1108,6 +1108,10 @@ private final class SheetContent: CombinedComponent {
func layoutLevel(_ level: Int32) {
var perks: [LevelSectionComponent.Perk] = []
if !isGroup && level >= requiredBoostSubjectLevel(subject: .autoTranslate, group: isGroup, context: component.context, configuration: premiumConfiguration) {
perks.append(.autoTranslate)
}
perks.append(.story(level))
if !isGroup {
@ -1171,12 +1175,6 @@ private final class SheetContent: CombinedComponent {
if !isGroup && level >= requiredBoostSubjectLevel(subject: .noAds, group: isGroup, context: component.context, configuration: premiumConfiguration) {
perks.append(.noAds)
}
if !isGroup && level >= requiredBoostSubjectLevel(subject: .autoTranslate, group: isGroup, context: component.context, configuration: premiumConfiguration) {
perks.append(.autoTranslate)
}
// if !isGroup && level >= requiredBoostSubjectLevel(subject: .wearGift, group: isGroup, context: component.context, configuration: premiumConfiguration) {
// perks.append(.wearGift)
// }
levelItems.append(
AnyComponentWithIdentity(

@ -103,6 +103,10 @@ private final class FetchImpl {
init(range: Range<Int64>) {
self.range = range
}
deinit {
self.disposable?.dispose()
}
}
private final class HashRangeData {

@ -2494,7 +2494,7 @@ private final class ResaleGiftsContextImpl {
let filterAttributes = self.filterAttributes
let currentAttributesHash = self.attributesHash
let dataState = self.dataState
let dataState = self.dataState
if case let .ready(true, initialNextOffset) = dataState {
self.dataState = .loading

@ -2553,6 +2553,8 @@ public class CameraScreenImpl: ViewController, CameraScreen {
transitionCircleLayer.animateScale(from: sourceLocalFrame.width / 320.0, to: 6.0, duration: 0.6, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
self.view.mask = nil
colorFillView.removeFromSuperview()
self.requestUpdateLayout(hasAppeared: true, transition: .immediate)
})
} else {
if case .story = controller.mode {

@ -95,7 +95,8 @@ final class GiftStoreScreenComponent: Component {
private var starsStateDisposable: Disposable?
private var starsState: StarsContext.State?
private var initialCount: Int?
private var initialCount: Int32?
private var showLoading = true
private var component: GiftStoreScreenComponent?
private(set) weak var state: State?
@ -338,7 +339,9 @@ final class GiftStoreScreenComponent: Component {
guard let self else {
return
}
self.showLoading = true
self.state?.starGiftsContext.updateFilterAttributes([])
self.scrollToTop()
},
animateScale: false
)
@ -357,7 +360,7 @@ final class GiftStoreScreenComponent: Component {
var emptyResultsActionFrame = CGRect(
origin: CGPoint(
x: floorToScreenPixels((availableWidth - emptyResultsActionSize.width) / 2.0),
y: max(self.scrollView.contentSize.height - 8.0, availableHeight - bottomInset - emptyResultsActionSize.height - 16.0)
y: max(self.scrollView.contentSize.height - 70.0, availableHeight - bottomInset - emptyResultsActionSize.height - 16.0)
),
size: emptyResultsActionSize
)
@ -435,7 +438,7 @@ final class GiftStoreScreenComponent: Component {
if view.superview == nil {
view.alpha = 0.0
fadeTransition.setAlpha(view: view, alpha: 1.0)
self.insertSubview(view, belowSubview: self.loadingNode.view)
self.scrollView.addSubview(view)
}
view.bounds = CGRect(origin: .zero, size: emptyResultsActionFrame.size)
ComponentTransition.immediate.setPosition(view: view, position: emptyResultsActionFrame.center)
@ -451,7 +454,7 @@ final class GiftStoreScreenComponent: Component {
}
let bottomContentOffset = max(0.0, self.scrollView.contentSize.height - self.scrollView.contentOffset.y - self.scrollView.frame.height)
if interactive, bottomContentOffset < 320.0 {
if interactive, bottomContentOffset < 1000.0 {
self.state?.starGiftsContext.loadMore()
}
}
@ -471,6 +474,7 @@ final class GiftStoreScreenComponent: Component {
guard let self else {
return
}
self.showLoading = true
self.state?.starGiftsContext.updateSorting(.value)
self.scrollToTop()
})))
@ -481,6 +485,7 @@ final class GiftStoreScreenComponent: Component {
guard let self else {
return
}
self.showLoading = true
self.state?.starGiftsContext.updateSorting(.date)
self.scrollToTop()
})))
@ -491,6 +496,7 @@ final class GiftStoreScreenComponent: Component {
guard let self else {
return
}
self.showLoading = true
self.state?.starGiftsContext.updateSorting(.number)
self.scrollToTop()
})))
@ -514,7 +520,13 @@ final class GiftStoreScreenComponent: Component {
} else {
return false
}
}
}.sorted(by: { lhs, rhs in
if case let .model(_, lhsFile, _) = lhs, case let .model(_, rhsFile, _) = rhs, let lhsCount = self.state?.starGiftsState?.attributeCount[.model(lhsFile.fileId.id)], let rhsCount = self.state?.starGiftsState?.attributeCount[.model(rhsFile.fileId.id)] {
return lhsCount > rhsCount
} else {
return false
}
})
let currentFilterAttributes = self.state?.starGiftsState?.filterAttributes ?? []
let selectedModelAttributes = currentFilterAttributes.filter { attribute in
@ -564,6 +576,7 @@ final class GiftStoreScreenComponent: Component {
updatedFilterAttributes.append(attribute)
}
}
self.showLoading = true
self.state?.starGiftsContext.updateFilterAttributes(updatedFilterAttributes)
self.scrollToTop()
},
@ -577,6 +590,7 @@ final class GiftStoreScreenComponent: Component {
}
return true
}
self.showLoading = true
self.state?.starGiftsContext.updateFilterAttributes(updatedFilterAttributes)
self.scrollToTop()
}
@ -607,7 +621,13 @@ final class GiftStoreScreenComponent: Component {
} else {
return false
}
}
}.sorted(by: { lhs, rhs in
if case let .backdrop(_, lhsId, _, _, _, _, _) = lhs, case let .backdrop(_, rhsId, _, _, _, _, _) = rhs, let lhsCount = self.state?.starGiftsState?.attributeCount[.backdrop(lhsId)], let rhsCount = self.state?.starGiftsState?.attributeCount[.backdrop(rhsId)] {
return lhsCount > rhsCount
} else {
return false
}
})
let currentFilterAttributes = self.state?.starGiftsState?.filterAttributes ?? []
let selectedBackdropAttributes = currentFilterAttributes.filter { attribute in
@ -657,6 +677,7 @@ final class GiftStoreScreenComponent: Component {
updatedFilterAttributes.append(attribute)
}
}
self.showLoading = true
self.state?.starGiftsContext.updateFilterAttributes(updatedFilterAttributes)
self.scrollToTop()
},
@ -670,6 +691,7 @@ final class GiftStoreScreenComponent: Component {
}
return true
}
self.showLoading = true
self.state?.starGiftsContext.updateFilterAttributes(updatedFilterAttributes)
self.scrollToTop()
}
@ -700,7 +722,13 @@ final class GiftStoreScreenComponent: Component {
} else {
return false
}
}
}.sorted(by: { lhs, rhs in
if case let .pattern(_, lhsFile, _) = lhs, case let .pattern(_, rhsFile, _) = rhs, let lhsCount = self.state?.starGiftsState?.attributeCount[.pattern(lhsFile.fileId.id)], let rhsCount = self.state?.starGiftsState?.attributeCount[.pattern(rhsFile.fileId.id)] {
return lhsCount > rhsCount
} else {
return false
}
})
let currentFilterAttributes = self.state?.starGiftsState?.filterAttributes ?? []
let selectedPatternAttributes = currentFilterAttributes.filter { attribute in
@ -750,6 +778,7 @@ final class GiftStoreScreenComponent: Component {
updatedFilterAttributes.append(attribute)
}
}
self.showLoading = true
self.state?.starGiftsContext.updateFilterAttributes(updatedFilterAttributes)
self.scrollToTop()
},
@ -763,6 +792,7 @@ final class GiftStoreScreenComponent: Component {
}
return true
}
self.showLoading = true
self.state?.starGiftsContext.updateFilterAttributes(updatedFilterAttributes)
self.scrollToTop()
}
@ -804,6 +834,12 @@ final class GiftStoreScreenComponent: Component {
self.component = component
let isLoading = self.effectiveIsLoading
if case let .ready(loadMore, nextOffset) = self.state?.starGiftsState?.dataState {
if loadMore && nextOffset == nil {
} else {
self.showLoading = false
}
}
let theme = environment.theme
let strings = environment.strings
@ -812,7 +848,7 @@ final class GiftStoreScreenComponent: Component {
self.backgroundColor = environment.theme.list.blocksBackgroundColor
}
let bottomContentInset: CGFloat = 24.0
let bottomContentInset: CGFloat = 56.0
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
let headerSideInset: CGFloat = 24.0 + environment.safeInsets.left
@ -927,7 +963,7 @@ final class GiftStoreScreenComponent: Component {
}
let effectiveCount: Int32
if let count = self.effectiveGifts?.count, count > 0 || self.initialCount != nil {
if let count = self.state?.starGiftsState?.count, count > 0 || self.initialCount != nil {
if self.initialCount == nil {
self.initialCount = count
}
@ -1047,6 +1083,7 @@ final class GiftStoreScreenComponent: Component {
let loadingTransition: ComponentTransition = .easeInOut(duration: 0.25)
var showingFilters = false
let filterSize = self.filterSelector.update(
transition: transition,
component: AnyComponent(FilterSelectorComponent(
@ -1069,6 +1106,7 @@ final class GiftStoreScreenComponent: Component {
if let initialCount = self.initialCount, initialCount >= minimumCountToDisplayFilters {
loadingTransition.setAlpha(view: filterSelectorView, alpha: 1.0)
showingFilters = true
}
}
@ -1112,8 +1150,8 @@ final class GiftStoreScreenComponent: Component {
self.updateScrolling(transition: transition)
if isLoading {
self.loadingNode.update(size: availableSize, theme: environment.theme, transition: .immediate)
if isLoading && self.showLoading {
self.loadingNode.update(size: availableSize, theme: environment.theme, showFilters: !showingFilters, transition: .immediate)
loadingTransition.setAlpha(view: self.loadingNode.view, alpha: 1.0)
} else {
loadingTransition.setAlpha(view: self.loadingNode.view, alpha: 0.0)

@ -125,7 +125,7 @@ final class LoadingShimmerNode: ASDisplayNode {
private let backgroundColorNode: ASDisplayNode
private let effectNode: SearchShimmerEffectNode
private let maskNode: ASImageNode
private var currentParams: (size: CGSize, theme: PresentationTheme)?
private var currentParams: (size: CGSize, theme: PresentationTheme, showFilters: Bool)?
override init() {
self.backgroundColorNode = ASDisplayNode()
@ -142,11 +142,11 @@ final class LoadingShimmerNode: ASDisplayNode {
self.addSubnode(self.maskNode)
}
func update(size: CGSize, theme: PresentationTheme, transition: ContainedViewLayoutTransition) {
func update(size: CGSize, theme: PresentationTheme, showFilters: Bool, transition: ContainedViewLayoutTransition) {
let color = theme.list.itemSecondaryTextColor.mixedWith(theme.list.blocksBackgroundColor, alpha: 0.85)
if self.currentParams?.size != size || self.currentParams?.theme !== theme {
self.currentParams = (size, theme)
if self.currentParams?.size != size || self.currentParams?.theme !== theme || self.currentParams?.showFilters != showFilters {
self.currentParams = (size, theme, showFilters)
self.backgroundColorNode.backgroundColor = color
@ -156,10 +156,12 @@ final class LoadingShimmerNode: ASDisplayNode {
let sideInset: CGFloat = 16.0
let filterSpacing: CGFloat = 6.0
let filterWidth = (size.width - sideInset * 2.0 - filterSpacing * 3.0) / 4.0
for i in 0 ..< 4 {
context.addPath(CGPath(roundedRect: CGRect(origin: CGPoint(x: sideInset + (filterWidth + filterSpacing) * CGFloat(i), y: 0.0), size: CGSize(width: filterWidth, height: 28.0)), cornerWidth: 14.0, cornerHeight: 14.0, transform: nil))
if showFilters {
let filterSpacing: CGFloat = 6.0
let filterWidth = (size.width - sideInset * 2.0 - filterSpacing * 3.0) / 4.0
for i in 0 ..< 4 {
context.addPath(CGPath(roundedRect: CGRect(origin: CGPoint(x: sideInset + (filterWidth + filterSpacing) * CGFloat(i), y: 0.0), size: CGSize(width: filterWidth, height: 28.0)), cornerWidth: 14.0, cornerHeight: 14.0, transform: nil))
}
}
var currentY: CGFloat = 39.0 + 7.0

@ -958,7 +958,7 @@ private final class GiftViewSheetContent: CombinedComponent {
let location = CGRect(origin: CGPoint(x: absoluteLocation.x, y: absoluteLocation.y - 12.0), size: CGSize())
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: text), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in
return .ignore
return .dismiss(consume: false)
})
controller.present(tooltipController, in: .current)
}

@ -2164,7 +2164,7 @@ private func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostSt
let ItemBanned = 11
let ItemRecentActions = 12
let ItemAffiliatePrograms = 13
let ItemPostSuggestionsSettings = 14
//let ItemPostSuggestionsSettings = 14
let ItemPeerAutoTranslate = 15
let isCreator = channel.flags.contains(.isCreator)
@ -2214,9 +2214,9 @@ private func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostSt
}))
//TODO:localize
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPostSuggestionsSettings, label: .text("Off"), additionalBadgeLabel: presentationData.strings.Settings_New, text: "Post Suggestions", icon: UIImage(bundleImageName: "Chat/Info/PostSuggestionsIcon"), action: {
/*items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPostSuggestionsSettings, label: .text("Off"), additionalBadgeLabel: presentationData.strings.Settings_New, text: "Post Suggestions", icon: UIImage(bundleImageName: "Chat/Info/PostSuggestionsIcon"), action: {
interaction.editingOpenPostSuggestionsSetup()
}))
}))*/
}
if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) {

@ -279,7 +279,7 @@ private final class SheetContent: CombinedComponent {
case .starGiftResell:
let amountInfoString: NSAttributedString
if let value = state.amount?.value, value > 0 {
let starsValue = Int32(floor(Float(value) * Float(resaleConfiguration.paidMessageCommissionPermille) / 1000.0))
let starsValue = Int32(floor(Float(value) * Float(resaleConfiguration.starGiftCommissionPermille) / 1000.0))
let starsString = environment.strings.Stars_SellGift_AmountInfo_Stars(starsValue)
amountInfoString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.Stars_SellGift_AmountInfo(starsString).string, attributes: amountMarkdownAttributes, textAlignment: .natural))
@ -288,7 +288,7 @@ private final class SheetContent: CombinedComponent {
amountRightLabel = "\(formatTonUsdValue(Int64(starsValue), divide: false, rate: usdRate, dateTimeFormat: environment.dateTimeFormat))"
}
} else {
amountInfoString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.Stars_SellGift_AmountInfo("\(resaleConfiguration.paidMessageCommissionPermille / 10)%").string, attributes: amountMarkdownAttributes, textAlignment: .natural))
amountInfoString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.Stars_SellGift_AmountInfo("\(resaleConfiguration.starGiftCommissionPermille / 10)%").string, attributes: amountMarkdownAttributes, textAlignment: .natural))
}
amountFooter = AnyComponent(MultilineTextComponent(
text: .plain(amountInfoString),

@ -539,6 +539,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
var customEmojiContainerView: CustomEmojiContainerView?
let textInputBackgroundNode: ASImageNode
var textInputBackgroundTapRecognizer: TouchDownGestureRecognizer?
private var transparentTextInputBackgroundImage: UIImage?
let actionButtons: ChatTextInputActionButtonsNode
private let slowModeButton: BoostSlowModeButton
@ -1089,6 +1090,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
return false
}
}
self.textInputBackgroundTapRecognizer = recognizer
self.textInputBackgroundNode.isUserInteractionEnabled = true
self.textInputBackgroundNode.view.addGestureRecognizer(recognizer)
@ -1166,6 +1168,11 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
textInputNode.isUserInteractionEnabled = !self.sendingTextDisabled
self.textInputNode = textInputNode
if let textInputBackgroundTapRecognizer = self.textInputBackgroundTapRecognizer {
self.textInputBackgroundTapRecognizer = nil
self.textInputBackgroundNode.view.removeGestureRecognizer(textInputBackgroundTapRecognizer)
}
var accessoryButtonsWidth: CGFloat = 0.0
var firstButton = true
for (_, button) in self.accessoryItemButtons {