mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Various fixes
This commit is contained in:
parent
10d5fdcac3
commit
c5081979f0
@ -14293,3 +14293,6 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Gift.Resale.Unavailable.Title" = "Resell Gift";
|
||||
"Gift.Resale.Unavailable.Text" = "Sorry, you can't list this gift yet.\n\Reselling will be available on %@.";
|
||||
|
||||
"Gift.Transfer.Unavailable.Title" = "Transfer Gift";
|
||||
"Gift.Transfer.Unavailable.Text" = "Sorry, you can't transfer this gift yet.\n\Transferring will be available on %@.";
|
||||
|
@ -101,6 +101,7 @@ public enum PremiumLimitSubject {
|
||||
case membershipInSharedFolders
|
||||
case channels
|
||||
case expiringStories
|
||||
case multiStories
|
||||
case storiesWeekly
|
||||
case storiesMonthly
|
||||
case storiesChannelBoost(peer: EnginePeer, isCurrent: Bool, level: Int32, currentLevelBoosts: Int32, nextLevelBoosts: Int32?, link: String?, myBoostCount: Int32, canBoostAgain: Bool)
|
||||
|
@ -388,10 +388,10 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
|
||||
self.itemsDisposable = (updatedState
|
||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||
guard let strongSelf = self else {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateState(state)
|
||||
self.updateState(state)
|
||||
})
|
||||
|
||||
self.gridNode.scrollingInitiated = { [weak self] in
|
||||
@ -404,15 +404,16 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
|
||||
self.hiddenMediaDisposable = (self.hiddenMediaId.get()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] id in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controller?.interaction?.hiddenMediaId = id
|
||||
strongSelf.gridNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? MediaPickerGridItemNode {
|
||||
itemNode.updateHiddenMedia()
|
||||
}
|
||||
}
|
||||
strongSelf.selectionNode?.updateHiddenMedia()
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.controller?.interaction?.hiddenMediaId = id
|
||||
self.gridNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? MediaPickerGridItemNode {
|
||||
itemNode.updateHiddenMedia()
|
||||
}
|
||||
}
|
||||
self.selectionNode?.updateHiddenMedia()
|
||||
})
|
||||
|
||||
if let selectionState = self.controller?.interaction?.selectionState {
|
||||
@ -431,8 +432,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
|
||||
self.selectionChangedDisposable = (selectionChangedSignal(selectionState: selectionState)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] animated in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateSelectionState(animated: animated)
|
||||
if let self {
|
||||
self.updateSelectionState(animated: animated)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -451,8 +452,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
|
||||
self.itemsDimensionsUpdatedDisposable = (itemsDimensionsUpdatedSignal(editingState: editingState)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateSelectionState()
|
||||
if let self {
|
||||
self.updateSelectionState()
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -536,8 +537,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
self?.gridNode.scrollView.isScrollEnabled = isEnabled
|
||||
}
|
||||
selectionGesture.itemAt = { [weak self] point in
|
||||
if let strongSelf = self, let itemNode = strongSelf.gridNode.itemNodeAtPoint(point) as? MediaPickerGridItemNode, let selectableItem = itemNode.selectableItem {
|
||||
return (selectableItem, strongSelf.controller?.interaction?.selectionState?.isIdentifierSelected(selectableItem.uniqueIdentifier) ?? false)
|
||||
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
|
||||
}
|
||||
@ -2004,8 +2005,11 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
var hasSelect = false
|
||||
if forCollage {
|
||||
hasSelect = true
|
||||
} else if case .story = mode, selectionContext.selectionLimit > 1 {
|
||||
hasSelect = true
|
||||
} else if case .story = mode {
|
||||
if selectionContext.selectionLimit == 1 && context.isPremium {
|
||||
} else {
|
||||
hasSelect = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasSelect {
|
||||
@ -2584,6 +2588,34 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
}
|
||||
|
||||
@objc private func selectPressed() {
|
||||
let context = self.context
|
||||
if let selectionState = self.interaction?.selectionState, selectionState.selectionLimit == 1, !context.isPremium {
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = context.sharedContext.makePremiumLimitController(
|
||||
context: self.context,
|
||||
subject: .multiStories,
|
||||
count: 1,
|
||||
forceDark: true,
|
||||
cancel: {},
|
||||
action: {
|
||||
let controller = context.sharedContext.makePremiumIntroController(
|
||||
context: context,
|
||||
source: .stories,
|
||||
forceDark: true,
|
||||
dismissed: nil
|
||||
)
|
||||
replaceImpl?(controller)
|
||||
return true
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
controller?.replace(with: c)
|
||||
}
|
||||
self.requestDismiss {
|
||||
self.parentController()?.push(controller)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
self.navigationItem.setRightBarButton(nil, animated: true)
|
||||
self.explicitMultipleSelection = true
|
||||
|
||||
@ -2724,11 +2756,10 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
loop: true
|
||||
), action: { [weak self] _, f in
|
||||
f(.default)
|
||||
guard let strongSelf = self else {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
if let selectionContext = strongSelf.interaction?.selectionState, let editingContext = strongSelf.interaction?.editingState {
|
||||
if let selectionContext = self.interaction?.selectionState, let editingContext = self.interaction?.editingState {
|
||||
for case let item as TGMediaEditableItem in selectionContext.selectedItems() {
|
||||
editingContext.setSpoiler(hasGeneric, for: item)
|
||||
}
|
||||
@ -2754,10 +2785,10 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
}
|
||||
|
||||
let controller = self.context.sharedContext.makeStarsAmountScreen(context: self.context, initialValue: price, completion: { [weak self] amount in
|
||||
guard let strongSelf = self else {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let selectionContext = strongSelf.interaction?.selectionState, let editingContext = strongSelf.interaction?.editingState {
|
||||
if let selectionContext = self.interaction?.selectionState, let editingContext = self.interaction?.editingState {
|
||||
selectionContext.selectionLimit = 10
|
||||
for case let item as TGMediaEditableItem in selectionContext.selectedItems() {
|
||||
editingContext.setPrice(NSNumber(value: amount), for: item)
|
||||
|
@ -1101,6 +1101,30 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
badgePosition = max(0.32, CGFloat(component.count) / CGFloat(premiumLimit))
|
||||
badgeGraphPosition = badgePosition
|
||||
|
||||
if isPremiumDisabled {
|
||||
badgeText = "\(limit)"
|
||||
let numberString = strings.Premium_MaxExpiringStoriesNoPremiumTextNumberFormat(Int32(limit))
|
||||
string = strings.Premium_MaxExpiringStoriesNoPremiumTextFormat(numberString).string
|
||||
}
|
||||
buttonAnimationName = nil
|
||||
case .multiStories:
|
||||
let limit = state.limits.maxExpiringStoriesCount
|
||||
let premiumLimit = state.premiumLimits.maxExpiringStoriesCount
|
||||
iconName = "Premium/Stories"
|
||||
badgeText = "\(limit)"
|
||||
if component.count >= premiumLimit {
|
||||
let limitNumberString = strings.Premium_MaxExpiringStoriesFinalTextNumberFormat(Int32(premiumLimit))
|
||||
string = strings.Premium_MaxExpiringStoriesFinalTextFormat(limitNumberString).string
|
||||
} else {
|
||||
let limitNumberString = strings.Premium_MaxExpiringStoriesTextNumberFormat(Int32(limit))
|
||||
let premiumLimitNumberString = strings.Premium_MaxExpiringStoriesTextPremiumNumberFormat(Int32(premiumLimit))
|
||||
string = strings.Premium_MaxExpiringStoriesTextFormat(limitNumberString, premiumLimitNumberString).string
|
||||
}
|
||||
defaultValue = ""
|
||||
premiumValue = component.count >= premiumLimit ? "" : "\(premiumLimit)"
|
||||
badgePosition = max(0.32, CGFloat(component.count) / CGFloat(premiumLimit))
|
||||
badgeGraphPosition = badgePosition
|
||||
|
||||
if isPremiumDisabled {
|
||||
badgeText = "\(limit)"
|
||||
let numberString = strings.Premium_MaxExpiringStoriesNoPremiumTextNumberFormat(Int32(limit))
|
||||
@ -1210,7 +1234,6 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
remaining = nextLevelBoosts - component.count
|
||||
}
|
||||
|
||||
|
||||
if let _ = link {
|
||||
if let remaining {
|
||||
let storiesString = strings.ChannelBoost_StoriesPerDay(level + 1)
|
||||
@ -1813,6 +1836,7 @@ public class PremiumLimitScreen: ViewControllerComponentContainer {
|
||||
case membershipInSharedFolders
|
||||
case channels
|
||||
case expiringStories
|
||||
case multiStories
|
||||
case storiesWeekly
|
||||
case storiesMonthly
|
||||
|
||||
|
@ -484,6 +484,13 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
||||
case peerId(EnginePeer.Id)
|
||||
case name(String)
|
||||
case address(String)
|
||||
|
||||
public var peerId: EnginePeer.Id? {
|
||||
if case let .peerId(peerId) = self {
|
||||
return peerId
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public enum DecodingError: Error {
|
||||
|
@ -3253,6 +3253,24 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
guard let self, let arguments = self.subject.arguments, let navigationController = self.navigationController as? NavigationController, case let .unique(gift) = arguments.gift, let reference = arguments.reference, let transferStars = arguments.transferStars else {
|
||||
return
|
||||
}
|
||||
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
if let canTransferDate = arguments.canTransferDate, currentTime < canTransferDate {
|
||||
let dateString = stringForFullDate(timestamp: canTransferDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)
|
||||
let controller = textAlertController(
|
||||
context: self.context,
|
||||
title: presentationData.strings.Gift_Transfer_Unavailable_Title,
|
||||
text: presentationData.strings.Gift_Transfer_Unavailable_Text(dateString).string,
|
||||
actions: [
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})
|
||||
],
|
||||
parseMarkdown: true
|
||||
)
|
||||
self.present(controller, in: .window(.root))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let _ = (context.account.stateManager.contactBirthdays
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { birthdays in
|
||||
@ -3477,8 +3495,33 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
}
|
||||
|
||||
let _ = ((updateResellStars?(price) ?? context.engine.payments.updateStarGiftResalePrice(reference: reference, price: price))
|
||||
|> deliverOnMainQueue).startStandalone(error: { error in
|
||||
|> deliverOnMainQueue).startStandalone(error: { [weak self] error in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let title: String?
|
||||
let text: String
|
||||
switch error {
|
||||
case .generic:
|
||||
title = nil
|
||||
text = presentationData.strings.Gift_Send_ErrorUnknown
|
||||
case let .starGiftResellTooEarly(canResaleDate):
|
||||
let dateString = stringForFullDate(timestamp: canResaleDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)
|
||||
title = presentationData.strings.Gift_Resale_Unavailable_Title
|
||||
text = presentationData.strings.Gift_Resale_Unavailable_Text(dateString).string
|
||||
}
|
||||
|
||||
let controller = textAlertController(
|
||||
context: self.context,
|
||||
title: title,
|
||||
text: text,
|
||||
actions: [
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})
|
||||
],
|
||||
parseMarkdown: true
|
||||
)
|
||||
self.present(controller, in: .window(.root))
|
||||
}, completed: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
@ -3597,13 +3640,15 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
}
|
||||
|
||||
if case let .unique(gift) = arguments.gift, let resellStars = gift.resellStars, resellStars > 0 {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_View_Context_ChangePrice, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PriceTag"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { c, _ in
|
||||
c?.dismiss(completion: nil)
|
||||
|
||||
resellGiftImpl?(true)
|
||||
})))
|
||||
if arguments.reference != nil || gift.owner.peerId == context.account.peerId {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_View_Context_ChangePrice, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PriceTag"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { c, _ in
|
||||
c?.dismiss(completion: nil)
|
||||
|
||||
resellGiftImpl?(true)
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_View_Context_CopyLink, icon: { theme in
|
||||
|
@ -12,6 +12,7 @@ swift_library(
|
||||
deps = [
|
||||
"//submodules/Display",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/SSignalKit/SwiftSignalKit",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -2,9 +2,10 @@ import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
|
||||
private let animationDuration: TimeInterval = 12.0
|
||||
private let animationDelay: TimeInterval = 2.0
|
||||
private let animationSpeed: TimeInterval = 50.0
|
||||
private let animationDelay: TimeInterval = 2.5
|
||||
private let spacing: CGFloat = 20.0
|
||||
|
||||
public final class MarqueeComponent: Component {
|
||||
@ -43,6 +44,8 @@ public final class MarqueeComponent: Component {
|
||||
private let gradientMaskLayer = SimpleGradientLayer()
|
||||
private var isAnimating = false
|
||||
private var isOverflowing = false
|
||||
|
||||
private var component: MarqueeComponent?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
@ -60,6 +63,9 @@ public final class MarqueeComponent: Component {
|
||||
}
|
||||
|
||||
public func update(component: MarqueeComponent, availableSize: CGSize) -> CGSize {
|
||||
let previousComponent = self.component
|
||||
self.component = component
|
||||
|
||||
let attributedText = component.attributedText
|
||||
if let measureState = self.measureState {
|
||||
if measureState.attributedText.isEqual(to: attributedText) && measureState.availableSize == availableSize {
|
||||
@ -88,7 +94,7 @@ public final class MarqueeComponent: Component {
|
||||
if isOverflowing {
|
||||
self.setupMarqueeTextLayers(textImage: image.cgImage!, textWidth: boundingRect.width, containerWidth: availableSize.width)
|
||||
self.setupGradientMask(size: CGSize(width: availableSize.width, height: boundingRect.height))
|
||||
self.startAnimation()
|
||||
self.startAnimation(force: previousComponent?.attributedText != attributedText)
|
||||
} else {
|
||||
self.stopAnimation()
|
||||
self.textLayer.frame = CGRect(origin: CGPoint(x: innerPadding, y: 0.0), size: boundingRect.size)
|
||||
@ -137,17 +143,26 @@ public final class MarqueeComponent: Component {
|
||||
self.layer.mask = self.gradientMaskLayer
|
||||
}
|
||||
|
||||
private func startAnimation() {
|
||||
guard !self.isAnimating else {
|
||||
private func startAnimation(force: Bool = false) {
|
||||
guard !self.isAnimating || force else {
|
||||
return
|
||||
}
|
||||
self.isAnimating = true
|
||||
|
||||
self.containerLayer.removeAllAnimations()
|
||||
|
||||
self.containerLayer.animateBoundsOriginXAdditive(from: 0.0, to: self.textLayer.frame.width + spacing, duration: animationDuration, delay: animationDelay, timingFunction: CAMediaTimingFunctionName.linear.rawValue, completion: { _ in
|
||||
self.isAnimating = false
|
||||
self.startAnimation()
|
||||
let distance = self.textLayer.frame.width + spacing
|
||||
let duration = distance / animationSpeed
|
||||
Queue.mainQueue().after(animationDelay, {
|
||||
guard self.isAnimating else {
|
||||
return
|
||||
}
|
||||
self.containerLayer.animateBoundsOriginXAdditive(from: 0.0, to: distance, duration: duration, delay: 0.0, timingFunction: CAMediaTimingFunctionName.linear.rawValue, completion: { finished in
|
||||
if finished {
|
||||
self.isAnimating = false
|
||||
self.startAnimation()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -62,8 +62,10 @@ public final class StarsBalanceOverlayComponent: Component {
|
||||
self.balanceDisposable?.dispose()
|
||||
}
|
||||
|
||||
private var didTap = false
|
||||
@objc private func tapped() {
|
||||
if let component = self.component {
|
||||
if let component = self.component, !self.didTap {
|
||||
self.didTap = true
|
||||
component.action()
|
||||
}
|
||||
}
|
||||
|
@ -2803,6 +2803,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
mappedSubject = .channels
|
||||
case .expiringStories:
|
||||
mappedSubject = .expiringStories
|
||||
case .multiStories:
|
||||
mappedSubject = .multiStories
|
||||
case .storiesWeekly:
|
||||
mappedSubject = .storiesWeekly
|
||||
case .storiesMonthly:
|
||||
|
@ -179,9 +179,7 @@ public func chatTranslationState(context: AccountContext, peerId: EnginePeer.Id)
|
||||
if baseLang.hasSuffix(rawSuffix) {
|
||||
baseLang = String(baseLang.dropLast(rawSuffix.count))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return combineLatest(
|
||||
context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings])
|
||||
|> map { sharedData -> TranslationSettings in
|
||||
|
Loading…
x
Reference in New Issue
Block a user