mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
3ed05bc39d
commit
3fd2bf7f2f
@ -506,6 +506,8 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
let containerFrame: CGRect
|
||||
let clipFrame: CGRect
|
||||
let containerScale: CGFloat
|
||||
|
||||
let isFullscreen = controllers.last?.isFullscreen == true
|
||||
if case .compact = layout.metrics.widthClass {
|
||||
self.clipNode.clipsToBounds = true
|
||||
|
||||
@ -524,7 +526,7 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
var containerTopInset: CGFloat
|
||||
if isLandscape || controllers.last?.isFullscreen == true {
|
||||
if isLandscape || isFullscreen {
|
||||
containerTopInset = 0.0
|
||||
containerLayout = layout
|
||||
|
||||
@ -560,7 +562,7 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
clipFrame = CGRect(x: containerFrame.minX + overflowInset, y: containerFrame.minY, width: containerFrame.width - overflowInset * 2.0, height: containerFrame.height)
|
||||
}
|
||||
} else {
|
||||
containerLayout = ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: layout.intrinsicInsets.bottom, right: 0.0), safeInsets: UIEdgeInsets(), additionalInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: layout.inVoiceOver)
|
||||
containerLayout = ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: layout.intrinsicInsets.bottom, right: 0.0), safeInsets: UIEdgeInsets(), additionalInsets: UIEdgeInsets(), statusBarHeight: isFullscreen ? layout.statusBarHeight : nil, inputHeight: isFullscreen ? layout.inputHeight : nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: layout.inVoiceOver)
|
||||
|
||||
let unscaledFrame = CGRect(origin: CGPoint(), size: containerLayout.size)
|
||||
containerScale = 1.0
|
||||
|
@ -1057,50 +1057,57 @@ public class AttachmentController: ViewController, MinimizableController {
|
||||
var containerLayout = layout
|
||||
let containerRect: CGRect
|
||||
if case .regular = layout.metrics.widthClass {
|
||||
let availableHeight = layout.size.height - (layout.inputHeight ?? 0.0) - 60.0
|
||||
|
||||
let size = CGSize(width: 390.0, height: min(620.0, availableHeight))
|
||||
|
||||
let insets = layout.insets(options: [.input])
|
||||
let masterWidth = min(max(320.0, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0))
|
||||
|
||||
let position: CGPoint
|
||||
let positionY = layout.size.height - size.height - insets.bottom - 40.0
|
||||
if let sourceRect = controller.getSourceRect?() {
|
||||
position = CGPoint(x: min(layout.size.width - size.width - 28.0, floor(sourceRect.midX - size.width / 2.0)), y: min(positionY, sourceRect.minY - size.height))
|
||||
if controller.isFullscreen {
|
||||
containerRect = CGRect(origin: .zero, size: layout.size)
|
||||
self.wrapperNode.cornerRadius = 0.0
|
||||
self.wrapperNode.view.mask = nil
|
||||
self.shadowNode.alpha = 0.0
|
||||
} else {
|
||||
position = CGPoint(x: masterWidth - 174.0, y: positionY)
|
||||
}
|
||||
|
||||
if controller.isStandalone && !controller.forceSourceRect {
|
||||
var containerY = floorToScreenPixels((layout.size.height - size.height) / 2.0)
|
||||
if let inputHeight = layout.inputHeight, inputHeight > 88.0 {
|
||||
containerY = layout.size.height - inputHeight - size.height - 80.0
|
||||
let availableHeight = layout.size.height - (layout.inputHeight ?? 0.0) - 60.0
|
||||
|
||||
let size = CGSize(width: 390.0, height: min(620.0, availableHeight))
|
||||
|
||||
let insets = layout.insets(options: [.input])
|
||||
let masterWidth = min(max(320.0, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0))
|
||||
|
||||
let position: CGPoint
|
||||
let positionY = layout.size.height - size.height - insets.bottom - 40.0
|
||||
if let sourceRect = controller.getSourceRect?() {
|
||||
position = CGPoint(x: min(layout.size.width - size.width - 28.0, floor(sourceRect.midX - size.width / 2.0)), y: min(positionY, sourceRect.minY - size.height))
|
||||
} else {
|
||||
position = CGPoint(x: masterWidth - 174.0, y: positionY)
|
||||
}
|
||||
|
||||
if controller.isStandalone && !controller.forceSourceRect {
|
||||
var containerY = floorToScreenPixels((layout.size.height - size.height) / 2.0)
|
||||
if let inputHeight = layout.inputHeight, inputHeight > 88.0 {
|
||||
containerY = layout.size.height - inputHeight - size.height - 80.0
|
||||
}
|
||||
containerRect = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - size.width) / 2.0), y: containerY), size: size)
|
||||
} else {
|
||||
containerRect = CGRect(origin: position, size: size)
|
||||
}
|
||||
containerLayout.size = containerRect.size
|
||||
containerLayout.intrinsicInsets.bottom = 12.0
|
||||
containerLayout.inputHeight = nil
|
||||
|
||||
if controller.isStandalone {
|
||||
self.wrapperNode.cornerRadius = 10.0
|
||||
} else if self.wrapperNode.view.mask == nil {
|
||||
let maskView = UIImageView()
|
||||
maskView.image = generateMaskImage()
|
||||
maskView.contentMode = .scaleToFill
|
||||
self.wrapperNode.view.mask = maskView
|
||||
}
|
||||
|
||||
if let maskView = self.wrapperNode.view.mask {
|
||||
transition.updateFrame(view: maskView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
|
||||
self.shadowNode.alpha = 1.0
|
||||
if self.shadowNode.image == nil {
|
||||
self.shadowNode.image = generateShadowImage()
|
||||
}
|
||||
containerRect = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - size.width) / 2.0), y: containerY), size: size)
|
||||
} else {
|
||||
containerRect = CGRect(origin: position, size: size)
|
||||
}
|
||||
containerLayout.size = containerRect.size
|
||||
containerLayout.intrinsicInsets.bottom = 12.0
|
||||
containerLayout.inputHeight = nil
|
||||
|
||||
if controller.isStandalone {
|
||||
self.wrapperNode.cornerRadius = 10.0
|
||||
} else if self.wrapperNode.view.mask == nil {
|
||||
let maskView = UIImageView()
|
||||
maskView.image = generateMaskImage()
|
||||
maskView.contentMode = .scaleToFill
|
||||
self.wrapperNode.view.mask = maskView
|
||||
}
|
||||
|
||||
if let maskView = self.wrapperNode.view.mask {
|
||||
transition.updateFrame(view: maskView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
|
||||
self.shadowNode.alpha = 1.0
|
||||
if self.shadowNode.image == nil {
|
||||
self.shadowNode.image = generateShadowImage()
|
||||
}
|
||||
} else {
|
||||
let containerHeight: CGFloat
|
||||
|
@ -167,6 +167,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
let sideInset: CGFloat = params.leftInset + 16.0
|
||||
let rightInset: CGFloat = sideInset + 24.0
|
||||
var titleRightInset = rightInset
|
||||
let verticalInset: CGFloat = 9.0
|
||||
var spacing: CGFloat = 0.0
|
||||
|
||||
@ -214,6 +215,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
||||
titleString = titleStringValue
|
||||
|
||||
textString = NSAttributedString(string: item.strings.ChatList_PremiumAnnualDiscountText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
|
||||
titleRightInset = sideInset
|
||||
case let .premiumRestore(discount):
|
||||
let discountString = "\(discount)%"
|
||||
let rawTitleString = item.strings.ChatList_PremiumRestoreDiscountTitle(discountString)
|
||||
@ -291,7 +293,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
||||
leftInset += avatarsWidth + 4.0
|
||||
}
|
||||
|
||||
let titleLayout = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset, height: 100.0), alignment: alignment, lineSpacing: 0.18))
|
||||
let titleLayout = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - titleRightInset, height: 100.0), alignment: alignment, lineSpacing: 0.18))
|
||||
|
||||
let textLayout = makeTextLayout(TextNodeLayoutArguments(attributedString: textString, maximumNumberOfLines: 10, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset, height: 100.0), alignment: alignment, lineSpacing: 0.18))
|
||||
|
||||
|
@ -1545,23 +1545,33 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
|
||||
if let _ = user.botInfo {
|
||||
//TODO:localize
|
||||
items[.permissions]!.append(PeerInfoScreenHeaderItem(id: 30, text: "ALLOW ACCESS TO"))
|
||||
var canManageEmojiStatus = false
|
||||
if let cachedData = data.cachedData as? CachedUserData, cachedData.flags.contains(.botCanManageEmojiStatus) {
|
||||
canManageEmojiStatus = true
|
||||
}
|
||||
items[.permissions]!.append(PeerInfoScreenSwitchItem(id: 31, text: "Emoji Status", value: canManageEmojiStatus, icon: UIImage(bundleImageName: "Chat/Info/Status"), isLocked: false, toggled: { value in
|
||||
let _ = (context.engine.peers.toggleBotEmojiStatusAccess(peerId: user.id, enabled: value)
|
||||
|> deliverOnMainQueue).startStandalone()
|
||||
}))
|
||||
|
||||
if canManageEmojiStatus || data.webAppPermissions?.emojiStatus?.isRequested == true {
|
||||
items[.permissions]!.append(PeerInfoScreenSwitchItem(id: 31, text: "Emoji Status", value: canManageEmojiStatus, icon: UIImage(bundleImageName: "Chat/Info/Status"), isLocked: false, toggled: { value in
|
||||
let _ = (context.engine.peers.toggleBotEmojiStatusAccess(peerId: user.id, enabled: value)
|
||||
|> deliverOnMainQueue).startStandalone()
|
||||
|
||||
let _ = updateWebAppPermissionsStateInteractively(context: context, peerId: user.id) { current in
|
||||
return WebAppPermissionsState(location: current?.location, emojiStatus: WebAppPermissionsState.EmojiStatus(isRequested: true))
|
||||
}.startStandalone()
|
||||
}))
|
||||
}
|
||||
|
||||
if data.webAppPermissions?.location?.isRequested == true || data.webAppPermissions?.location?.isAllowed == true {
|
||||
items[.permissions]!.append(PeerInfoScreenSwitchItem(id: 32, text: "Geolocation", value: data.webAppPermissions?.location?.isAllowed ?? false, icon: UIImage(bundleImageName: "Chat/Info/Location"), isLocked: false, toggled: { value in
|
||||
let _ = updateWebAppPermissionsStateInteractively(context: context, peerId: user.id) { current in
|
||||
return WebAppPermissionsState(location: WebAppPermissionsState.Location(isRequested: true, isAllowed: value))
|
||||
return WebAppPermissionsState(location: WebAppPermissionsState.Location(isRequested: true, isAllowed: value), emojiStatus: current?.emojiStatus)
|
||||
}.startStandalone()
|
||||
}))
|
||||
}
|
||||
|
||||
if !items[.permissions]!.isEmpty {
|
||||
items[.permissions]!.insert(PeerInfoScreenHeaderItem(id: 30, text: "ALLOW ACCESS TO"), at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) {
|
||||
|
@ -222,7 +222,7 @@ func openWebAppImpl(
|
||||
} else {
|
||||
source = url.isEmpty ? .generic : .simple
|
||||
}
|
||||
let params = WebAppParameters(source: source, peerId: chatPeer?.id ?? botId, botId: botId, botName: botName, botVerified: botVerified, botAddress: botPeer.addressName ?? "", appName: nil, url: result.url, queryId: nil, payload: payload, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false, fullSize: result.flags.contains(.fullSize), isFullscreen: result.flags.contains(.fullScreen), appSettings: appSettings)
|
||||
let params = WebAppParameters(source: source, peerId: chatPeer?.id ?? botId, botId: botId, botName: botName, botVerified: botVerified, botAddress: botPeer.addressName ?? "", appName: "", url: result.url, queryId: nil, payload: payload, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false, fullSize: result.flags.contains(.fullSize), isFullscreen: result.flags.contains(.fullScreen), appSettings: appSettings)
|
||||
let controller = standaloneWebAppController(context: context, updatedPresentationData: updatedPresentationData, params: params, threadId: threadId, openUrl: { [weak parentController] url, concealed, forceUpdate, commit in
|
||||
ChatControllerImpl.botOpenUrl(context: context, peerId: chatPeer?.id ?? botId, controller: parentController as? ChatControllerImpl, url: url, concealed: concealed, forceUpdate: forceUpdate, present: { c, a in
|
||||
presentImpl?(c, a)
|
||||
|
@ -1423,7 +1423,11 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
case "web_app_check_location":
|
||||
self.checkLocation()
|
||||
case "web_app_open_location_settings":
|
||||
break
|
||||
if let lastTouchTimestamp = self.webView?.lastTouchTimestamp, currentTimestamp < lastTouchTimestamp + 10.0 {
|
||||
self.webView?.lastTouchTimestamp = nil
|
||||
|
||||
self.openLocationSettings()
|
||||
}
|
||||
case "web_app_send_prepared_message":
|
||||
if let json = json, let id = json["id"] as? String {
|
||||
self.sendPreparedMessage(id: id)
|
||||
@ -2409,10 +2413,9 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
guard let self, let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
let context = self.context
|
||||
let botId = controller.botId
|
||||
if result {
|
||||
let context = self.context
|
||||
let botId = controller.botId
|
||||
|
||||
if !context.isPremium {
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let demoController = context.sharedContext.makePremiumDemoController(context: context, subject: .emojiStatus, forceDark: false, action: {
|
||||
@ -2431,7 +2434,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
|> deliverOnMainQueue).startStandalone(completed: { [weak self] in
|
||||
self?.webView?.sendEvent(name: "emoji_status_access_requested", data: "{status: \"allowed\"}")
|
||||
})
|
||||
|
||||
|
||||
//TODO:localize
|
||||
if let botPeer {
|
||||
let resultController = UndoOverlayController(
|
||||
@ -2451,6 +2454,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
} else {
|
||||
self.webView?.sendEvent(name: "emoji_status_access_requested", data: "{status: \"cancelled\"}")
|
||||
}
|
||||
|
||||
let _ = updateWebAppPermissionsStateInteractively(context: context, peerId: botId) { current in
|
||||
return WebAppPermissionsState(location: current?.location, emojiStatus: WebAppPermissionsState.EmojiStatus(isRequested: true))
|
||||
}.startStandalone()
|
||||
}
|
||||
)
|
||||
alertController.dismissed = { [weak self] byOutsideTap in
|
||||
@ -2550,6 +2557,21 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
})
|
||||
}
|
||||
|
||||
fileprivate func openLocationSettings() {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: controller.botId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let self, let controller = self.controller, let peer else {
|
||||
return
|
||||
}
|
||||
if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
controller.parentController()?.push(infoController)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fileprivate func checkLocation() {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
@ -2671,7 +2693,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
self.webView?.sendEvent(name: "location_requested", data: JSON(dictionary: data)?.string)
|
||||
}
|
||||
let _ = updateWebAppPermissionsStateInteractively(context: context, peerId: botId) { current in
|
||||
return WebAppPermissionsState(location: WebAppPermissionsState.Location(isRequested: true, isAllowed: result))
|
||||
return WebAppPermissionsState(location: WebAppPermissionsState.Location(isRequested: true, isAllowed: result), emojiStatus: current?.emojiStatus)
|
||||
}.start()
|
||||
}
|
||||
)
|
||||
@ -2960,6 +2982,17 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
|
||||
self?.controllerNode.webView?.reload()
|
||||
})))
|
||||
|
||||
//TODO:localize
|
||||
if let _ = self?.appName {
|
||||
items.append(.action(ContextMenuActionItem(text: "Add to Home Screen", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddCircle"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] c, _ in
|
||||
c?.dismiss(completion: nil)
|
||||
|
||||
self?.controllerNode.addToHomeScreen()
|
||||
})))
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.WebApp_TermsOfUse, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor)
|
||||
@ -3002,18 +3035,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: self.presentationData.strings.WebApp_PrivacyPolicy_URL, forceExternal: false, presentationData: self.presentationData, navigationController: self.getNavigationController(), dismissInput: {})
|
||||
}
|
||||
})))
|
||||
|
||||
#if DEBUG
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Add to Home Screen", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddCircle"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] c, _ in
|
||||
c?.dismiss(completion: nil)
|
||||
|
||||
self?.controllerNode.addToHomeScreen()
|
||||
})))
|
||||
#endif
|
||||
|
||||
|
||||
if let _ = attachMenuBot, [.attachMenu, .settings, .generic].contains(source) {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.WebApp_RemoveBot, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
|
||||
|
@ -8,6 +8,7 @@ import TelegramUIPreferences
|
||||
public struct WebAppPermissionsState: Codable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case location
|
||||
case emojiStatus
|
||||
}
|
||||
|
||||
public struct Location: Codable {
|
||||
@ -41,25 +42,56 @@ public struct WebAppPermissionsState: Codable {
|
||||
try container.encode(self.isAllowed, forKey: .isAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
public struct EmojiStatus: Codable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case isRequested
|
||||
}
|
||||
|
||||
public let isRequested: Bool
|
||||
|
||||
public init(
|
||||
isRequested: Bool
|
||||
) {
|
||||
self.isRequested = isRequested
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.isRequested = try container.decode(Bool.self, forKey: .isRequested)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encode(self.isRequested, forKey: .isRequested)
|
||||
}
|
||||
}
|
||||
|
||||
public let location: Location?
|
||||
public let emojiStatus: EmojiStatus?
|
||||
|
||||
public init(
|
||||
location: Location?
|
||||
location: Location?,
|
||||
emojiStatus: EmojiStatus?
|
||||
) {
|
||||
self.location = location
|
||||
self.emojiStatus = emojiStatus
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.location = try container.decode(WebAppPermissionsState.Location.self, forKey: .location)
|
||||
self.location = try container.decodeIfPresent(WebAppPermissionsState.Location.self, forKey: .location)
|
||||
self.emojiStatus = try container.decodeIfPresent(WebAppPermissionsState.EmojiStatus.self, forKey: .emojiStatus)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encodeIfPresent(self.location, forKey: .location)
|
||||
try container.encodeIfPresent(self.emojiStatus, forKey: .emojiStatus)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user