Web app improvements

This commit is contained in:
Ilya Laktyushin 2023-10-27 03:21:21 +04:00
parent bc09798555
commit e3866ea65b
10 changed files with 168 additions and 65 deletions

View File

@ -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";

View File

@ -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

View File

@ -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]

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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(

View File

@ -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)

View File

@ -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)

View File

@ -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()