Merge commit '11a4dbf0a0'
This commit is contained in:
commit
cfe335d124
13 changed files with 284 additions and 209 deletions
|
|
@ -16342,3 +16342,16 @@ Error: %8$@";
|
|||
|
||||
"MediaPicker.SetNewGroupPhoto" = "Set new group photo";
|
||||
"MediaPicker.SetNewChannelPhoto" = "Set new channel photo";
|
||||
|
||||
"Attachment.Link" = "Link";
|
||||
|
||||
"Checkout.PaymentMethod.Proceed" = "Proceed";
|
||||
"Checkout.ShippingMethod.Proceed" = "Proceed";
|
||||
|
||||
"OpenLinkConfirmation.Title" = "Open link?";
|
||||
"OpenLinkConfirmation.Open" = "Open";
|
||||
|
||||
"Chat.ContextMenu.OpenInBrowser" = "Open in Browser";
|
||||
"Chat.ContextMenu.OpenInApp" = "Open In-App";
|
||||
|
||||
"CreatePoll.Link.Description" = "Attach a link to this option.";
|
||||
|
|
|
|||
|
|
@ -244,8 +244,7 @@ private final class AttachButtonComponent: CombinedComponent {
|
|||
name = strings.Attachment_Audio
|
||||
imageName = "Chat/Attach Menu/Audio"
|
||||
case .link:
|
||||
//TODO:localize
|
||||
name = "Link"
|
||||
name = strings.Attachment_Link
|
||||
imageName = "Chat/Attach Menu/Link"
|
||||
case let .app(bot):
|
||||
botPeer = bot.peer
|
||||
|
|
@ -2187,8 +2186,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate, ASGestureRecog
|
|||
case .audio:
|
||||
accessibilityTitle = self.presentationData.strings.Attachment_Audio
|
||||
case .link:
|
||||
//TODO:localize
|
||||
accessibilityTitle = "Link"
|
||||
accessibilityTitle = self.presentationData.strings.Attachment_Link
|
||||
case let .app(bot):
|
||||
accessibilityTitle = bot.shortName
|
||||
case .standalone:
|
||||
|
|
|
|||
|
|
@ -328,9 +328,9 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
|
|||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let environmentValue = environment[ViewControllerComponentContainer.Environment.self].value
|
||||
let controller = environmentValue.controller
|
||||
let theme = environmentValue.theme.withModalBlocksBackground()
|
||||
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
||||
let controller = environment.controller
|
||||
let theme = environment.theme.withModalBlocksBackground()
|
||||
|
||||
let dismiss: (Bool, (() -> Void)?) -> Void = { [weak self] animated, completion in
|
||||
guard let self, !self.isDismissing else {
|
||||
|
|
@ -379,7 +379,7 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
|
|||
)),
|
||||
titleItem: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: environmentValue.strings.Checkout_PaymentMethod,
|
||||
string: environment.strings.Checkout_PaymentMethod,
|
||||
font: Font.semibold(17.0),
|
||||
textColor: theme.list.itemPrimaryTextColor
|
||||
)),
|
||||
|
|
@ -402,7 +402,6 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
|
|||
}
|
||||
)
|
||||
),
|
||||
//TODO:localize
|
||||
bottomItem: AnyComponent(ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
style: .glass,
|
||||
|
|
@ -413,7 +412,7 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
|
|||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable("proceed"),
|
||||
component: AnyComponent(ButtonTextContentComponent(
|
||||
text: "Proceed",
|
||||
text: environment.strings.Checkout_PaymentMethod_Proceed,
|
||||
badge: 0,
|
||||
textColor: theme.list.itemCheckColors.foregroundColor,
|
||||
badgeBackground: theme.list.itemCheckColors.foregroundColor,
|
||||
|
|
@ -440,16 +439,16 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
|
|||
animateOut: self.animateOut
|
||||
)),
|
||||
environment: {
|
||||
environmentValue
|
||||
environment
|
||||
ResizableSheetComponentEnvironment(
|
||||
theme: theme,
|
||||
statusBarHeight: environmentValue.statusBarHeight,
|
||||
safeInsets: environmentValue.safeInsets,
|
||||
statusBarHeight: environment.statusBarHeight,
|
||||
safeInsets: environment.safeInsets,
|
||||
inputHeight: 0.0,
|
||||
metrics: environmentValue.metrics,
|
||||
deviceMetrics: environmentValue.deviceMetrics,
|
||||
isDisplaying: environmentValue.isVisible,
|
||||
isCentered: environmentValue.metrics.widthClass == .regular,
|
||||
metrics: environment.metrics,
|
||||
deviceMetrics: environment.deviceMetrics,
|
||||
isDisplaying: environment.isVisible,
|
||||
isCentered: environment.metrics.widthClass == .regular,
|
||||
screenSize: availableSize,
|
||||
regularMetricsSize: nil,
|
||||
dismiss: { animated in
|
||||
|
|
|
|||
|
|
@ -225,9 +225,9 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
|
|||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let environmentValue = environment[ViewControllerComponentContainer.Environment.self].value
|
||||
let controller = environmentValue.controller
|
||||
let theme = environmentValue.theme.withModalBlocksBackground()
|
||||
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
||||
let controller = environment.controller
|
||||
let theme = environment.theme.withModalBlocksBackground()
|
||||
|
||||
let dismiss: (Bool) -> Void = { [weak self] animated in
|
||||
guard let self, !self.isDismissing else {
|
||||
|
|
@ -268,7 +268,7 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
|
|||
)),
|
||||
titleItem: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: environmentValue.strings.Checkout_ShippingMethod,
|
||||
string: environment.strings.Checkout_ShippingMethod,
|
||||
font: Font.semibold(17.0),
|
||||
textColor: theme.list.itemPrimaryTextColor
|
||||
)),
|
||||
|
|
@ -291,7 +291,6 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
|
|||
}
|
||||
)
|
||||
),
|
||||
//TODO:localize
|
||||
bottomItem: AnyComponent(ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
style: .glass,
|
||||
|
|
@ -302,7 +301,7 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
|
|||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable("proceed"),
|
||||
component: AnyComponent(ButtonTextContentComponent(
|
||||
text: "Proceed",
|
||||
text: environment.strings.Checkout_ShippingMethod_Proceed,
|
||||
badge: 0,
|
||||
textColor: theme.list.itemCheckColors.foregroundColor,
|
||||
badgeBackground: theme.list.itemCheckColors.foregroundColor,
|
||||
|
|
@ -323,16 +322,16 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
|
|||
animateOut: self.animateOut
|
||||
)),
|
||||
environment: {
|
||||
environmentValue
|
||||
environment
|
||||
ResizableSheetComponentEnvironment(
|
||||
theme: theme,
|
||||
statusBarHeight: environmentValue.statusBarHeight,
|
||||
safeInsets: environmentValue.safeInsets,
|
||||
statusBarHeight: environment.statusBarHeight,
|
||||
safeInsets: environment.safeInsets,
|
||||
inputHeight: 0.0,
|
||||
metrics: environmentValue.metrics,
|
||||
deviceMetrics: environmentValue.deviceMetrics,
|
||||
isDisplaying: environmentValue.isVisible,
|
||||
isCentered: environmentValue.metrics.widthClass == .regular,
|
||||
metrics: environment.metrics,
|
||||
deviceMetrics: environment.deviceMetrics,
|
||||
isDisplaying: environment.isVisible,
|
||||
isCentered: environment.metrics.widthClass == .regular,
|
||||
screenSize: availableSize,
|
||||
regularMetricsSize: nil,
|
||||
dismiss: { animated in
|
||||
|
|
|
|||
|
|
@ -5900,8 +5900,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||
}
|
||||
actions.append(.init(title: self.presentationData.strings.Common_Cancel))
|
||||
|
||||
//TODO:localize
|
||||
let title: String = "Delete Chat"
|
||||
let title: String = self.presentationData.strings.ChatList_DeleteChat
|
||||
var text: String
|
||||
if mainPeer.id == self.context.account.peerId {
|
||||
text = self.presentationData.strings.ChatList_DeleteSavedMessagesConfirmation
|
||||
|
|
@ -5948,56 +5947,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||
],
|
||||
actions: actions
|
||||
)
|
||||
|
||||
|
||||
// let actionSheet = ActionSheetController(presentationData: self.presentationData)
|
||||
// var items: [ActionSheetItem] = []
|
||||
//
|
||||
// items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .delete, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder))
|
||||
//
|
||||
// if joined || mainPeer.isDeleted {
|
||||
// items.append(ActionSheetButtonItem(title: self.presentationData.strings.Common_Delete, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
// actionSheet?.dismissAnimated()
|
||||
// self?.schedulePeerChatRemoval(peer: peer, type: .forEveryone, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
|
||||
// removed()
|
||||
// })
|
||||
// completion(true)
|
||||
// }))
|
||||
// } else {
|
||||
// items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
// actionSheet?.dismissAnimated()
|
||||
// self?.schedulePeerChatRemoval(peer: peer, type: .forLocalPeer, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
|
||||
// removed()
|
||||
// })
|
||||
// completion(true)
|
||||
// }))
|
||||
// items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).string, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
// actionSheet?.dismissAnimated()
|
||||
// guard let strongSelf = self else {
|
||||
// return
|
||||
// }
|
||||
// strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationText, actions: [
|
||||
// TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
// completion(false)
|
||||
// }),
|
||||
// TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: {
|
||||
// self?.schedulePeerChatRemoval(peer: peer, type: .forEveryone, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
|
||||
// removed()
|
||||
// })
|
||||
// completion(true)
|
||||
// })
|
||||
// ], parseMarkdown: true), in: .window(.root))
|
||||
// }))
|
||||
// }
|
||||
// actionSheet.setItemGroups([
|
||||
// ActionSheetItemGroup(items: items),
|
||||
// ActionSheetItemGroup(items: [
|
||||
// ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
// actionSheet?.dismissAnimated()
|
||||
// completion(false)
|
||||
// })
|
||||
// ])
|
||||
// ])
|
||||
self.present(alertScreen, in: .window(.root))
|
||||
} else if peer.peerId == self.context.account.peerId {
|
||||
self.present(textAlertController(context: self.context, title: self.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: self.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
|
||||
|
|
|
|||
|
|
@ -76,17 +76,17 @@ public struct ItemListRevealOption: Equatable {
|
|||
private let titleFont = Font.regular(11.0)
|
||||
private let iconlessTitleFont = Font.regular(13.0)
|
||||
|
||||
private let optionSpacing: CGFloat = 10.0
|
||||
private let optionEdgeInset: CGFloat = 10.0
|
||||
private let optionTitleSpacing: CGFloat = 4.0
|
||||
private let optionRevealStartOverlap: CGFloat = 12.0
|
||||
private let optionRevealEndDistance: CGFloat = 10.0
|
||||
private let optionExpandedActivationWidthFactor: CGFloat = 3.0
|
||||
private let optionExpandedTransitionDistance: CGFloat = 16.0
|
||||
private let optionPillTitleHorizontalPadding: CGFloat = 6.0
|
||||
private let optionIconlessTitleAdditionalHorizontalPadding: CGFloat = 8.0
|
||||
private let optionIconAnimationResponse: CGFloat = 18.0
|
||||
private let optionIconAnimationSnapDistance: CGFloat = 0.5
|
||||
private let spacing: CGFloat = 10.0
|
||||
private let edgeInset: CGFloat = 10.0
|
||||
private let ritleSpacing: CGFloat = 4.0
|
||||
private let revealStartOverlap: CGFloat = 12.0
|
||||
private let revealEndDistance: CGFloat = 10.0
|
||||
private let expandedActivationWidthFactor: CGFloat = 3.0
|
||||
private let expandedTransitionDistance: CGFloat = 16.0
|
||||
private let iconlessTitleExpandedHorizontalPadding: CGFloat = 8.0
|
||||
private let iconlessTitleHorizontalPadding: CGFloat = 8.0
|
||||
private let iconAnimationResponse: CGFloat = 18.0
|
||||
private let iconAnimationSnapDistance: CGFloat = 0.5
|
||||
|
||||
private extension ItemListRevealOptionIcon {
|
||||
var hasVisualIcon: Bool {
|
||||
|
|
@ -108,7 +108,7 @@ private struct ItemListRevealOptionLayoutMetrics {
|
|||
let expandedIconInset: CGFloat
|
||||
|
||||
var contentHeight: CGFloat {
|
||||
return self.shapeSize.height + optionTitleSpacing + ceil(titleFont.lineHeight)
|
||||
return self.shapeSize.height + ritleSpacing + ceil(titleFont.lineHeight)
|
||||
}
|
||||
|
||||
var slotShapeInset: CGFloat {
|
||||
|
|
@ -118,7 +118,7 @@ private struct ItemListRevealOptionLayoutMetrics {
|
|||
static func metrics(for height: CGFloat, hasVisualIcons: Bool) -> ItemListRevealOptionLayoutMetrics {
|
||||
let regularShapeSize = CGSize(width: 50.0, height: 50.0)
|
||||
let compactShapeSize = CGSize(width: 60.0, height: 32.0)
|
||||
let regularContentHeight = regularShapeSize.height + optionTitleSpacing + ceil(titleFont.lineHeight)
|
||||
let regularContentHeight = regularShapeSize.height + ritleSpacing + ceil(titleFont.lineHeight)
|
||||
if height < regularContentHeight || !hasVisualIcons {
|
||||
return ItemListRevealOptionLayoutMetrics(shapeSize: compactShapeSize, slotWidth: 70.0, titleWidth: 70.0, iconMaxSide: 24.0, cornerRadius: 16.0, expandedIconInset: 16.0)
|
||||
} else {
|
||||
|
|
@ -127,16 +127,16 @@ private struct ItemListRevealOptionLayoutMetrics {
|
|||
}
|
||||
|
||||
func withGroupTitleWidth(_ maxTitleWidth: CGFloat) -> ItemListRevealOptionLayoutMetrics {
|
||||
if maxTitleWidth <= self.shapeSize.width - optionPillTitleHorizontalPadding {
|
||||
if maxTitleWidth <= self.shapeSize.width - iconlessTitleExpandedHorizontalPadding {
|
||||
return self
|
||||
}
|
||||
|
||||
let updatedShapeWidth = ceil(maxTitleWidth + optionPillTitleHorizontalPadding)
|
||||
let updatedShapeWidth = ceil(maxTitleWidth + iconlessTitleExpandedHorizontalPadding)
|
||||
let slotWidthDelta = self.slotWidth - self.shapeSize.width
|
||||
return ItemListRevealOptionLayoutMetrics(
|
||||
shapeSize: CGSize(width: updatedShapeWidth, height: self.shapeSize.height),
|
||||
slotWidth: updatedShapeWidth + slotWidthDelta,
|
||||
titleWidth: max(self.titleWidth, updatedShapeWidth - optionPillTitleHorizontalPadding),
|
||||
titleWidth: max(self.titleWidth, updatedShapeWidth - iconlessTitleExpandedHorizontalPadding),
|
||||
iconMaxSide: self.iconMaxSide,
|
||||
cornerRadius: self.cornerRadius,
|
||||
expandedIconInset: self.expandedIconInset
|
||||
|
|
@ -147,7 +147,7 @@ private struct ItemListRevealOptionLayoutMetrics {
|
|||
if count == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return optionEdgeInset * 2.0 + self.shapeSize.width * CGFloat(count) + optionSpacing * CGFloat(count - 1)
|
||||
return edgeInset * 2.0 + self.shapeSize.width * CGFloat(count) + spacing * CGFloat(count - 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -174,10 +174,10 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
|
|||
private var animationNodeOffset: CGFloat = 0.0
|
||||
private var animationNodeFlip = false
|
||||
|
||||
private var iconAnimationLink: SharedDisplayLinkDriver.Link?
|
||||
private weak var manuallyAnimatedIconNode: ASDisplayNode?
|
||||
private var currentIconCenter: CGPoint?
|
||||
private var targetIconCenter: CGPoint?
|
||||
private var contentAnimationLink: SharedDisplayLinkDriver.Link?
|
||||
private weak var manuallyAnimatedContentNode: ASDisplayNode?
|
||||
private var currentContentCenter: CGPoint?
|
||||
private var targetContentCenter: CGPoint?
|
||||
|
||||
private var didApplyLayout = false
|
||||
var isExpanded: Bool = false
|
||||
|
|
@ -189,7 +189,7 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
|
|||
var titleWidthForGroupPillSizing: CGFloat {
|
||||
var titleWidth = self.titleNode.updateLayout(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)).width
|
||||
if self.displaysTitleInsidePill {
|
||||
titleWidth += optionIconlessTitleAdditionalHorizontalPadding
|
||||
titleWidth += iconlessTitleHorizontalPadding
|
||||
}
|
||||
return titleWidth
|
||||
}
|
||||
|
|
@ -264,7 +264,7 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
|
|||
}
|
||||
|
||||
deinit {
|
||||
self.stopManualIconAnimation()
|
||||
self.stopManualContentAnimation()
|
||||
}
|
||||
|
||||
func setHighlighted(_ highlighted: Bool) {
|
||||
|
|
@ -280,81 +280,81 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
|
|||
|
||||
func resetAnimation() {
|
||||
self.animationNode?.reset()
|
||||
self.stopManualIconAnimation()
|
||||
self.stopManualContentAnimation()
|
||||
}
|
||||
|
||||
private func currentIconPresentationCenter(iconNode: ASDisplayNode) -> CGPoint {
|
||||
return iconNode.layer.presentation()?.position ?? iconNode.position
|
||||
private func currentContentPresentationCenter(contentNode: ASDisplayNode) -> CGPoint {
|
||||
return contentNode.layer.presentation()?.position ?? contentNode.position
|
||||
}
|
||||
|
||||
private func isManualIconAnimationAtTarget(center: CGPoint) -> Bool {
|
||||
guard let targetIconCenter = self.targetIconCenter else {
|
||||
private func isManualContentAnimationAtTarget(center: CGPoint) -> Bool {
|
||||
guard let targetContentCenter = self.targetContentCenter else {
|
||||
return true
|
||||
}
|
||||
let centerDeltaX = targetIconCenter.x - center.x
|
||||
let centerDeltaY = targetIconCenter.y - center.y
|
||||
let centerDeltaX = targetContentCenter.x - center.x
|
||||
let centerDeltaY = targetContentCenter.y - center.y
|
||||
let centerDistance = sqrt(centerDeltaX * centerDeltaX + centerDeltaY * centerDeltaY)
|
||||
return centerDistance <= optionIconAnimationSnapDistance
|
||||
return centerDistance <= iconAnimationSnapDistance
|
||||
}
|
||||
|
||||
private func stopManualIconAnimation() {
|
||||
self.iconAnimationLink?.isPaused = true
|
||||
self.iconAnimationLink?.invalidate()
|
||||
self.iconAnimationLink = nil
|
||||
self.manuallyAnimatedIconNode = nil
|
||||
self.currentIconCenter = nil
|
||||
self.targetIconCenter = nil
|
||||
private func stopManualContentAnimation() {
|
||||
self.contentAnimationLink?.isPaused = true
|
||||
self.contentAnimationLink?.invalidate()
|
||||
self.contentAnimationLink = nil
|
||||
self.manuallyAnimatedContentNode = nil
|
||||
self.currentContentCenter = nil
|
||||
self.targetContentCenter = nil
|
||||
}
|
||||
|
||||
private func updateManualIconCenter(iconNode: ASDisplayNode, targetCenter: CGPoint, forceImmediate: Bool) {
|
||||
iconNode.layer.removeAnimation(forKey: "position")
|
||||
private func updateManualContentCenter(contentNode: ASDisplayNode, targetCenter: CGPoint, forceImmediate: Bool) {
|
||||
contentNode.layer.removeAnimation(forKey: "position")
|
||||
|
||||
if self.manuallyAnimatedIconNode !== iconNode || self.currentIconCenter == nil {
|
||||
self.currentIconCenter = self.currentIconPresentationCenter(iconNode: iconNode)
|
||||
self.manuallyAnimatedIconNode = iconNode
|
||||
if self.manuallyAnimatedContentNode !== contentNode || self.currentContentCenter == nil {
|
||||
self.currentContentCenter = self.currentContentPresentationCenter(contentNode: contentNode)
|
||||
self.manuallyAnimatedContentNode = contentNode
|
||||
}
|
||||
|
||||
self.targetIconCenter = targetCenter
|
||||
self.targetContentCenter = targetCenter
|
||||
|
||||
if forceImmediate {
|
||||
iconNode.position = targetCenter
|
||||
self.stopManualIconAnimation()
|
||||
contentNode.position = targetCenter
|
||||
self.stopManualContentAnimation()
|
||||
return
|
||||
}
|
||||
|
||||
if let currentIconCenter = self.currentIconCenter, self.isManualIconAnimationAtTarget(center: currentIconCenter) {
|
||||
iconNode.position = targetCenter
|
||||
self.stopManualIconAnimation()
|
||||
if let currentContentCenter = self.currentContentCenter, self.isManualContentAnimationAtTarget(center: currentContentCenter) {
|
||||
contentNode.position = targetCenter
|
||||
self.stopManualContentAnimation()
|
||||
return
|
||||
}
|
||||
|
||||
if self.iconAnimationLink == nil {
|
||||
self.iconAnimationLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] deltaTime in
|
||||
self?.tickManualIconAnimation(deltaTime: deltaTime)
|
||||
if self.contentAnimationLink == nil {
|
||||
self.contentAnimationLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] deltaTime in
|
||||
self?.tickManualContentAnimation(deltaTime: deltaTime)
|
||||
})
|
||||
self.iconAnimationLink?.isPaused = false
|
||||
self.contentAnimationLink?.isPaused = false
|
||||
}
|
||||
}
|
||||
|
||||
private func tickManualIconAnimation(deltaTime: CGFloat) {
|
||||
guard let iconNode = self.manuallyAnimatedIconNode, let currentIconCenter = self.currentIconCenter, let targetIconCenter = self.targetIconCenter else {
|
||||
self.stopManualIconAnimation()
|
||||
private func tickManualContentAnimation(deltaTime: CGFloat) {
|
||||
guard let contentNode = self.manuallyAnimatedContentNode, let currentContentCenter = self.currentContentCenter, let targetContentCenter = self.targetContentCenter else {
|
||||
self.stopManualContentAnimation()
|
||||
return
|
||||
}
|
||||
|
||||
let clampedDeltaTime = min(0.05, max(0.0, deltaTime))
|
||||
let progress = 1.0 - exp(-clampedDeltaTime * optionIconAnimationResponse)
|
||||
let progress = 1.0 - exp(-clampedDeltaTime * iconAnimationResponse)
|
||||
let updatedCenter = CGPoint(
|
||||
x: currentIconCenter.x + (targetIconCenter.x - currentIconCenter.x) * progress,
|
||||
y: currentIconCenter.y + (targetIconCenter.y - currentIconCenter.y) * progress
|
||||
x: currentContentCenter.x + (targetContentCenter.x - currentContentCenter.x) * progress,
|
||||
y: currentContentCenter.y + (targetContentCenter.y - currentContentCenter.y) * progress
|
||||
)
|
||||
|
||||
if self.isManualIconAnimationAtTarget(center: updatedCenter) {
|
||||
iconNode.position = targetIconCenter
|
||||
self.stopManualIconAnimation()
|
||||
if self.isManualContentAnimationAtTarget(center: updatedCenter) {
|
||||
contentNode.position = targetContentCenter
|
||||
self.stopManualContentAnimation()
|
||||
} else {
|
||||
self.currentIconCenter = updatedCenter
|
||||
iconNode.position = updatedCenter
|
||||
self.currentContentCenter = updatedCenter
|
||||
contentNode.position = updatedCenter
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -375,7 +375,7 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
|
|||
if self.displaysTitleInsidePill {
|
||||
shapeY = floorToScreenPixels((bounds.height - pillSize.height) / 2.0)
|
||||
} else {
|
||||
let contentHeight = pillSize.height + optionTitleSpacing + titleSize.height
|
||||
let contentHeight = pillSize.height + ritleSpacing + titleSize.height
|
||||
shapeY = floorToScreenPixels((bounds.height - contentHeight) / 2.0)
|
||||
}
|
||||
|
||||
|
|
@ -412,7 +412,7 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
|
|||
transition.updateTransform(node: self.contentContainerNode, transform: CGAffineTransform(scaleX: contentScale, y: contentScale))
|
||||
|
||||
let titleAlpha: CGFloat = isPrimary && !self.displaysTitleInsidePill ? (1.0 - expandedProgress) : 1.0
|
||||
var didApplyManualIconCenter = false
|
||||
var didApplyManualContentCenter = false
|
||||
|
||||
let centeredIconCenterX = isPrimary ? backgroundFrame.midX : shapeFrame.midX
|
||||
let iconCenterX: CGFloat
|
||||
|
|
@ -440,15 +440,15 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
|
|||
let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(iconCenterX - imageSize.width / 2.0), y: floorToScreenPixels(iconCenterY - imageSize.height / 2.0) + (6.0 + self.animationNodeOffset) * scaleFraction), size: imageSize)
|
||||
|
||||
if isPrimary {
|
||||
didApplyManualIconCenter = true
|
||||
didApplyManualContentCenter = true
|
||||
transition.updateBounds(node: animationNode, bounds: CGRect(origin: CGPoint(), size: iconFrame.size))
|
||||
let targetCenter = frameCenter(iconFrame)
|
||||
if didApplyLayout && wasExpanded != isExpanded && revealProgress >= CGFloat.ulpOfOne {
|
||||
self.updateManualIconCenter(iconNode: animationNode, targetCenter: targetCenter, forceImmediate: false)
|
||||
} else if self.manuallyAnimatedIconNode === animationNode && self.iconAnimationLink != nil && revealProgress >= CGFloat.ulpOfOne {
|
||||
self.updateManualIconCenter(iconNode: animationNode, targetCenter: targetCenter, forceImmediate: false)
|
||||
self.updateManualContentCenter(contentNode: animationNode, targetCenter: targetCenter, forceImmediate: false)
|
||||
} else if self.manuallyAnimatedContentNode === animationNode && self.contentAnimationLink != nil && revealProgress >= CGFloat.ulpOfOne {
|
||||
self.updateManualContentCenter(contentNode: animationNode, targetCenter: targetCenter, forceImmediate: false)
|
||||
} else {
|
||||
self.stopManualIconAnimation()
|
||||
self.stopManualContentAnimation()
|
||||
transition.updatePosition(node: animationNode, position: targetCenter)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -470,35 +470,63 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
|
|||
}
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(iconCenterX - fittedSize.width / 2.0), y: floorToScreenPixels(iconCenterY - fittedSize.height / 2.0)), size: fittedSize)
|
||||
if isPrimary {
|
||||
didApplyManualIconCenter = true
|
||||
didApplyManualContentCenter = true
|
||||
transition.updateBounds(node: iconNode, bounds: CGRect(origin: CGPoint(), size: iconFrame.size))
|
||||
let targetCenter = frameCenter(iconFrame)
|
||||
if didApplyLayout && wasExpanded != isExpanded && revealProgress >= CGFloat.ulpOfOne {
|
||||
self.updateManualIconCenter(iconNode: iconNode, targetCenter: targetCenter, forceImmediate: false)
|
||||
} else if self.manuallyAnimatedIconNode === iconNode && self.iconAnimationLink != nil && revealProgress >= CGFloat.ulpOfOne {
|
||||
self.updateManualIconCenter(iconNode: iconNode, targetCenter: targetCenter, forceImmediate: false)
|
||||
self.updateManualContentCenter(contentNode: iconNode, targetCenter: targetCenter, forceImmediate: false)
|
||||
} else if self.manuallyAnimatedContentNode === iconNode && self.contentAnimationLink != nil && revealProgress >= CGFloat.ulpOfOne {
|
||||
self.updateManualContentCenter(contentNode: iconNode, targetCenter: targetCenter, forceImmediate: false)
|
||||
} else {
|
||||
self.stopManualIconAnimation()
|
||||
self.stopManualContentAnimation()
|
||||
transition.updatePosition(node: iconNode, position: targetCenter)
|
||||
}
|
||||
} else {
|
||||
transition.updateFrame(node: iconNode, frame: iconFrame)
|
||||
}
|
||||
}
|
||||
|
||||
if !didApplyManualIconCenter {
|
||||
self.stopManualIconAnimation()
|
||||
}
|
||||
transition.updateAlpha(node: self.titleNode, alpha: titleAlpha)
|
||||
|
||||
let titleFrame: CGRect
|
||||
if self.displaysTitleInsidePill {
|
||||
titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(backgroundFrame.midX - titleSize.width / 2.0), y: floorToScreenPixels(backgroundFrame.midY - titleSize.height / 2.0)), size: titleSize)
|
||||
if isPrimary {
|
||||
didApplyManualContentCenter = true
|
||||
transition.updateBounds(node: self.titleNode, bounds: CGRect(origin: CGPoint(), size: titleFrame.size))
|
||||
let titleCenterX: CGFloat
|
||||
if expandedProgress > 0.0 {
|
||||
let titleEdgeInset = max(metrics.expandedIconInset, titleSize.width / 2.0 + iconlessTitleExpandedHorizontalPadding)
|
||||
let expandedTitleCenterX: CGFloat
|
||||
if isLeft {
|
||||
expandedTitleCenterX = backgroundFrame.maxX - titleEdgeInset
|
||||
} else {
|
||||
expandedTitleCenterX = backgroundFrame.minX + titleEdgeInset
|
||||
}
|
||||
titleCenterX = backgroundFrame.midX + (expandedTitleCenterX - backgroundFrame.midX) * expandedProgress
|
||||
} else {
|
||||
titleCenterX = backgroundFrame.midX
|
||||
}
|
||||
let targetCenter = CGPoint(x: titleCenterX, y: backgroundFrame.midY)
|
||||
if didApplyLayout && wasExpanded != isExpanded && revealProgress >= CGFloat.ulpOfOne {
|
||||
self.updateManualContentCenter(contentNode: self.titleNode, targetCenter: targetCenter, forceImmediate: false)
|
||||
} else if self.manuallyAnimatedContentNode === self.titleNode && self.contentAnimationLink != nil && revealProgress >= CGFloat.ulpOfOne {
|
||||
self.updateManualContentCenter(contentNode: self.titleNode, targetCenter: targetCenter, forceImmediate: false)
|
||||
} else {
|
||||
self.stopManualContentAnimation()
|
||||
transition.updatePosition(node: self.titleNode, position: targetCenter)
|
||||
}
|
||||
} else {
|
||||
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
||||
}
|
||||
} else {
|
||||
let titleCenterX = isPrimary ? backgroundFrame.midX : shapeFrame.midX
|
||||
titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(titleCenterX - titleSize.width / 2.0), y: shapeFrame.maxY + optionTitleSpacing), size: titleSize)
|
||||
titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(titleCenterX - titleSize.width / 2.0), y: shapeFrame.maxY + ritleSpacing), size: titleSize)
|
||||
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
||||
}
|
||||
|
||||
if !didApplyManualContentCenter {
|
||||
self.stopManualContentAnimation()
|
||||
}
|
||||
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
||||
}
|
||||
|
||||
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||
|
|
@ -616,10 +644,10 @@ public final class ItemListRevealOptionsNode: ASDisplayNode {
|
|||
let revealedDistance = abs(self.revealOffset)
|
||||
let boundedRevealedDistance = min(revealedDistance, size.width)
|
||||
let overswipeDistance = max(0.0, revealedDistance - size.width)
|
||||
let overswipeProgress = clampToUnitInterval(overswipeDistance / optionExpandedTransitionDistance)
|
||||
let expandedActivationDistance = 50.0 * (optionExpandedActivationWidthFactor - 1.0)
|
||||
let overswipeProgress = clampToUnitInterval(overswipeDistance / expandedTransitionDistance)
|
||||
let expandedActivationDistance = 50.0 * (expandedActivationWidthFactor - 1.0)
|
||||
let primaryIndex = self.isLeft ? 0 : self.optionNodes.count - 1
|
||||
let stride = metrics.shapeSize.width + optionSpacing
|
||||
let stride = metrics.shapeSize.width + spacing
|
||||
|
||||
let clippingFrameX: CGFloat
|
||||
if self.isLeft {
|
||||
|
|
@ -657,17 +685,17 @@ public final class ItemListRevealOptionsNode: ASDisplayNode {
|
|||
let revealProgress: CGFloat
|
||||
|
||||
if self.isLeft {
|
||||
let baseCircleLeft = size.width - boundedRevealedDistance + self.sideInset + optionEdgeInset + CGFloat(i) * stride
|
||||
let baseCircleLeft = size.width - boundedRevealedDistance + self.sideInset + edgeInset + CGFloat(i) * stride
|
||||
baseCircleFrame = CGRect(origin: CGPoint(x: baseCircleLeft, y: 0.0), size: metrics.shapeSize)
|
||||
let distanceFromShutterEdge = size.width - baseCircleFrame.maxX
|
||||
revealProgress = clampToUnitInterval((distanceFromShutterEdge + optionRevealStartOverlap) / (optionRevealStartOverlap + optionRevealEndDistance))
|
||||
revealProgress = clampToUnitInterval((distanceFromShutterEdge + revealStartOverlap) / (revealStartOverlap + revealEndDistance))
|
||||
|
||||
if isStretched {
|
||||
let primaryLeft = size.width - boundedRevealedDistance + self.sideInset + optionEdgeInset
|
||||
let primaryLeft = size.width - boundedRevealedDistance + self.sideInset + edgeInset
|
||||
let primaryRight: CGFloat
|
||||
if self.optionNodes.count > 1 {
|
||||
let neighborLeft = primaryLeft + stride + overswipeDistance
|
||||
primaryRight = max(primaryLeft + metrics.shapeSize.width, neighborLeft - optionSpacing)
|
||||
primaryRight = max(primaryLeft + metrics.shapeSize.width, neighborLeft - spacing)
|
||||
} else {
|
||||
primaryRight = primaryLeft + metrics.shapeSize.width + overswipeDistance
|
||||
}
|
||||
|
|
@ -677,16 +705,16 @@ public final class ItemListRevealOptionsNode: ASDisplayNode {
|
|||
nodeFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(circleLeft - metrics.slotShapeInset), y: 0.0), size: CGSize(width: metrics.slotWidth, height: size.height))
|
||||
}
|
||||
} else {
|
||||
let baseCircleRight = revealedDistance + self.sideInset - optionEdgeInset - CGFloat(self.optionNodes.count - 1 - i) * stride
|
||||
let baseCircleRight = revealedDistance + self.sideInset - edgeInset - CGFloat(self.optionNodes.count - 1 - i) * stride
|
||||
baseCircleFrame = CGRect(origin: CGPoint(x: baseCircleRight - metrics.shapeSize.width, y: 0.0), size: metrics.shapeSize)
|
||||
revealProgress = clampToUnitInterval((baseCircleFrame.minX + optionRevealStartOverlap) / (optionRevealStartOverlap + optionRevealEndDistance))
|
||||
revealProgress = clampToUnitInterval((baseCircleFrame.minX + revealStartOverlap) / (revealStartOverlap + revealEndDistance))
|
||||
|
||||
if isStretched {
|
||||
let primaryRight = revealedDistance + self.sideInset - optionEdgeInset
|
||||
let primaryRight = revealedDistance + self.sideInset - edgeInset
|
||||
let primaryLeft: CGFloat
|
||||
if self.optionNodes.count > 1 {
|
||||
let neighborRight = primaryRight - stride - overswipeDistance
|
||||
primaryLeft = min(primaryRight - metrics.shapeSize.width, neighborRight + optionSpacing)
|
||||
primaryLeft = min(primaryRight - metrics.shapeSize.width, neighborRight + spacing)
|
||||
} else {
|
||||
primaryLeft = primaryRight - metrics.shapeSize.width - overswipeDistance
|
||||
}
|
||||
|
|
|
|||
|
|
@ -737,6 +737,14 @@ NSString *suffix = @"";
|
|||
[platform isEqualToString:@"iPad16,6"])
|
||||
return @"iPad Pro 12.9 inch (7th gen)";
|
||||
|
||||
if ([platform isEqualToString:@"iPad16,8"] ||
|
||||
[platform isEqualToString:@"iPad16,9"])
|
||||
return @"iPad Air 11 inch (8th gen)";
|
||||
|
||||
if ([platform isEqualToString:@"iPad16,10"] ||
|
||||
[platform isEqualToString:@"iPad16,11"])
|
||||
return @"iPad Air 13 inch (8th gen)";
|
||||
|
||||
if ([platform hasPrefix:@"iPhone"])
|
||||
return @"Unknown iPhone";
|
||||
if ([platform hasPrefix:@"iPod"])
|
||||
|
|
|
|||
|
|
@ -760,7 +760,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
|
|||
var canTransfer = false
|
||||
var canDismiss = false
|
||||
let canEditProcessJoinRequests: Bool
|
||||
if case let .user(user) = admin, user.botInfo?.flags.contains(.isGuardBot) == true {
|
||||
if !isChannel, case let .user(user) = admin, user.botInfo?.flags.contains(.isGuardBot) == true {
|
||||
canEditProcessJoinRequests = canEdit && user.id != accountPeerId
|
||||
} else {
|
||||
canEditProcessJoinRequests = false
|
||||
|
|
|
|||
|
|
@ -13,6 +13,15 @@ private enum RevealOptionKey: Int32 {
|
|||
case delete
|
||||
}
|
||||
|
||||
private func webBrowserDomainExceptionPlaceholderLetter(_ title: String) -> String {
|
||||
let trimmedTitle = title.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if let firstCharacter = trimmedTitle.first {
|
||||
return String(firstCharacter).uppercased()
|
||||
} else {
|
||||
return "#"
|
||||
}
|
||||
}
|
||||
|
||||
final class WebBrowserDomainExceptionItem: ListViewItem, ItemListItem {
|
||||
let presentationData: ItemListPresentationData
|
||||
let systemStyle: ItemListSystemStyle
|
||||
|
|
@ -92,6 +101,8 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
|||
private let maskNode: ASImageNode
|
||||
|
||||
let iconNode: TransformImageNode
|
||||
private let iconPlaceholderNode: ASDisplayNode
|
||||
private let iconPlaceholderTextNode: TextNode
|
||||
let titleNode: TextNode
|
||||
let labelNode: TextNode
|
||||
|
||||
|
|
@ -126,6 +137,15 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
|||
self.iconNode.isLayerBacked = true
|
||||
self.iconNode.displaysAsynchronously = false
|
||||
|
||||
self.iconPlaceholderNode = ASDisplayNode()
|
||||
self.iconPlaceholderNode.clipsToBounds = true
|
||||
self.iconPlaceholderNode.cornerRadius = 7.0
|
||||
|
||||
self.iconPlaceholderTextNode = TextNode()
|
||||
self.iconPlaceholderTextNode.isUserInteractionEnabled = false
|
||||
self.iconPlaceholderTextNode.contentMode = .center
|
||||
self.iconPlaceholderTextNode.contentsScale = UIScreen.main.scale
|
||||
|
||||
self.titleNode = TextNode()
|
||||
self.titleNode.isUserInteractionEnabled = false
|
||||
|
||||
|
|
@ -136,6 +156,8 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
|||
|
||||
super.init(layerBacked: false, rotated: false, seeThrough: false)
|
||||
|
||||
self.addSubnode(self.iconPlaceholderNode)
|
||||
self.iconPlaceholderNode.addSubnode(self.iconPlaceholderTextNode)
|
||||
self.addSubnode(self.iconNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.labelNode)
|
||||
|
|
@ -150,6 +172,7 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
|||
func asyncLayout() -> (_ item: WebBrowserDomainExceptionItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
||||
let makeIconPlaceholderTextLayout = TextNode.asyncLayout(self.iconPlaceholderTextNode)
|
||||
|
||||
let currentItem = self.item
|
||||
|
||||
|
|
@ -163,6 +186,7 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
|||
let insets: UIEdgeInsets
|
||||
let separatorHeight = UIScreenPixel
|
||||
let separatorRightInset: CGFloat = item.systemStyle == .glass ? 16.0 : 0.0
|
||||
let iconSize = CGSize(width: 30.0, height: 30.0)
|
||||
|
||||
let itemBackgroundColor: UIColor
|
||||
let itemSeparatorColor: UIColor
|
||||
|
|
@ -181,6 +205,10 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
|||
|
||||
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.label, font: labelFont, textColor: labelColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: maxTitleWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let iconPlaceholderText = webBrowserDomainExceptionPlaceholderLetter(item.title)
|
||||
let iconPlaceholderFont = Font.with(size: 17.0, design: .round, weight: .semibold)
|
||||
let (iconPlaceholderTextLayout, iconPlaceholderTextApply) = makeIconPlaceholderTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: iconPlaceholderText, font: iconPlaceholderFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: iconSize, alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let verticalInset: CGFloat
|
||||
switch item.systemStyle {
|
||||
case .glass:
|
||||
|
|
@ -220,12 +248,13 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
|||
strongSelf.topStripeNode.backgroundColor = itemSeparatorColor
|
||||
strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
|
||||
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
|
||||
strongSelf.iconPlaceholderNode.backgroundColor = item.presentationData.theme.list.mediaPlaceholderColor
|
||||
}
|
||||
|
||||
let iconSize = CGSize(width: 30.0, height: 30.0)
|
||||
if currentItem?.favicon != item.favicon {
|
||||
strongSelf.currentIconFile = nil
|
||||
strongSelf.iconDisposable.set(nil)
|
||||
strongSelf.iconNode.reset()
|
||||
|
||||
if let favicon = item.favicon {
|
||||
strongSelf.iconDisposable.set((item.context.engine.stickers.resolveInlineStickers(fileIds: [favicon])
|
||||
|
|
@ -244,6 +273,8 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
|||
strongSelf.iconNode.setSignal(chatMessageSticker(account: item.context.account, userLocation: .other, file: file, small: false, fetched: true))
|
||||
}
|
||||
strongSelf.iconNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(radius: 8.0), imageSize: resolvedImageSize, boundingSize: iconSize, intrinsicInsets: .zero))()
|
||||
strongSelf.iconPlaceholderNode.isHidden = true
|
||||
strongSelf.iconNode.isHidden = false
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
@ -251,9 +282,11 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
|||
if strongSelf.currentIconFile?.fileId.id == item.favicon, let dimensions = strongSelf.currentIconFile?.dimensions?.cgSize {
|
||||
imageSize = dimensions.aspectFilled(imageSize)
|
||||
}
|
||||
let hasResolvedIcon = item.favicon != nil && strongSelf.currentIconFile?.fileId.id == item.favicon
|
||||
|
||||
let _ = titleApply()
|
||||
let _ = labelApply()
|
||||
let _ = iconPlaceholderTextApply()
|
||||
|
||||
switch item.style {
|
||||
case .plain:
|
||||
|
|
@ -324,6 +357,10 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
|||
strongSelf.labelNode.frame = labelFrame
|
||||
|
||||
let iconFrame = CGRect(origin: CGPoint(x: params.leftInset + 16.0 + strongSelf.revealOffset, y: floorToScreenPixels((contentSize.height - iconSize.height) / 2.0)), size: iconSize)
|
||||
strongSelf.iconPlaceholderNode.frame = iconFrame
|
||||
strongSelf.iconPlaceholderTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((iconSize.width - iconPlaceholderTextLayout.size.width) / 2.0) + UIScreenPixel, y: floorToScreenPixels((iconSize.height - iconPlaceholderTextLayout.size.height) / 2.0) + 1.0), size: iconPlaceholderTextLayout.size)
|
||||
strongSelf.iconPlaceholderNode.isHidden = hasResolvedIcon
|
||||
strongSelf.iconNode.isHidden = !hasResolvedIcon
|
||||
strongSelf.iconNode.frame = iconFrame
|
||||
|
||||
strongSelf.iconNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(radius: 7.0), imageSize: imageSize, boundingSize: iconSize, intrinsicInsets: .zero))()
|
||||
|
|
@ -360,6 +397,10 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
|||
iconFrame.origin.x = params.leftInset + 16.0 + offset
|
||||
transition.updateFrame(node: self.iconNode, frame: iconFrame)
|
||||
|
||||
var iconPlaceholderFrame = self.iconPlaceholderNode.frame
|
||||
iconPlaceholderFrame.origin.x = params.leftInset + 16.0 + offset
|
||||
transition.updateFrame(node: self.iconPlaceholderNode, frame: iconPlaceholderFrame)
|
||||
|
||||
var titleFrame = self.titleNode.frame
|
||||
titleFrame.origin.x = leftInset + offset
|
||||
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import EdgeEffect
|
|||
import LocationUI
|
||||
import CountrySelectionUI
|
||||
import ShareWithPeersScreen
|
||||
import ChatTextLinkEditUI
|
||||
|
||||
public final class ComposedPoll {
|
||||
public struct Text {
|
||||
|
|
@ -918,6 +919,11 @@ final class ComposePollScreenComponent: Component {
|
|||
|
||||
self.deactivateInput()
|
||||
|
||||
if !replace, case .pollOption = subject, let webpage = self.attachedMedia(for: subject)?.media.media as? TelegramMediaWebpage, let link = webpage.content.url {
|
||||
self.openAttachedLinkMedia(subject: subject, link: link)
|
||||
return
|
||||
}
|
||||
|
||||
guard replace || !self.openAttachMediaContextMenu(subject: subject) else {
|
||||
return
|
||||
}
|
||||
|
|
@ -967,6 +973,38 @@ final class ComposePollScreenComponent: Component {
|
|||
})
|
||||
}
|
||||
|
||||
private func openAttachedLinkMedia(subject: MediaAttachSubject, link: String) {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = chatTextLinkEditController(
|
||||
context: component.context,
|
||||
updatedPresentationData: (presentationData, .never()),
|
||||
text: presentationData.strings.CreatePoll_Link_Description,
|
||||
link: link,
|
||||
preview: true,
|
||||
apply: { [weak self] link, webpage in
|
||||
guard let self, let link else {
|
||||
return
|
||||
}
|
||||
|
||||
if link.isEmpty {
|
||||
self.setAttachedMedia(nil, for: subject)
|
||||
self.state?.updated(transition: .easeInOut(duration: 0.2))
|
||||
return
|
||||
}
|
||||
|
||||
let attachedMedia = AttachedMedia(media: .standalone(media: webpage ?? makePollAttachmentLinkWebpage(link: link)))
|
||||
self.setAttachedMedia(attachedMedia, for: subject)
|
||||
self.uploadAttachedMediaIfNeeded(attachedMedia)
|
||||
self.state?.updated(transition: .easeInOut(duration: 0.2))
|
||||
}
|
||||
)
|
||||
(self.environment?.controller() as? ComposePollScreen)?.parentController()?.present(controller, in: .window(.root))
|
||||
}
|
||||
|
||||
private func uploadAttachedMediaIfNeeded(_ media: AttachedMedia) {
|
||||
guard let component = self.component, media.requiresUpload, media.uploadDisposable == nil else {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -23,6 +23,34 @@ public enum PollAttachmentSubject {
|
|||
case option
|
||||
}
|
||||
|
||||
func makePollAttachmentLinkWebpage(link: String) -> TelegramMediaWebpage {
|
||||
let mediaId = Int64.random(in: Int64.min ... Int64.max)
|
||||
return TelegramMediaWebpage(
|
||||
webpageId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: mediaId),
|
||||
content: .Loaded(TelegramMediaWebpageLoadedContent(
|
||||
url: link,
|
||||
displayUrl: link,
|
||||
hash: 0,
|
||||
type: nil,
|
||||
websiteName: nil,
|
||||
title: nil,
|
||||
text: nil,
|
||||
embedUrl: nil,
|
||||
embedType: nil,
|
||||
embedSize: nil,
|
||||
duration: nil,
|
||||
author: nil,
|
||||
isMediaLargeByDefault: nil,
|
||||
imageIsVideoCover: false,
|
||||
image: nil,
|
||||
file: nil,
|
||||
story: nil,
|
||||
attributes: [],
|
||||
instantPage: nil
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
public func presentPollAttachmentScreen(
|
||||
context: AccountContext,
|
||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
|
||||
|
|
@ -236,11 +264,11 @@ public func presentPollAttachmentScreen(
|
|||
case .link:
|
||||
attachmentController?.dismiss(animated: true)
|
||||
|
||||
//TODO:localize
|
||||
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = chatTextLinkEditController(
|
||||
context: context,
|
||||
updatedPresentationData: updatedPresentationData,
|
||||
text: "Attach a link to this option.",
|
||||
text: presentationData.strings.CreatePoll_Link_Description,
|
||||
link: nil,
|
||||
preview: true,
|
||||
apply: { link, webpage in
|
||||
|
|
@ -251,31 +279,7 @@ public func presentPollAttachmentScreen(
|
|||
completion(.standalone(media: webpage))
|
||||
return
|
||||
}
|
||||
let mediaId = Int64.random(in: Int64.min ... Int64.max)
|
||||
let webPage = TelegramMediaWebpage(
|
||||
webpageId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: mediaId),
|
||||
content: .Loaded(TelegramMediaWebpageLoadedContent(
|
||||
url: link,
|
||||
displayUrl: link,
|
||||
hash: 0,
|
||||
type: nil,
|
||||
websiteName: nil,
|
||||
title: nil,
|
||||
text: nil,
|
||||
embedUrl: nil,
|
||||
embedType: nil,
|
||||
embedSize: nil,
|
||||
duration: nil,
|
||||
author: nil,
|
||||
isMediaLargeByDefault: nil,
|
||||
imageIsVideoCover: false,
|
||||
image: nil,
|
||||
file: nil,
|
||||
story: nil,
|
||||
attributes: [],
|
||||
instantPage: nil
|
||||
)))
|
||||
completion(.standalone(media: webPage))
|
||||
completion(.standalone(media: makePollAttachmentLinkWebpage(link: link)))
|
||||
}
|
||||
)
|
||||
present(controller, false)
|
||||
|
|
|
|||
|
|
@ -125,9 +125,8 @@ public func openUserGeneratedUrl(
|
|||
let disposable = MetaDisposable()
|
||||
let checkState = concealedAlertOption.map { _ in AlertCheckComponent.ExternalState() }
|
||||
|
||||
//TODO:localize
|
||||
var content: [AnyComponentWithIdentity<AlertComponentEnvironment>] = []
|
||||
content.append(AnyComponentWithIdentity(id: "title", component: AnyComponent(AlertTitleComponent(title: "Open link?"))))
|
||||
content.append(AnyComponentWithIdentity(id: "title", component: AnyComponent(AlertTitleComponent(title: presentationData.strings.OpenLinkConfirmation_Title))))
|
||||
content.append(AnyComponentWithIdentity(id: "text", component: AnyComponent(AlertTextComponent(
|
||||
content: .plain(displayUrl),
|
||||
alignment: .center,
|
||||
|
|
@ -158,7 +157,7 @@ public func openUserGeneratedUrl(
|
|||
content: content,
|
||||
actions: [
|
||||
.init(title: presentationData.strings.Common_Cancel),
|
||||
.init(title: "Open", type: .default, action: {
|
||||
.init(title: presentationData.strings.OpenLinkConfirmation_Open, type: .default, action: {
|
||||
if checkState?.value == true {
|
||||
concealedAlertOption?.action()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -250,8 +250,7 @@ extension ChatControllerImpl {
|
|||
}))
|
||||
|
||||
if let openMode {
|
||||
//TODO:localize
|
||||
let reverseText = openMode.shouldOpenInApp ? "Open in Browser" : "Open In-App"
|
||||
let reverseText = openMode.shouldOpenInApp ? self.presentationData.strings.Chat_ContextMenu_OpenInBrowser : self.presentationData.strings.Chat_ContextMenu_OpenInApp
|
||||
items.append(ActionSheetButtonItem(title: reverseText, color: .accent, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
guard let self else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue