mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 13:35:19 +00:00
Various fixes
This commit is contained in:
parent
fa46338010
commit
ee38ee55d4
@ -14315,3 +14315,8 @@ Sorry for the inconvenience.";
|
||||
"Gift.Buy.Confirm.Text.Stars_any" = "**%@** Stars";
|
||||
"Gift.Buy.Confirm.BuyFor_1" = "Buy for %@ Star";
|
||||
"Gift.Buy.Confirm.BuyFor_any" = "Buy for %@ Stars";
|
||||
|
||||
"Calls.HideCallsTab" = "Hide Calls Tab";
|
||||
|
||||
"Story.Editor.TooltipSelection_1" = "Tap here to view your %@ story";
|
||||
"Story.Editor.TooltipSelection_any" = "Tap here to view your %@ stories";
|
||||
|
@ -16,6 +16,7 @@ import TelegramBaseController
|
||||
import InviteLinksUI
|
||||
import UndoUI
|
||||
import TelegramCallsUI
|
||||
import TelegramUIPreferences
|
||||
|
||||
public enum CallListControllerMode {
|
||||
case tab
|
||||
@ -734,10 +735,22 @@ public final class CallListController: TelegramBaseController {
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] c, f in
|
||||
c?.dismiss(completion: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
strongSelf.callPressed()
|
||||
self.callPressed()
|
||||
})
|
||||
})))
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Calls_HideCallsTab, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Peer Info/HideIcon"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] c, f in
|
||||
c?.dismiss(completion: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = updateCallListSettingsInteractively(accountManager: self.context.sharedContext.accountManager, {
|
||||
$0.withUpdatedShowTab(false)
|
||||
}).start()
|
||||
})
|
||||
})))
|
||||
|
||||
|
@ -1015,7 +1015,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
|
||||
}
|
||||
}
|
||||
|
||||
itemNode.listNode.isMainTab.set(self.availableFilters.firstIndex(where: { $0.id == id }) == 0 ? true : false)
|
||||
itemNode.listNode.isMainTab.set(self.availableFilters.firstIndex(where: { $0.id == id }) == 0)
|
||||
itemNode.updateLayout(size: layout.size, insets: insets, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: itemInlineNavigationTransitionFraction, storiesInset: storiesInset, transition: nodeTransition)
|
||||
if let scrollingOffset = self.scrollingOffset {
|
||||
itemNode.updateScrollingOffset(navigationHeight: scrollingOffset.navigationHeight, offset: scrollingOffset.offset, transition: nodeTransition)
|
||||
|
@ -2092,8 +2092,6 @@ public final class ChatListNode: ListView {
|
||||
return .single(.setupPhoto(accountPeer))
|
||||
} else if suggestions.contains(.gracePremium) {
|
||||
return .single(.premiumGrace)
|
||||
} else if suggestions.contains(.setupBirthday) && birthday == nil {
|
||||
return .single(.setupBirthday)
|
||||
} else if suggestions.contains(.xmasPremiumGift) {
|
||||
return .single(.xmasPremiumGift)
|
||||
} else if suggestions.contains(.annualPremium) || suggestions.contains(.upgradePremium) || suggestions.contains(.restorePremium), let inAppPurchaseManager = context.inAppPurchaseManager {
|
||||
@ -2149,6 +2147,8 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
return .birthdayPremiumGift(peers: todayBirthdayPeers, birthdays: birthdays)
|
||||
}
|
||||
} else if suggestions.contains(.setupBirthday) && birthday == nil {
|
||||
return .single(.setupBirthday)
|
||||
} else if case let .link(id, url, title, subtitle) = suggestions.first(where: { if case .link = $0 { return true } else { return false} }) {
|
||||
return .single(.link(id: id, url: url, title: title, subtitle: subtitle))
|
||||
} else {
|
||||
|
@ -501,7 +501,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView {
|
||||
animationFraction = max(0.0, min(1.0, (CACurrentMediaTime() - animationState.startTime) / animationState.duration))
|
||||
animationFraction = animationState.curve.solve(at: animationFraction)
|
||||
if animationState.fromExtracted != isExtracted {
|
||||
fixedTransitionDirection = isExtracted ? true : false
|
||||
fixedTransitionDirection = isExtracted
|
||||
}
|
||||
} else {
|
||||
animationFraction = 1.0
|
||||
|
@ -527,7 +527,12 @@ final class ContextControllerNode: ViewControllerTracingNode, ASScrollViewDelega
|
||||
guard let strongSelf = self, let _ = gesture else {
|
||||
return
|
||||
}
|
||||
let localPoint = strongSelf.view.convert(point, from: view)
|
||||
let localPoint: CGPoint
|
||||
if let layout = strongSelf.validLayout, layout.metrics.isTablet, layout.size.width > layout.size.height, let view {
|
||||
localPoint = view.convert(point, to: nil)
|
||||
} else {
|
||||
localPoint = strongSelf.view.convert(point, from: view)
|
||||
}
|
||||
let initialPoint: CGPoint
|
||||
if let current = strongSelf.initialContinueGesturePoint {
|
||||
initialPoint = current
|
||||
|
@ -697,7 +697,7 @@ final class ColorGridComponent: Component {
|
||||
bottomRightRadius = largeCornerRadius
|
||||
}
|
||||
|
||||
let isLight = (selectedColor?.toUIColor().lightness ?? 1.0) < 0.5 ? true : false
|
||||
let isLight = (selectedColor?.toUIColor().lightness ?? 1.0) < 0.5
|
||||
|
||||
var selectionKnobImage = ColorSelectionImage(size: CGSize(width: squareSize, height: squareSize), topLeftRadius: topLeftRadius, topRightRadius: topRightRadius, bottomLeftRadius: bottomLeftRadius, bottomRightRadius: bottomRightRadius, isLight: isLight)
|
||||
if selectionKnobImage != self.selectionKnobImage {
|
||||
|
@ -203,6 +203,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
|
||||
case channelSendGiftTooltip = 76
|
||||
case starGiftWearTips = 77
|
||||
case channelSuggestTooltip = 78
|
||||
case multipleStoriesTooltip = 79
|
||||
|
||||
var key: ValueBoxKey {
|
||||
let v = ValueBoxKey(length: 4)
|
||||
@ -564,6 +565,10 @@ private struct ApplicationSpecificNoticeKeys {
|
||||
static func channelSuggestTooltip() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.channelSuggestTooltip.key)
|
||||
}
|
||||
|
||||
static func multipleStoriesTooltip() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.multipleStoriesTooltip.key)
|
||||
}
|
||||
}
|
||||
|
||||
public struct ApplicationSpecificNotice {
|
||||
@ -2426,4 +2431,31 @@ public struct ApplicationSpecificNotice {
|
||||
return Int(previousValue)
|
||||
}
|
||||
}
|
||||
|
||||
public static func getMultipleStoriesTooltip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
|
||||
return accountManager.transaction { transaction -> Int32 in
|
||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.multipleStoriesTooltip())?.get(ApplicationSpecificCounterNotice.self) {
|
||||
return value.value
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func incrementMultipleStoriesTooltip(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int = 1) -> Signal<Int, NoError> {
|
||||
return accountManager.transaction { transaction -> Int in
|
||||
var currentValue: Int32 = 0
|
||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.multipleStoriesTooltip())?.get(ApplicationSpecificCounterNotice.self) {
|
||||
currentValue = value.value
|
||||
}
|
||||
let previousValue = currentValue
|
||||
currentValue += Int32(count)
|
||||
|
||||
if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) {
|
||||
transaction.setNotice(ApplicationSpecificNoticeKeys.multipleStoriesTooltip(), entry)
|
||||
}
|
||||
|
||||
return Int(previousValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3630,7 +3630,13 @@ public class CameraScreenImpl: ViewController, CameraScreen {
|
||||
self.node.resumeCameraCapture(fromGallery: true)
|
||||
}
|
||||
|
||||
var dismissControllerImpl: (() -> Void)?
|
||||
class DismissArgs {
|
||||
var resumeOnDismiss = true
|
||||
}
|
||||
|
||||
var dismissControllerImpl: ((Bool) -> Void)?
|
||||
let dismissArgs = DismissArgs()
|
||||
|
||||
let controller: ViewController
|
||||
if let current = self.galleryController {
|
||||
controller = current
|
||||
@ -3686,7 +3692,7 @@ public class CameraScreenImpl: ViewController, CameraScreen {
|
||||
}
|
||||
}
|
||||
|
||||
dismissControllerImpl?()
|
||||
dismissControllerImpl?(true)
|
||||
} else {
|
||||
stopCameraCapture()
|
||||
|
||||
@ -3759,17 +3765,19 @@ public class CameraScreenImpl: ViewController, CameraScreen {
|
||||
self.node.collage?.addResults(signals: results)
|
||||
}
|
||||
} else {
|
||||
self.node.animateOutToEditor()
|
||||
if let assets = results as? [PHAsset] {
|
||||
self.completion(.single(.assets(assets)), nil, self.remainingStoryCount, {
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
self.galleryController = nil
|
||||
|
||||
dismissControllerImpl?()
|
||||
dismissControllerImpl?(false)
|
||||
}, dismissed: { [weak self] in
|
||||
resumeCameraCapture()
|
||||
if dismissArgs.resumeOnDismiss {
|
||||
resumeCameraCapture()
|
||||
}
|
||||
if let self {
|
||||
self.node.hasGallery = false
|
||||
self.node.requestUpdateLayout(transition: .immediate)
|
||||
@ -3780,7 +3788,8 @@ public class CameraScreenImpl: ViewController, CameraScreen {
|
||||
)
|
||||
self.galleryController = controller
|
||||
|
||||
dismissControllerImpl = { [weak controller] in
|
||||
dismissControllerImpl = { [weak controller] resume in
|
||||
dismissArgs.resumeOnDismiss = resume
|
||||
controller?.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
@ -235,6 +235,8 @@ final class GiftOptionsScreenComponent: Component {
|
||||
|
||||
private var chevronImage: (UIImage, PresentationTheme)?
|
||||
|
||||
private var resaleConfiguration: StarsSubscriptionConfiguration?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.scrollView = ScrollView()
|
||||
self.scrollView.showsVerticalScrollIndicator = true
|
||||
@ -408,9 +410,14 @@ final class GiftOptionsScreenComponent: Component {
|
||||
switch gift {
|
||||
case let .generic(gift):
|
||||
if let availability = gift.availability, availability.remains == 0, let minResaleStars = availability.minResaleStars {
|
||||
subject = .starGift(gift: gift, price: "⭐️ \(minResaleStars)+")
|
||||
let priceString = presentationStringsFormattedNumber(Int32(minResaleStars), environment.dateTimeFormat.groupingSeparator)
|
||||
if let resaleConfiguration = self.resaleConfiguration, minResaleStars == resaleConfiguration.starGiftResaleMaxAmount || availability.resale == 1 {
|
||||
subject = .starGift(gift: gift, price: "⭐️ \(priceString)")
|
||||
} else {
|
||||
subject = .starGift(gift: gift, price: "⭐️ \(priceString)+")
|
||||
}
|
||||
} else {
|
||||
subject = .starGift(gift: gift, price: "⭐️ \(gift.price)")
|
||||
subject = .starGift(gift: gift, price: "⭐️ \(presentationStringsFormattedNumber(Int32(gift.price), environment.dateTimeFormat.groupingSeparator))")
|
||||
}
|
||||
case let .unique(gift):
|
||||
subject = .uniqueGift(gift: gift, price: nil)
|
||||
@ -773,6 +780,8 @@ final class GiftOptionsScreenComponent: Component {
|
||||
self.optionsPromise.set(component.context.engine.payments.starsTopUpOptions()
|
||||
|> map(Optional.init))
|
||||
}
|
||||
|
||||
self.resaleConfiguration = StarsSubscriptionConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 })
|
||||
}
|
||||
self.component = component
|
||||
|
||||
|
@ -109,6 +109,15 @@ public final class FilterSelectorComponent: Component {
|
||||
return true
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
for (_, item) in self.visibleItems {
|
||||
if let itemView = item.title.view {
|
||||
itemView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
itemView.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: FilterSelectorComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
@ -27,6 +27,8 @@ import UndoUI
|
||||
import ContextUI
|
||||
import LottieComponent
|
||||
|
||||
private let minimumCountToDisplayFilters = 18
|
||||
|
||||
final class GiftStoreScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
@ -93,7 +95,8 @@ final class GiftStoreScreenComponent: Component {
|
||||
|
||||
private var starsStateDisposable: Disposable?
|
||||
private var starsState: StarsContext.State?
|
||||
|
||||
private var initialCount: Int?
|
||||
|
||||
private var component: GiftStoreScreenComponent?
|
||||
private(set) weak var state: State?
|
||||
private var environment: EnvironmentType?
|
||||
@ -148,6 +151,13 @@ final class GiftStoreScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
private var effectiveIsLoading: Bool {
|
||||
if self.state?.starGiftsState?.gifts == nil || self.state?.starGiftsState?.dataState == .loading {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private func updateScrolling(interactive: Bool = false, transition: ComponentTransition) {
|
||||
guard let environment = self.environment, let component = self.component, self.state?.starGiftsState?.dataState != .loading else {
|
||||
return
|
||||
@ -163,6 +173,11 @@ final class GiftStoreScreenComponent: Component {
|
||||
transition.setAlpha(view: topSeparator, alpha: topPanelAlpha)
|
||||
}
|
||||
|
||||
var topInset = environment.navigationHeight + 39.0
|
||||
if let initialCount = self.initialCount, initialCount < minimumCountToDisplayFilters {
|
||||
topInset = environment.navigationHeight
|
||||
}
|
||||
|
||||
let visibleBounds = self.scrollView.bounds.insetBy(dx: 0.0, dy: -10.0)
|
||||
if let starGifts = self.effectiveGifts {
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
@ -172,7 +187,7 @@ final class GiftStoreScreenComponent: Component {
|
||||
let starsOptionSize = CGSize(width: optionWidth, height: 154.0)
|
||||
|
||||
var validIds: [AnyHashable] = []
|
||||
var itemFrame = CGRect(origin: CGPoint(x: sideInset, y: environment.navigationHeight + 39.0 + 9.0), size: starsOptionSize)
|
||||
var itemFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset + 9.0), size: starsOptionSize)
|
||||
|
||||
let controller = environment.controller
|
||||
|
||||
@ -337,7 +352,6 @@ final class GiftStoreScreenComponent: Component {
|
||||
showClearFilters = true
|
||||
}
|
||||
|
||||
let topInset: CGFloat = environment.navigationHeight + 39.0
|
||||
let bottomInset: CGFloat = environment.safeInsets.bottom
|
||||
|
||||
var emptyResultsActionFrame = CGRect(
|
||||
@ -443,7 +457,7 @@ final class GiftStoreScreenComponent: Component {
|
||||
}
|
||||
|
||||
func openSortContextMenu(sourceView: UIView) {
|
||||
guard let component = self.component, let controller = self.environment?.controller() else {
|
||||
guard let component = self.component, let controller = self.environment?.controller(), !self.effectiveIsLoading else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -486,10 +500,10 @@ final class GiftStoreScreenComponent: Component {
|
||||
}
|
||||
|
||||
func openModelContextMenu(sourceView: UIView) {
|
||||
guard let component = self.component, let controller = self.environment?.controller() else {
|
||||
guard let component = self.component, let controller = self.environment?.controller(), !self.effectiveIsLoading else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let searchQueryPromise = ValuePromise<String>("")
|
||||
|
||||
@ -579,7 +593,7 @@ final class GiftStoreScreenComponent: Component {
|
||||
}
|
||||
|
||||
func openBackdropContextMenu(sourceView: UIView) {
|
||||
guard let component = self.component, let controller = self.environment?.controller() else {
|
||||
guard let component = self.component, let controller = self.environment?.controller(), !self.effectiveIsLoading else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -672,7 +686,7 @@ final class GiftStoreScreenComponent: Component {
|
||||
}
|
||||
|
||||
func openSymbolContextMenu(sourceView: UIView) {
|
||||
guard let component = self.component, let controller = self.environment?.controller() else {
|
||||
guard let component = self.component, let controller = self.environment?.controller(), !self.effectiveIsLoading else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -789,10 +803,7 @@ final class GiftStoreScreenComponent: Component {
|
||||
}
|
||||
self.component = component
|
||||
|
||||
var isLoading = false
|
||||
if self.state?.starGiftsState?.gifts == nil || self.state?.starGiftsState?.dataState == .loading {
|
||||
isLoading = true
|
||||
}
|
||||
let isLoading = self.effectiveIsLoading
|
||||
|
||||
let theme = environment.theme
|
||||
let strings = environment.strings
|
||||
@ -808,7 +819,10 @@ final class GiftStoreScreenComponent: Component {
|
||||
var contentHeight: CGFloat = 0.0
|
||||
contentHeight += environment.navigationHeight
|
||||
|
||||
let topPanelHeight = environment.navigationHeight + 39.0
|
||||
var topPanelHeight = environment.navigationHeight + 39.0
|
||||
if let initialCount = self.initialCount, initialCount < minimumCountToDisplayFilters {
|
||||
topPanelHeight = environment.navigationHeight
|
||||
}
|
||||
|
||||
let topPanelSize = self.topPanel.update(
|
||||
transition: transition,
|
||||
@ -913,7 +927,10 @@ final class GiftStoreScreenComponent: Component {
|
||||
}
|
||||
|
||||
let effectiveCount: Int32
|
||||
if let count = self.effectiveGifts?.count {
|
||||
if let count = self.effectiveGifts?.count, count > 0 || self.initialCount != nil {
|
||||
if self.initialCount == nil {
|
||||
self.initialCount = count
|
||||
}
|
||||
effectiveCount = Int32(count)
|
||||
} else if let resale = component.gift.availability?.resale {
|
||||
effectiveCount = Int32(resale)
|
||||
@ -1028,13 +1045,15 @@ final class GiftStoreScreenComponent: Component {
|
||||
}
|
||||
))
|
||||
|
||||
let loadingTransition: ComponentTransition = .easeInOut(duration: 0.25)
|
||||
|
||||
let filterSize = self.filterSelector.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(FilterSelectorComponent(
|
||||
context: component.context,
|
||||
colors: FilterSelectorComponent.Colors(
|
||||
foreground: theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.65),
|
||||
background: theme.list.itemSecondaryTextColor.withMultipliedAlpha(0.15)
|
||||
background: theme.list.itemSecondaryTextColor.mixedWith(theme.list.blocksBackgroundColor, alpha: 0.85)
|
||||
),
|
||||
items: filterItems
|
||||
)),
|
||||
@ -1043,9 +1062,14 @@ final class GiftStoreScreenComponent: Component {
|
||||
)
|
||||
if let filterSelectorView = self.filterSelector.view {
|
||||
if filterSelectorView.superview == nil {
|
||||
filterSelectorView.alpha = 0.0
|
||||
self.addSubview(filterSelectorView)
|
||||
}
|
||||
transition.setFrame(view: filterSelectorView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - filterSize.width) / 2.0), y: topInset + 56.0), size: filterSize))
|
||||
|
||||
if let initialCount = self.initialCount, initialCount >= minimumCountToDisplayFilters {
|
||||
loadingTransition.setAlpha(view: filterSelectorView, alpha: 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
if let starGifts = self.state?.starGiftsState?.gifts {
|
||||
@ -1088,14 +1112,13 @@ final class GiftStoreScreenComponent: Component {
|
||||
|
||||
self.updateScrolling(transition: transition)
|
||||
|
||||
let loadingTransition: ComponentTransition = .easeInOut(duration: 0.25)
|
||||
if isLoading {
|
||||
self.loadingNode.update(size: availableSize, theme: environment.theme, transition: .immediate)
|
||||
loadingTransition.setAlpha(view: self.loadingNode.view, alpha: 1.0)
|
||||
} else {
|
||||
loadingTransition.setAlpha(view: self.loadingNode.view, alpha: 0.0)
|
||||
}
|
||||
transition.setFrame(view: self.loadingNode.view, frame: CGRect(origin: CGPoint(x: 0.0, y: environment.navigationHeight + 39.0 + 7.0), size: availableSize))
|
||||
transition.setFrame(view: self.loadingNode.view, frame: CGRect(origin: CGPoint(x: 0.0, y: environment.navigationHeight), size: availableSize))
|
||||
|
||||
return availableSize
|
||||
}
|
||||
@ -1108,19 +1131,22 @@ final class GiftStoreScreenComponent: Component {
|
||||
final class State: ComponentState {
|
||||
private let context: AccountContext
|
||||
var peerId: EnginePeer.Id
|
||||
private let gift: StarGift.Gift
|
||||
|
||||
private var disposable: Disposable?
|
||||
|
||||
fileprivate let starGiftsContext: ResaleGiftsContext
|
||||
fileprivate var starGiftsState: ResaleGiftsContext.State?
|
||||
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
peerId: EnginePeer.Id,
|
||||
giftId: Int64
|
||||
gift: StarGift.Gift
|
||||
) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.starGiftsContext = ResaleGiftsContext(account: context.account, giftId: giftId)
|
||||
self.gift = gift
|
||||
self.starGiftsContext = ResaleGiftsContext(account: context.account, giftId: gift.id)
|
||||
|
||||
super.init()
|
||||
|
||||
@ -1140,7 +1166,7 @@ final class GiftStoreScreenComponent: Component {
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State(context: self.context, peerId: self.peerId, giftId: self.gift.id)
|
||||
return State(context: self.context, peerId: self.peerId, gift: self.gift)
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: State, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||
|
@ -154,10 +154,17 @@ final class LoadingShimmerNode: ASDisplayNode {
|
||||
context.setFillColor(theme.list.blocksBackgroundColor.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
var currentY: CGFloat = 0.0
|
||||
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))
|
||||
}
|
||||
|
||||
var currentY: CGFloat = 39.0 + 7.0
|
||||
var rowIndex: Int = 0
|
||||
|
||||
let sideInset: CGFloat = 16.0// + environment.safeInsets.left
|
||||
let optionSpacing: CGFloat = 10.0
|
||||
let optionWidth = (size.width - sideInset * 2.0 - optionSpacing * 2.0) / 3.0
|
||||
let itemSize = CGSize(width: optionWidth, height: 154.0)
|
||||
@ -167,7 +174,7 @@ final class LoadingShimmerNode: ASDisplayNode {
|
||||
|
||||
while currentY < size.height {
|
||||
for i in 0 ..< 3 {
|
||||
let itemOrigin = CGPoint(x: sideInset + CGFloat(i) * (itemSize.width + optionSpacing), y: 2.0 + CGFloat(rowIndex) * (itemSize.height + optionSpacing))
|
||||
let itemOrigin = CGPoint(x: sideInset + CGFloat(i) * (itemSize.width + optionSpacing), y: 39.0 + 9.0 + CGFloat(rowIndex) * (itemSize.height + optionSpacing))
|
||||
context.addPath(CGPath(roundedRect: CGRect(origin: itemOrigin, size: itemSize), cornerWidth: 10.0, cornerHeight: 10.0, transform: nil))
|
||||
}
|
||||
currentY += itemSize.height
|
||||
|
@ -496,7 +496,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
|
||||
if currentTime > starsConvertMaxDate {
|
||||
let days: Int32 = Int32(ceil(Float(configuration.convertToStarsPeriod) / 86400.0))
|
||||
let controller = textAlertController(
|
||||
let alertController = textAlertController(
|
||||
context: self.context,
|
||||
title: presentationData.strings.Gift_Convert_Title,
|
||||
text: presentationData.strings.Gift_Convert_Period_Unavailable_Text(presentationData.strings.Gift_Convert_Period_Unavailable_Days(days)).string,
|
||||
@ -505,7 +505,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
],
|
||||
parseMarkdown: true
|
||||
)
|
||||
controller.present(controller, in: .window(.root))
|
||||
controller.present(alertController, in: .window(.root))
|
||||
} else {
|
||||
let delta = starsConvertMaxDate - currentTime
|
||||
let days: Int32 = Int32(ceil(Float(delta) / 86400.0))
|
||||
|
@ -67,6 +67,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/SaveProgressScreen",
|
||||
"//submodules/TelegramUI/Components/MediaAssetsContext",
|
||||
"//submodules/CheckNode",
|
||||
"//submodules/TelegramNotices",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -49,6 +49,7 @@ import StickerPickerScreen
|
||||
import UIKitRuntimeUtils
|
||||
import ImageObjectSeparation
|
||||
import SaveProgressScreen
|
||||
import TelegramNotices
|
||||
|
||||
private let playbackButtonTag = GenericComponentViewTag()
|
||||
private let muteButtonTag = GenericComponentViewTag()
|
||||
@ -58,6 +59,7 @@ private let drawButtonTag = GenericComponentViewTag()
|
||||
private let textButtonTag = GenericComponentViewTag()
|
||||
private let stickerButtonTag = GenericComponentViewTag()
|
||||
private let dayNightButtonTag = GenericComponentViewTag()
|
||||
private let selectionButtonTag = GenericComponentViewTag()
|
||||
|
||||
final class MediaEditorScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
@ -2320,7 +2322,8 @@ final class MediaEditorScreenComponent: Component {
|
||||
controller.hapticFeedback.impact(.light)
|
||||
}
|
||||
},
|
||||
animateAlpha: false
|
||||
animateAlpha: false,
|
||||
tag: selectionButtonTag
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 33.0, height: 33.0)
|
||||
@ -4744,6 +4747,33 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
self.controller?.present(tooltipController, in: .current)
|
||||
}
|
||||
|
||||
private var displayedSelectionTooltip = false
|
||||
func presentSelectionTooltip() {
|
||||
guard let sourceView = self.componentHost.findTaggedView(tag: selectionButtonTag), !self.displayedSelectionTooltip, self.items.count > 1 else {
|
||||
return
|
||||
}
|
||||
|
||||
self.displayedSelectionTooltip = true
|
||||
|
||||
let _ = (ApplicationSpecificNotice.getMultipleStoriesTooltip(accountManager: self.context.sharedContext.accountManager)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] count in
|
||||
guard let self, count < 3 else {
|
||||
return
|
||||
}
|
||||
let parentFrame = self.view.convert(self.bounds, to: nil)
|
||||
let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0)
|
||||
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 3.0), size: CGSize())
|
||||
|
||||
let text = self.presentationData.strings.Story_Editor_TooltipSelection(Int32(self.items.count))
|
||||
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: text), location: .point(location, .bottom), displayDuration: .default, inset: 8.0, shouldDismissOnTouch: { _, _ in
|
||||
return .dismiss(consume: false)
|
||||
})
|
||||
self.controller?.present(tooltipController, in: .current)
|
||||
|
||||
let _ = ApplicationSpecificNotice.incrementMultipleStoriesTooltip(accountManager: self.context.sharedContext.accountManager).start()
|
||||
})
|
||||
}
|
||||
|
||||
fileprivate weak var saveTooltip: SaveProgressScreen?
|
||||
func presentSaveTooltip() {
|
||||
guard let controller = self.controller else {
|
||||
@ -5725,6 +5755,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
|
||||
if hasAppeared && !self.hasAppeared {
|
||||
self.hasAppeared = hasAppeared
|
||||
|
||||
self.presentSelectionTooltip()
|
||||
}
|
||||
|
||||
let componentSize = self.componentHost.update(
|
||||
|
@ -347,7 +347,7 @@ extension PeerInfoScreenImpl {
|
||||
|
||||
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
||||
self.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
|
||||
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: mode == .custom ? true : false)
|
||||
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: mode == .custom)
|
||||
|
||||
if [.suggest, .fallback].contains(mode) {
|
||||
} else {
|
||||
|
@ -895,8 +895,13 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
if let controller = self.controller {
|
||||
webView.updateMetrics(height: viewportFrame.height, isExpanded: controller.isContainerExpanded(), isStable: !controller.isContainerPanning(), transition: transition)
|
||||
|
||||
let contentInsetsData = "{top:\(contentTopInset), bottom:0.0, left:0.0, right:0.0}"
|
||||
webView.sendEvent(name: "content_safe_area_changed", data: contentInsetsData)
|
||||
let data: JSON = [
|
||||
"top": Double(contentTopInset),
|
||||
"bottom": 0.0,
|
||||
"left": 0.0,
|
||||
"right": 0.0
|
||||
]
|
||||
webView.sendEvent(name: "content_safe_area_changed", data: data.string)
|
||||
|
||||
if self.updateWebViewWhenStable && !controller.isContainerPanning() {
|
||||
self.updateWebViewWhenStable = false
|
||||
@ -1333,7 +1338,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
controller.completion = { [weak self] result in
|
||||
if let strongSelf = self {
|
||||
if let result = result {
|
||||
strongSelf.sendQrCodeScannedEvent(data: result)
|
||||
strongSelf.sendQrCodeScannedEvent(dataString: result)
|
||||
} else {
|
||||
strongSelf.sendQrCodeScannerClosedEvent()
|
||||
}
|
||||
@ -1923,8 +1928,11 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
|
||||
private func sendInvoiceClosedEvent(slug: String, result: InvoiceCloseResult) {
|
||||
let paramsString = "{slug: \"\(slug)\", status: \"\(result.string)\"}"
|
||||
self.webView?.sendEvent(name: "invoice_closed", data: paramsString)
|
||||
let data: JSON = [
|
||||
"slug": slug,
|
||||
"status": result.string
|
||||
]
|
||||
self.webView?.sendEvent(name: "invoice_closed", data: data.string)
|
||||
}
|
||||
|
||||
fileprivate func sendBackButtonEvent() {
|
||||
@ -1936,24 +1944,23 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
|
||||
fileprivate func sendAlertButtonEvent(id: String?) {
|
||||
var paramsString: String?
|
||||
if let id = id {
|
||||
paramsString = "{button_id: \"\(id)\"}"
|
||||
var data: [String: Any] = [:]
|
||||
if let id {
|
||||
data["button_id"] = id
|
||||
}
|
||||
self.webView?.sendEvent(name: "popup_closed", data: paramsString ?? "{}")
|
||||
}
|
||||
|
||||
fileprivate func sendPhoneRequestedEvent(phone: String?) {
|
||||
var paramsString: String?
|
||||
if let phone = phone {
|
||||
paramsString = "{phone_number: \"\(phone)\"}"
|
||||
if let serializedData = JSON(dictionary: data)?.string {
|
||||
self.webView?.sendEvent(name: "popup_closed", data: serializedData)
|
||||
}
|
||||
self.webView?.sendEvent(name: "phone_requested", data: paramsString)
|
||||
}
|
||||
|
||||
fileprivate func sendQrCodeScannedEvent(data: String?) {
|
||||
let paramsString = data.flatMap { "{data: \"\($0)\"}" } ?? "{}"
|
||||
self.webView?.sendEvent(name: "qr_text_received", data: paramsString)
|
||||
|
||||
fileprivate func sendQrCodeScannedEvent(dataString: String?) {
|
||||
var data: [String: Any] = [:]
|
||||
if let dataString {
|
||||
data["data"] = dataString
|
||||
}
|
||||
if let serializedData = JSON(dictionary: data)?.string {
|
||||
self.webView?.sendEvent(name: "qr_text_received", data: serializedData)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func sendQrCodeScannerClosedEvent() {
|
||||
@ -1961,14 +1968,15 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
|
||||
fileprivate func sendClipboardTextEvent(requestId: String, fillData: Bool) {
|
||||
var paramsString: String
|
||||
var data: [String: Any] = [:]
|
||||
data["req_id"] = requestId
|
||||
if fillData {
|
||||
let data = UIPasteboard.general.string ?? ""
|
||||
paramsString = "{req_id: \"\(requestId)\", data: \"\(data)\"}"
|
||||
} else {
|
||||
paramsString = "{req_id: \"\(requestId)\"}"
|
||||
let pasteboardData = UIPasteboard.general.string ?? ""
|
||||
data["data"] = pasteboardData
|
||||
}
|
||||
if let serializedData = JSON(dictionary: data)?.string {
|
||||
self.webView?.sendEvent(name: "clipboard_text_received", data: serializedData)
|
||||
}
|
||||
self.webView?.sendEvent(name: "clipboard_text_received", data: paramsString)
|
||||
}
|
||||
|
||||
fileprivate func requestWriteAccess() {
|
||||
@ -1977,13 +1985,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
|
||||
let sendEvent: (Bool) -> Void = { success in
|
||||
var paramsString: String
|
||||
if success {
|
||||
paramsString = "{status: \"allowed\"}"
|
||||
} else {
|
||||
paramsString = "{status: \"cancelled\"}"
|
||||
}
|
||||
self.webView?.sendEvent(name: "write_access_requested", data: paramsString)
|
||||
let data: JSON = [
|
||||
"status": success ? "allowed" : "cancelled"
|
||||
]
|
||||
self.webView?.sendEvent(name: "write_access_requested", data: data.string)
|
||||
}
|
||||
|
||||
let _ = (self.context.engine.messages.canBotSendMessages(botId: controller.botId)
|
||||
@ -2021,13 +2026,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
return
|
||||
}
|
||||
let sendEvent: (Bool) -> Void = { success in
|
||||
var paramsString: String
|
||||
if success {
|
||||
paramsString = "{status: \"sent\"}"
|
||||
} else {
|
||||
paramsString = "{status: \"cancelled\"}"
|
||||
}
|
||||
self.webView?.sendEvent(name: "phone_requested", data: paramsString)
|
||||
let data: JSON = [
|
||||
"status": success ? "sent" : "cancelled"
|
||||
]
|
||||
self.webView?.sendEvent(name: "phone_requested", data: data.string)
|
||||
}
|
||||
|
||||
let _ = (self.context.engine.data.get(
|
||||
@ -2348,28 +2350,15 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
state.opaqueToken = encryptedData
|
||||
return state
|
||||
})
|
||||
|
||||
var data: [String: Any] = [:]
|
||||
data["status"] = "updated"
|
||||
|
||||
guard let jsonData = try? JSONSerialization.data(withJSONObject: data) else {
|
||||
return
|
||||
}
|
||||
guard let jsonDataString = String(data: jsonData, encoding: .utf8) else {
|
||||
return
|
||||
}
|
||||
self.webView?.sendEvent(name: "biometry_token_updated", data: jsonDataString)
|
||||
let data: JSON = [
|
||||
"status": "updated"
|
||||
]
|
||||
self.webView?.sendEvent(name: "biometry_token_updated", data: data.string)
|
||||
} else {
|
||||
var data: [String: Any] = [:]
|
||||
data["status"] = "failed"
|
||||
|
||||
guard let jsonData = try? JSONSerialization.data(withJSONObject: data) else {
|
||||
return
|
||||
}
|
||||
guard let jsonDataString = String(data: jsonData, encoding: .utf8) else {
|
||||
return
|
||||
}
|
||||
self.webView?.sendEvent(name: "biometry_token_updated", data: jsonDataString)
|
||||
let data: JSON = [
|
||||
"status": "failed"
|
||||
]
|
||||
self.webView?.sendEvent(name: "biometry_token_updated", data: data.string)
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
@ -2379,17 +2368,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
state.opaqueToken = nil
|
||||
return state
|
||||
})
|
||||
|
||||
var data: [String: Any] = [:]
|
||||
data["status"] = "removed"
|
||||
|
||||
guard let jsonData = try? JSONSerialization.data(withJSONObject: data) else {
|
||||
return
|
||||
}
|
||||
guard let jsonDataString = String(data: jsonData, encoding: .utf8) else {
|
||||
return
|
||||
}
|
||||
self.webView?.sendEvent(name: "biometry_token_updated", data: jsonDataString)
|
||||
let data: JSON = [
|
||||
"status": "removed"
|
||||
]
|
||||
self.webView?.sendEvent(name: "biometry_token_updated", data: data.string)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2410,13 +2392,18 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
return
|
||||
}
|
||||
guard controller.isFullscreen != isFullscreen else {
|
||||
self.webView?.sendEvent(name: "fullscreen_failed", data: "{error: \"ALREADY_FULLSCREEN\"}")
|
||||
let data: JSON = [
|
||||
"error": "ALREADY_FULLSCREEN"
|
||||
]
|
||||
self.webView?.sendEvent(name: "fullscreen_failed", data: data.string)
|
||||
return
|
||||
}
|
||||
|
||||
let paramsString = "{is_fullscreen: \( isFullscreen ? "true" : "false" )}"
|
||||
self.webView?.sendEvent(name: "fullscreen_changed", data: paramsString)
|
||||
|
||||
let data: JSON = [
|
||||
"is_fullscreen": isFullscreen
|
||||
]
|
||||
self.webView?.sendEvent(name: "fullscreen_changed", data: data.string)
|
||||
|
||||
controller.isFullscreen = isFullscreen
|
||||
if isFullscreen {
|
||||
controller.requestAttachmentMenuExpansion()
|
||||
@ -2436,7 +2423,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
private var isAccelerometerActive = false
|
||||
fileprivate func setIsAccelerometerActive(_ isActive: Bool, refreshRate: Double? = nil) {
|
||||
guard self.motionManager.isAccelerometerAvailable else {
|
||||
self.webView?.sendEvent(name: "accelerometer_failed", data: "{error: \"UNSUPPORTED\"}")
|
||||
let data: JSON = [
|
||||
"error": "UNSUPPORTED"
|
||||
]
|
||||
self.webView?.sendEvent(name: "accelerometer_failed", data: data.string)
|
||||
return
|
||||
}
|
||||
guard self.isAccelerometerActive != isActive else {
|
||||
@ -2451,15 +2441,17 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
} else {
|
||||
self.motionManager.accelerometerUpdateInterval = 1.0
|
||||
}
|
||||
self.motionManager.startAccelerometerUpdates(to: OperationQueue.main) { [weak self] data, error in
|
||||
guard let self, let data else {
|
||||
self.motionManager.startAccelerometerUpdates(to: OperationQueue.main) { [weak self] accelerometerData, error in
|
||||
guard let self, let accelerometerData else {
|
||||
return
|
||||
}
|
||||
let gravityConstant = 9.81
|
||||
self.webView?.sendEvent(
|
||||
name: "accelerometer_changed",
|
||||
data: "{x: \(data.acceleration.x * gravityConstant), y: \(data.acceleration.y * gravityConstant), z: \(data.acceleration.z * gravityConstant)}"
|
||||
)
|
||||
let gravityConstant: Double = 9.81
|
||||
let data: JSON = [
|
||||
"x": Double(accelerometerData.acceleration.x * gravityConstant),
|
||||
"y": Double(accelerometerData.acceleration.y * gravityConstant),
|
||||
"z": Double(accelerometerData.acceleration.z * gravityConstant)
|
||||
]
|
||||
self.webView?.sendEvent(name: "accelerometer_changed", data: data.string)
|
||||
}
|
||||
} else {
|
||||
if self.motionManager.isAccelerometerActive {
|
||||
@ -2472,7 +2464,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
private var isDeviceOrientationActive = false
|
||||
fileprivate func setIsDeviceOrientationActive(_ isActive: Bool, refreshRate: Double? = nil, absolute: Bool = false) {
|
||||
guard self.motionManager.isDeviceMotionAvailable else {
|
||||
self.webView?.sendEvent(name: "device_orientation_failed", data: "{error: \"UNSUPPORTED\"}")
|
||||
let data: JSON = [
|
||||
"error": "UNSUPPORTED"
|
||||
]
|
||||
self.webView?.sendEvent(name: "device_orientation_failed", data: data.string)
|
||||
return
|
||||
}
|
||||
guard self.isDeviceOrientationActive != isActive else {
|
||||
@ -2505,25 +2500,29 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
effectiveIsAbsolute = false
|
||||
}
|
||||
self.motionManager.startDeviceMotionUpdates(using: referenceFrame, to: OperationQueue.main) { [weak self] data, error in
|
||||
guard let self, let data else {
|
||||
self.motionManager.startDeviceMotionUpdates(using: referenceFrame, to: OperationQueue.main) { [weak self] motionData, error in
|
||||
guard let self, let motionData else {
|
||||
return
|
||||
}
|
||||
var alpha: Double
|
||||
if effectiveIsAbsolute {
|
||||
alpha = data.heading * .pi / 180.0
|
||||
alpha = motionData.heading * .pi / 180.0
|
||||
if alpha > .pi {
|
||||
alpha -= 2.0 * .pi
|
||||
} else if alpha < -.pi {
|
||||
alpha += 2.0 * .pi
|
||||
}
|
||||
} else {
|
||||
alpha = data.attitude.yaw
|
||||
alpha = motionData.attitude.yaw
|
||||
}
|
||||
self.webView?.sendEvent(
|
||||
name: "device_orientation_changed",
|
||||
data: "{absolute: \(effectiveIsAbsolute ? "true" : "false"), alpha: \(alpha), beta: \(data.attitude.pitch), gamma: \(data.attitude.roll)}"
|
||||
)
|
||||
|
||||
let data: JSON = [
|
||||
"absolute": effectiveIsAbsolute,
|
||||
"alpha": Double(alpha),
|
||||
"beta": Double(motionData.attitude.pitch),
|
||||
"gamma": Double(motionData.attitude.roll)
|
||||
]
|
||||
self.webView?.sendEvent(name: "device_orientation_changed", data: data.string)
|
||||
}
|
||||
} else {
|
||||
if self.motionManager.isDeviceMotionActive {
|
||||
@ -2536,7 +2535,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
private var isGyroscopeActive = false
|
||||
fileprivate func setIsGyroscopeActive(_ isActive: Bool, refreshRate: Double? = nil) {
|
||||
guard self.motionManager.isGyroAvailable else {
|
||||
self.webView?.sendEvent(name: "gyroscope_failed", data: "{error: \"UNSUPPORTED\"}")
|
||||
let data: JSON = [
|
||||
"error": "UNSUPPORTED"
|
||||
]
|
||||
self.webView?.sendEvent(name: "gyroscope_failed", data: data.string)
|
||||
return
|
||||
}
|
||||
guard self.isGyroscopeActive != isActive else {
|
||||
@ -2551,14 +2553,16 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
} else {
|
||||
self.motionManager.gyroUpdateInterval = 1.0
|
||||
}
|
||||
self.motionManager.startGyroUpdates(to: OperationQueue.main) { [weak self] data, error in
|
||||
guard let self, let data else {
|
||||
self.motionManager.startGyroUpdates(to: OperationQueue.main) { [weak self] gyroData, error in
|
||||
guard let self, let gyroData else {
|
||||
return
|
||||
}
|
||||
self.webView?.sendEvent(
|
||||
name: "gyroscope_changed",
|
||||
data: "{x: \(data.rotationRate.x), y: \(data.rotationRate.y), z: \(data.rotationRate.z)}"
|
||||
)
|
||||
let data: JSON = [
|
||||
"x": Double(gyroData.rotationRate.x),
|
||||
"y": Double(gyroData.rotationRate.y),
|
||||
"z": Double(gyroData.rotationRate.z)
|
||||
]
|
||||
self.webView?.sendEvent(name: "gyroscope_changed", data: data.string)
|
||||
}
|
||||
} else {
|
||||
if self.motionManager.isGyroActive {
|
||||
@ -2575,7 +2579,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
let _ = (self.context.engine.messages.getPreparedInlineMessage(botId: controller.botId, id: id)
|
||||
|> deliverOnMainQueue).start(next: { [weak self, weak controller] preparedMessage in
|
||||
guard let self, let controller, let preparedMessage else {
|
||||
self?.webView?.sendEvent(name: "prepared_message_failed", data: "{error: \"MESSAGE_EXPIRED\"}")
|
||||
let data: JSON = [
|
||||
"error": "MESSAGE_EXPIRED"
|
||||
]
|
||||
self?.webView?.sendEvent(name: "prepared_message_failed", data: data.string)
|
||||
return
|
||||
}
|
||||
let previewController = WebAppMessagePreviewScreen(context: controller.context, botName: controller.botName, botAddress: controller.botAddress, preparedMessage: preparedMessage, completion: { [weak self] result in
|
||||
@ -2585,7 +2592,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
if result {
|
||||
self.webView?.sendEvent(name: "prepared_message_sent", data: nil)
|
||||
} else {
|
||||
self.webView?.sendEvent(name: "prepared_message_failed", data: "{error: \"USER_DECLINED\"}")
|
||||
let data: JSON = [
|
||||
"error": "USER_DECLINED"
|
||||
]
|
||||
self.webView?.sendEvent(name: "prepared_message_failed", data: data.string)
|
||||
}
|
||||
})
|
||||
previewController.navigationPresentation = .flatModal
|
||||
@ -2599,7 +2609,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
|
||||
guard !fileName.contains("/") && fileName.lengthOfBytes(using: .utf8) < 256 && url.lengthOfBytes(using: .utf8) < 32768 else {
|
||||
self.webView?.sendEvent(name: "file_download_requested", data: "{status: \"cancelled\"}")
|
||||
let data: JSON = [
|
||||
"status": "cancelled"
|
||||
]
|
||||
self.webView?.sendEvent(name: "file_download_requested", data: data.string)
|
||||
return
|
||||
}
|
||||
|
||||
@ -2635,7 +2648,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
return
|
||||
}
|
||||
guard canDownload else {
|
||||
self.webView?.sendEvent(name: "file_download_requested", data: "{status: \"cancelled\"}")
|
||||
let data: JSON = [
|
||||
"status": "cancelled"
|
||||
]
|
||||
self.webView?.sendEvent(name: "file_download_requested", data: data.string)
|
||||
return
|
||||
}
|
||||
var fileSizeString = ""
|
||||
@ -2646,14 +2662,20 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
let text: String = self.presentationData.strings.WebApp_Download_Text(controller.botName, fileName, fileSizeString).string
|
||||
let alertController = standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: title, text: text, actions: [
|
||||
TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { [weak self] in
|
||||
self?.webView?.sendEvent(name: "file_download_requested", data: "{status: \"cancelled\"}")
|
||||
let data: JSON = [
|
||||
"status": "cancelled"
|
||||
]
|
||||
self?.webView?.sendEvent(name: "file_download_requested", data: data.string)
|
||||
}),
|
||||
TextAlertAction(type: .defaultAction, title: self.presentationData.strings.WebApp_Download_Download, action: { [weak self] in
|
||||
self?.startDownload(url: url, fileName: fileName, fileSize: fileSize, isMedia: isMedia)
|
||||
})
|
||||
], parseMarkdown: true)
|
||||
alertController.dismissed = { [weak self] byOutsideTap in
|
||||
self?.webView?.sendEvent(name: "file_download_requested", data: "{status: \"cancelled\"}")
|
||||
let data: JSON = [
|
||||
"status": "cancelled"
|
||||
]
|
||||
self?.webView?.sendEvent(name: "file_download_requested", data: data.string)
|
||||
}
|
||||
controller.present(alertController, in: .window(.root))
|
||||
})
|
||||
@ -2664,7 +2686,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
self.webView?.sendEvent(name: "file_download_requested", data: "{status: \"downloading\"}")
|
||||
let data: JSON = [
|
||||
"status": "downloading"
|
||||
]
|
||||
self.webView?.sendEvent(name: "file_download_requested", data: data.string)
|
||||
|
||||
var removeImpl: (() -> Void)?
|
||||
let fileDownload = FileDownload(
|
||||
@ -2840,13 +2865,20 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
demoController?.replace(with: c)
|
||||
}
|
||||
controller.parentController()?.push(demoController)
|
||||
self.webView?.sendEvent(name: "emoji_status_access_requested", data: "{status: \"cancelled\"}")
|
||||
|
||||
let data: JSON = [
|
||||
"status": "cancelled"
|
||||
]
|
||||
self.webView?.sendEvent(name: "emoji_status_access_requested", data: data.string)
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (context.engine.peers.toggleBotEmojiStatusAccess(peerId: botId, enabled: true)
|
||||
|> deliverOnMainQueue).startStandalone(completed: { [weak self] in
|
||||
self?.webView?.sendEvent(name: "emoji_status_access_requested", data: "{status: \"allowed\"}")
|
||||
let data: JSON = [
|
||||
"status": "allowed"
|
||||
]
|
||||
self?.webView?.sendEvent(name: "emoji_status_access_requested", data: data.string)
|
||||
})
|
||||
|
||||
if let botPeer {
|
||||
@ -2865,7 +2897,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
controller.present(resultController, in: .window(.root))
|
||||
}
|
||||
} else {
|
||||
self.webView?.sendEvent(name: "emoji_status_access_requested", data: "{status: \"cancelled\"}")
|
||||
let data: JSON = [
|
||||
"status": "cancelled"
|
||||
]
|
||||
self.webView?.sendEvent(name: "emoji_status_access_requested", data: data.string)
|
||||
}
|
||||
|
||||
let _ = updateWebAppPermissionsStateInteractively(context: context, peerId: botId) { current in
|
||||
@ -2874,7 +2909,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
)
|
||||
alertController.dismissed = { [weak self] byOutsideTap in
|
||||
self?.webView?.sendEvent(name: "emoji_status_access_requested", data: "{status: \"cancelled\"}")
|
||||
let data: JSON = [
|
||||
"status": "cancelled"
|
||||
]
|
||||
self?.webView?.sendEvent(name: "emoji_status_access_requested", data: data.string)
|
||||
}
|
||||
controller.present(alertController, in: .window(.root))
|
||||
})
|
||||
@ -2894,7 +2932,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
return
|
||||
}
|
||||
guard let file = files[fileId] else {
|
||||
self.webView?.sendEvent(name: "emoji_status_failed", data: "{error: \"SUGGESTED_EMOJI_INVALID\"}")
|
||||
let data: JSON = [
|
||||
"error": "SUGGESTED_EMOJI_INVALID"
|
||||
]
|
||||
self.webView?.sendEvent(name: "emoji_status_failed", data: data.string)
|
||||
return
|
||||
}
|
||||
let confirmController = WebAppSetEmojiStatusScreen(
|
||||
@ -2919,7 +2960,11 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
demoController?.replace(with: c)
|
||||
}
|
||||
controller.parentController()?.push(demoController)
|
||||
self.webView?.sendEvent(name: "emoji_status_failed", data: "{error: \"USER_DECLINED\"}")
|
||||
|
||||
let data: JSON = [
|
||||
"error": "USER_DECLINED"
|
||||
]
|
||||
self.webView?.sendEvent(name: "emoji_status_failed", data: data.string)
|
||||
return
|
||||
}
|
||||
|
||||
@ -2951,7 +2996,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
)
|
||||
controller.present(resultController, in: .window(.root))
|
||||
} else {
|
||||
self.webView?.sendEvent(name: "emoji_status_failed", data: "{error: \"USER_DECLINED\"}")
|
||||
let data: JSON = [
|
||||
"error": "USER_DECLINED"
|
||||
]
|
||||
self.webView?.sendEvent(name: "emoji_status_failed", data: data.string)
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -3302,6 +3350,24 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.longTapWithTabBar = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (context.engine.messages.attachMenuBots()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] attachMenuBots in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let attachMenuBot = attachMenuBots.first(where: { $0.peer.id == self.botId && !$0.flags.contains(.notActivated) })
|
||||
if let _ = attachMenuBot, [.attachMenu, .settings, .generic].contains(self.source) {
|
||||
self.removeAttachBot()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
@ -3561,14 +3627,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}, action: { [weak self] c, _ in
|
||||
c?.dismiss(completion: nil)
|
||||
|
||||
if let strongSelf = self {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
strongSelf.present(textAlertController(context: context, title: presentationData.strings.WebApp_RemoveConfirmationTitle, text: presentationData.strings.WebApp_RemoveAllConfirmationText(strongSelf.botName).string, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let _ = context.engine.messages.removeBotFromAttachMenu(botId: strongSelf.botId).start()
|
||||
strongSelf.dismiss()
|
||||
}
|
||||
})], parseMarkdown: true), in: .window(.root))
|
||||
if let self {
|
||||
self.removeAttachBot()
|
||||
}
|
||||
})))
|
||||
}
|
||||
@ -3580,6 +3640,17 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
self.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
private func removeAttachBot() {
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.present(textAlertController(context: context, title: presentationData.strings.WebApp_RemoveConfirmationTitle, text: presentationData.strings.WebApp_RemoveAllConfirmationText(self.botName).string, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self.context.engine.messages.removeBotFromAttachMenu(botId: self.botId).start()
|
||||
self.dismiss()
|
||||
})], parseMarkdown: true), in: .window(.root))
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = Node(context: self.context, controller: self)
|
||||
|
||||
@ -3660,7 +3731,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
self.controllerNode.webView?.setNeedsLayout()
|
||||
}
|
||||
|
||||
self.controllerNode.webView?.sendEvent(name: "visibility_changed", data: "{is_visible: \(self.isMinimized ? "false" : "true")}")
|
||||
let data: JSON = [
|
||||
"is_visible": !self.isMinimized,
|
||||
]
|
||||
self.controllerNode.webView?.sendEvent(name: "visibility_changed", data: data.string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user