mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Web app improvements
This commit is contained in:
parent
bc09798555
commit
e3866ea65b
@ -10411,3 +10411,5 @@ Sorry for the inconvenience.";
|
||||
"ChannelBoost.EnableColors" = "Enable Colors";
|
||||
"ChannelBoost.EnableColorsText" = "Your channel needs %1$@ to change channel color.\n\nAsk your **Premium** subscribers to boost your channel with this link:";
|
||||
"ChannelBoost.BoostAgain" = "Boost Again";
|
||||
|
||||
"Settings.New" = "NEW";
|
||||
|
@ -2421,6 +2421,25 @@ public func chatMessageImageFile(account: Account, userLocation: MediaResourceUs
|
||||
}
|
||||
}
|
||||
|
||||
public func preloadedBotIcon(account: Account, fileReference: FileMediaReference) -> Signal<Bool, NoError> {
|
||||
let signal = Signal<Bool, NoError> { subscriber in
|
||||
let fetched = fetchedMediaResource(mediaBox: account.postbox.mediaBox, userLocation: .other, userContentType: MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(fileReference.media.resource)).start()
|
||||
let dataDisposable = account.postbox.mediaBox.resourceData(fileReference.media.resource, option: .incremental(waitUntilFetchStatus: false)).start(next: { data in
|
||||
if data.complete {
|
||||
subscriber.putNext(true)
|
||||
subscriber.putCompletion()
|
||||
} else {
|
||||
subscriber.putNext(false)
|
||||
}
|
||||
})
|
||||
return ActionDisposable {
|
||||
fetched.dispose()
|
||||
dataDisposable.dispose()
|
||||
}
|
||||
}
|
||||
return signal
|
||||
}
|
||||
|
||||
public func instantPageImageFile(account: Account, userLocation: MediaResourceUserLocation, fileReference: FileMediaReference, fetched: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
return chatMessageFileDatas(account: account, userLocation: userLocation, fileReference: fileReference, progressive: false, fetched: fetched)
|
||||
|> map { value in
|
||||
|
@ -471,7 +471,7 @@ func _internal_acceptAttachMenuBotDisclaimer(postbox: Postbox, botId: PeerId) ->
|
||||
} |> ignoreValues
|
||||
}
|
||||
|
||||
public struct AttachMenuBot {
|
||||
public struct AttachMenuBot: Equatable {
|
||||
public let peer: EnginePeer
|
||||
public let shortName: String
|
||||
public let icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile]
|
||||
|
@ -13551,6 +13551,26 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
completion(controller, controller.mediaPickerContext)
|
||||
strongSelf.controllerNavigationDisposable.set(nil)
|
||||
|
||||
if bot.flags.contains(.notActivated) {
|
||||
let alertController = webAppTermsAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, bot: bot, completion: { [weak self] allowWrite in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if bot.flags.contains(.showInSettingsDisclaimer) {
|
||||
let _ = self.context.engine.messages.acceptAttachMenuBotDisclaimer(botId: bot.peer.id).startStandalone()
|
||||
}
|
||||
let _ = (self.context.engine.messages.addBotToAttachMenu(botId: bot.peer.id, allowWrite: allowWrite)
|
||||
|> deliverOnMainQueue).startStandalone(error: { _ in
|
||||
}, completed: { [weak controller] in
|
||||
controller?.refresh()
|
||||
})
|
||||
},
|
||||
dismissed: {
|
||||
strongSelf.attachmentController?.dismiss(animated: true)
|
||||
})
|
||||
strongSelf.present(alertController, in: .window(.root))
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -459,16 +459,6 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
||||
}
|
||||
chatsNode.updateState { state in
|
||||
var state = state
|
||||
if "".isEmpty {
|
||||
if !state.selectedAdditionalCategoryIds.contains(id) {
|
||||
for id in state.selectedAdditionalCategoryIds {
|
||||
removedTokenIds.append(id)
|
||||
state.selectedAdditionalCategoryIds.remove(id)
|
||||
}
|
||||
state.selectedAdditionalCategoryIds.insert(id)
|
||||
addedToken = categoryToken
|
||||
}
|
||||
} else {
|
||||
if state.selectedAdditionalCategoryIds.contains(id) {
|
||||
state.selectedAdditionalCategoryIds.remove(id)
|
||||
removedTokenIds.append(id)
|
||||
@ -476,7 +466,6 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
||||
state.selectedAdditionalCategoryIds.insert(id)
|
||||
addedToken = categoryToken
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
@ -9,12 +9,13 @@ final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem {
|
||||
case text(String)
|
||||
case badge(String, UIColor)
|
||||
case semitransparentBadge(String, UIColor)
|
||||
case titleBadge(String, UIColor)
|
||||
|
||||
var text: String {
|
||||
switch self {
|
||||
case .none:
|
||||
return ""
|
||||
case let .text(text), let .badge(text, _), let .semitransparentBadge(text, _):
|
||||
case let .text(text), let .badge(text, _), let .semitransparentBadge(text, _), let .titleBadge(text, _):
|
||||
return text
|
||||
}
|
||||
}
|
||||
@ -23,7 +24,7 @@ final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem {
|
||||
switch self {
|
||||
case .none, .text:
|
||||
return nil
|
||||
case let .badge(_, color), let .semitransparentBadge(_, color):
|
||||
case let .badge(_, color), let .semitransparentBadge(_, color), let .titleBadge(_, color):
|
||||
return color
|
||||
}
|
||||
}
|
||||
@ -146,6 +147,9 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
} else if case .badge = item.label {
|
||||
labelColorValue = presentationData.theme.list.itemCheckColors.foregroundColor
|
||||
labelFont = Font.regular(15.0)
|
||||
} else if case .titleBadge = item.label {
|
||||
labelColorValue = presentationData.theme.list.itemCheckColors.foregroundColor
|
||||
labelFont = Font.medium(11.0)
|
||||
} else {
|
||||
labelColorValue = presentationData.theme.list.itemSecondaryTextColor
|
||||
labelFont = titleFont
|
||||
@ -217,6 +221,13 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
if self.labelBadgeNode.supernode == nil {
|
||||
self.insertSubnode(self.labelBadgeNode, belowSubnode: self.labelNode)
|
||||
}
|
||||
} else if case let .titleBadge(text, badgeColor) = item.label, !text.isEmpty {
|
||||
if previousItem?.label.badgeColor != badgeColor {
|
||||
self.labelBadgeNode.image = generateFilledRoundedRectImage(size: CGSize(width: 16.0, height: 16.0), cornerRadius: 5.0, color: badgeColor)?.stretchableImage(withLeftCapWidth: 6, topCapHeight: 6)
|
||||
}
|
||||
if self.labelBadgeNode.supernode == nil {
|
||||
self.insertSubnode(self.labelBadgeNode, belowSubnode: self.labelNode)
|
||||
}
|
||||
} else {
|
||||
self.labelBadgeNode.removeFromSupernode()
|
||||
}
|
||||
@ -230,11 +241,18 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
labelFrame = CGRect(origin: CGPoint(x: width - rightInset - badgeWidth + (badgeWidth - labelSize.width) / 2.0, y: floor((height - labelSize.height) / 2.0)), size: labelSize)
|
||||
} else if case .badge = item.label {
|
||||
labelFrame = CGRect(origin: CGPoint(x: width - rightInset - badgeWidth + (badgeWidth - labelSize.width) / 2.0, y: floor((height - labelSize.height) / 2.0)), size: labelSize)
|
||||
} else if case .titleBadge = item.label {
|
||||
labelFrame = CGRect(origin: CGPoint(x: textFrame.maxX + 10.0, y: floor((height - labelSize.height) / 2.0) + 1.0), size: labelSize)
|
||||
} else {
|
||||
labelFrame = CGRect(origin: CGPoint(x: width - rightInset - labelSize.width, y: 12.0), size: labelSize)
|
||||
}
|
||||
|
||||
let labelBadgeNodeFrame = CGRect(origin: CGPoint(x: width - rightInset - badgeWidth, y: floorToScreenPixels(labelFrame.midY - badgeDiameter / 2.0)), size: CGSize(width: badgeWidth, height: badgeDiameter))
|
||||
let labelBadgeNodeFrame: CGRect
|
||||
if case .titleBadge = item.label {
|
||||
labelBadgeNodeFrame = labelFrame.insetBy(dx: -4.0, dy: -2.0 + UIScreenPixel)
|
||||
} else {
|
||||
labelBadgeNodeFrame = CGRect(origin: CGPoint(x: width - rightInset - badgeWidth, y: floorToScreenPixels(labelFrame.midY - badgeDiameter / 2.0)), size: CGSize(width: badgeWidth, height: badgeDiameter))
|
||||
}
|
||||
|
||||
self.activateArea.accessibilityLabel = item.text
|
||||
self.activateArea.accessibilityValue = item.label.text
|
||||
|
@ -13,6 +13,7 @@ import TelegramNotices
|
||||
import AccountUtils
|
||||
import DeviceAccess
|
||||
import PeerInfoVisualMediaPaneNode
|
||||
import PhotoResources
|
||||
|
||||
enum PeerInfoUpdatingAvatar {
|
||||
case none
|
||||
@ -494,21 +495,57 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
|
||||
|
||||
let botsKey = ValueBoxKey(length: 8)
|
||||
botsKey.setInt64(0, value: 0)
|
||||
|
||||
var iconLoaded: [EnginePeer.Id: Bool] = [:]
|
||||
let bots = context.engine.data.subscribe(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: Namespaces.CachedItemCollection.attachMenuBots, id: botsKey))
|
||||
|> mapToSignal { entry -> Signal<[AttachMenuBot], NoError> in
|
||||
let bots: [AttachMenuBots.Bot] = entry?.get(AttachMenuBots.self)?.bots ?? []
|
||||
return context.engine.data.subscribe(
|
||||
EngineDataMap(bots.map(\.peerId).map(TelegramEngine.EngineData.Item.Peer.Peer.init))
|
||||
)
|
||||
|> map { peersMap -> [AttachMenuBot] in
|
||||
var result: [AttachMenuBot] = []
|
||||
|> mapToSignal { peersMap -> Signal<[AttachMenuBot], NoError> in
|
||||
var result: [Signal<AttachMenuBot?, NoError>] = []
|
||||
for bot in bots {
|
||||
if let maybePeer = peersMap[bot.peerId], let peer = maybePeer {
|
||||
result.append(AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons, peerTypes: bot.peerTypes, flags: bot.flags))
|
||||
let resultBot = AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons, peerTypes: bot.peerTypes, flags: bot.flags)
|
||||
if bot.flags.contains(.showInSettings) {
|
||||
if let peer = PeerReference(peer._asPeer()), let icon = bot.icons[.iOSSettingsStatic] {
|
||||
let fileReference: FileMediaReference = .attachBot(peer: peer, media: icon)
|
||||
let signal: Signal<AttachMenuBot?, NoError>
|
||||
if let _ = iconLoaded[peer.id] {
|
||||
signal = .single(resultBot)
|
||||
} else {
|
||||
signal = .single(nil)
|
||||
|> then(
|
||||
preloadedBotIcon(account: context.account, fileReference: fileReference)
|
||||
|> filter { $0 }
|
||||
|> map { _ -> AttachMenuBot? in
|
||||
return resultBot
|
||||
}
|
||||
|> afterNext { _ in
|
||||
iconLoaded[peer.id] = true
|
||||
}
|
||||
)
|
||||
}
|
||||
result.append(signal)
|
||||
} else {
|
||||
result.append(.single(resultBot))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return combineLatest(result)
|
||||
|> map { bots in
|
||||
var result: [AttachMenuBot] = []
|
||||
for bot in bots {
|
||||
if let bot {
|
||||
result.append(bot)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
}
|
||||
}
|
||||
|
||||
return combineLatest(
|
||||
|
@ -818,7 +818,6 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
|
||||
var appIndex = 1000
|
||||
if let settings = data.globalSettings {
|
||||
for bot in settings.bots {
|
||||
if bot.flags.contains(.showInSettings) {
|
||||
let iconSignal: Signal<UIImage?, NoError>
|
||||
if let peer = PeerReference(bot.peer._asPeer()), let icon = bot.icons[.iOSSettingsStatic] {
|
||||
let fileReference: FileMediaReference = .attachBot(peer: peer, media: icon)
|
||||
@ -832,13 +831,13 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
|
||||
} else {
|
||||
iconSignal = .single(UIImage(bundleImageName: "Settings/Menu/Websites")!)
|
||||
}
|
||||
items[.apps]!.append(PeerInfoScreenDisclosureItem(id: bot.peer.id.id._internalGetInt64Value(), text: bot.shortName, icon: nil, iconSignal: iconSignal, action: {
|
||||
let label: PeerInfoScreenDisclosureItem.Label = bot.flags.contains(.notActivated) || bot.flags.contains(.showInSettingsDisclaimer) ? .titleBadge(presentationData.strings.Settings_New, presentationData.theme.list.itemAccentColor) : .none
|
||||
items[.apps]!.append(PeerInfoScreenDisclosureItem(id: bot.peer.id.id._internalGetInt64Value(), label: label, text: bot.shortName, icon: nil, iconSignal: iconSignal, action: {
|
||||
interaction.openBotApp(bot)
|
||||
}))
|
||||
appIndex += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
items[.apps]!.append(PeerInfoScreenDisclosureItem(id: 0, text: presentationData.strings.Settings_MyStories, icon: PresentationResourcesSettings.stories, action: {
|
||||
interaction.openSettings(.stories)
|
||||
@ -4756,6 +4755,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let showInstalledTooltip = !bot.flags.contains(.showInSettingsDisclaimer)
|
||||
if bot.flags.contains(.showInSettingsDisclaimer) {
|
||||
let _ = self.context.engine.messages.acceptAttachMenuBotDisclaimer(botId: bot.peer.id).startStandalone()
|
||||
}
|
||||
@ -4763,7 +4763,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
let _ = (self.context.engine.messages.addBotToAttachMenu(botId: bot.peer.id, allowWrite: allowWrite)
|
||||
|> deliverOnMainQueue).startStandalone(error: { _ in
|
||||
}, completed: {
|
||||
proceed(true)
|
||||
proceed(showInstalledTooltip)
|
||||
})
|
||||
} else {
|
||||
proceed(false)
|
||||
|
@ -413,6 +413,32 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
})
|
||||
})
|
||||
|
||||
self.setupWebView()
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.placeholderDisposable?.dispose()
|
||||
self.iconDisposable?.dispose()
|
||||
self.keepAliveDisposable?.dispose()
|
||||
self.paymentDisposable?.dispose()
|
||||
|
||||
self.webView?.removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress))
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
guard let webView = self.webView else {
|
||||
return
|
||||
}
|
||||
self.view.addSubview(webView)
|
||||
webView.scrollView.insertSubview(self.topOverscrollNode.view, at: 0)
|
||||
}
|
||||
|
||||
func setupWebView() {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
if let url = controller.url, controller.source != .menu {
|
||||
self.queryId = controller.queryId
|
||||
if let parsedUrl = URL(string: url) {
|
||||
@ -433,7 +459,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
} else {
|
||||
if controller.source.isSimple {
|
||||
let _ = (context.engine.messages.requestSimpleWebView(botId: controller.botId, url: nil, source: .settings, themeParams: generateWebAppThemeParams(presentationData.theme))
|
||||
let _ = (self.context.engine.messages.requestSimpleWebView(botId: controller.botId, url: nil, source: .settings, themeParams: generateWebAppThemeParams(presentationData.theme))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -443,7 +469,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
})
|
||||
} else {
|
||||
let _ = (context.engine.messages.requestWebView(peerId: controller.peerId, botId: controller.botId, url: controller.url, payload: controller.payload, themeParams: generateWebAppThemeParams(presentationData.theme), fromMenu: controller.source == .menu, replyToMessageId: controller.replyToMessageId, threadId: controller.threadId)
|
||||
let _ = (self.context.engine.messages.requestWebView(peerId: controller.peerId, botId: controller.botId, url: controller.url, payload: controller.payload, themeParams: generateWebAppThemeParams(presentationData.theme), fromMenu: controller.source == .menu, replyToMessageId: controller.replyToMessageId, threadId: controller.threadId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -469,25 +495,6 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.placeholderDisposable?.dispose()
|
||||
self.iconDisposable?.dispose()
|
||||
self.keepAliveDisposable?.dispose()
|
||||
self.paymentDisposable?.dispose()
|
||||
|
||||
self.webView?.removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress))
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
guard let webView = self.webView else {
|
||||
return
|
||||
}
|
||||
self.view.addSubview(webView)
|
||||
webView.scrollView.insertSubview(self.topOverscrollNode.view, at: 0)
|
||||
}
|
||||
|
||||
@objc fileprivate func mainButtonPressed() {
|
||||
if let mainButtonState = self.mainButtonState, !mainButtonState.isVisible || !mainButtonState.isEnabled {
|
||||
return
|
||||
@ -1624,6 +1631,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
self.updateTabBarAlpha(1.0, .immediate)
|
||||
}
|
||||
|
||||
public func refresh() {
|
||||
self.controllerNode.setupWebView()
|
||||
}
|
||||
|
||||
public func requestDismiss(completion: @escaping () -> Void) {
|
||||
if self.controllerNode.needDismissConfirmation {
|
||||
let actionSheet = ActionSheetController(presentationData: self.presentationData)
|
||||
|
@ -353,7 +353,8 @@ public func webAppTermsAlertController(
|
||||
context: AccountContext,
|
||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
|
||||
bot: AttachMenuBot,
|
||||
completion: @escaping (Bool) -> Void
|
||||
completion: @escaping (Bool) -> Void,
|
||||
dismissed: @escaping () -> Void = {}
|
||||
) -> AlertController {
|
||||
let theme = defaultDarkColorPresentationTheme
|
||||
let presentationData: PresentationData
|
||||
@ -369,6 +370,7 @@ public func webAppTermsAlertController(
|
||||
completion(true)
|
||||
dismissImpl?(true)
|
||||
}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
dismissed()
|
||||
dismissImpl?(true)
|
||||
})]
|
||||
|
||||
@ -382,6 +384,11 @@ public func webAppTermsAlertController(
|
||||
})
|
||||
}
|
||||
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
|
||||
controller.dismissed = { outside in
|
||||
if outside {
|
||||
dismissed()
|
||||
}
|
||||
}
|
||||
dismissImpl = { [weak controller] animated in
|
||||
if animated {
|
||||
controller?.dismissAnimated()
|
||||
|
Loading…
x
Reference in New Issue
Block a user