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.SetNewGroupPhoto" = "Set new group photo";
|
||||||
"MediaPicker.SetNewChannelPhoto" = "Set new channel 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
|
name = strings.Attachment_Audio
|
||||||
imageName = "Chat/Attach Menu/Audio"
|
imageName = "Chat/Attach Menu/Audio"
|
||||||
case .link:
|
case .link:
|
||||||
//TODO:localize
|
name = strings.Attachment_Link
|
||||||
name = "Link"
|
|
||||||
imageName = "Chat/Attach Menu/Link"
|
imageName = "Chat/Attach Menu/Link"
|
||||||
case let .app(bot):
|
case let .app(bot):
|
||||||
botPeer = bot.peer
|
botPeer = bot.peer
|
||||||
|
|
@ -2187,8 +2186,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate, ASGestureRecog
|
||||||
case .audio:
|
case .audio:
|
||||||
accessibilityTitle = self.presentationData.strings.Attachment_Audio
|
accessibilityTitle = self.presentationData.strings.Attachment_Audio
|
||||||
case .link:
|
case .link:
|
||||||
//TODO:localize
|
accessibilityTitle = self.presentationData.strings.Attachment_Link
|
||||||
accessibilityTitle = "Link"
|
|
||||||
case let .app(bot):
|
case let .app(bot):
|
||||||
accessibilityTitle = bot.shortName
|
accessibilityTitle = bot.shortName
|
||||||
case .standalone:
|
case .standalone:
|
||||||
|
|
|
||||||
|
|
@ -328,9 +328,9 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
|
||||||
self.component = component
|
self.component = component
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
let environmentValue = environment[ViewControllerComponentContainer.Environment.self].value
|
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
||||||
let controller = environmentValue.controller
|
let controller = environment.controller
|
||||||
let theme = environmentValue.theme.withModalBlocksBackground()
|
let theme = environment.theme.withModalBlocksBackground()
|
||||||
|
|
||||||
let dismiss: (Bool, (() -> Void)?) -> Void = { [weak self] animated, completion in
|
let dismiss: (Bool, (() -> Void)?) -> Void = { [weak self] animated, completion in
|
||||||
guard let self, !self.isDismissing else {
|
guard let self, !self.isDismissing else {
|
||||||
|
|
@ -379,7 +379,7 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
|
||||||
)),
|
)),
|
||||||
titleItem: AnyComponent(MultilineTextComponent(
|
titleItem: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: environmentValue.strings.Checkout_PaymentMethod,
|
string: environment.strings.Checkout_PaymentMethod,
|
||||||
font: Font.semibold(17.0),
|
font: Font.semibold(17.0),
|
||||||
textColor: theme.list.itemPrimaryTextColor
|
textColor: theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
|
|
@ -402,7 +402,6 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
//TODO:localize
|
|
||||||
bottomItem: AnyComponent(ButtonComponent(
|
bottomItem: AnyComponent(ButtonComponent(
|
||||||
background: ButtonComponent.Background(
|
background: ButtonComponent.Background(
|
||||||
style: .glass,
|
style: .glass,
|
||||||
|
|
@ -413,7 +412,7 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
|
||||||
content: AnyComponentWithIdentity(
|
content: AnyComponentWithIdentity(
|
||||||
id: AnyHashable("proceed"),
|
id: AnyHashable("proceed"),
|
||||||
component: AnyComponent(ButtonTextContentComponent(
|
component: AnyComponent(ButtonTextContentComponent(
|
||||||
text: "Proceed",
|
text: environment.strings.Checkout_PaymentMethod_Proceed,
|
||||||
badge: 0,
|
badge: 0,
|
||||||
textColor: theme.list.itemCheckColors.foregroundColor,
|
textColor: theme.list.itemCheckColors.foregroundColor,
|
||||||
badgeBackground: theme.list.itemCheckColors.foregroundColor,
|
badgeBackground: theme.list.itemCheckColors.foregroundColor,
|
||||||
|
|
@ -440,16 +439,16 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
|
||||||
animateOut: self.animateOut
|
animateOut: self.animateOut
|
||||||
)),
|
)),
|
||||||
environment: {
|
environment: {
|
||||||
environmentValue
|
environment
|
||||||
ResizableSheetComponentEnvironment(
|
ResizableSheetComponentEnvironment(
|
||||||
theme: theme,
|
theme: theme,
|
||||||
statusBarHeight: environmentValue.statusBarHeight,
|
statusBarHeight: environment.statusBarHeight,
|
||||||
safeInsets: environmentValue.safeInsets,
|
safeInsets: environment.safeInsets,
|
||||||
inputHeight: 0.0,
|
inputHeight: 0.0,
|
||||||
metrics: environmentValue.metrics,
|
metrics: environment.metrics,
|
||||||
deviceMetrics: environmentValue.deviceMetrics,
|
deviceMetrics: environment.deviceMetrics,
|
||||||
isDisplaying: environmentValue.isVisible,
|
isDisplaying: environment.isVisible,
|
||||||
isCentered: environmentValue.metrics.widthClass == .regular,
|
isCentered: environment.metrics.widthClass == .regular,
|
||||||
screenSize: availableSize,
|
screenSize: availableSize,
|
||||||
regularMetricsSize: nil,
|
regularMetricsSize: nil,
|
||||||
dismiss: { animated in
|
dismiss: { animated in
|
||||||
|
|
|
||||||
|
|
@ -225,9 +225,9 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
|
||||||
self.component = component
|
self.component = component
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
let environmentValue = environment[ViewControllerComponentContainer.Environment.self].value
|
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
||||||
let controller = environmentValue.controller
|
let controller = environment.controller
|
||||||
let theme = environmentValue.theme.withModalBlocksBackground()
|
let theme = environment.theme.withModalBlocksBackground()
|
||||||
|
|
||||||
let dismiss: (Bool) -> Void = { [weak self] animated in
|
let dismiss: (Bool) -> Void = { [weak self] animated in
|
||||||
guard let self, !self.isDismissing else {
|
guard let self, !self.isDismissing else {
|
||||||
|
|
@ -268,7 +268,7 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
|
||||||
)),
|
)),
|
||||||
titleItem: AnyComponent(MultilineTextComponent(
|
titleItem: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: environmentValue.strings.Checkout_ShippingMethod,
|
string: environment.strings.Checkout_ShippingMethod,
|
||||||
font: Font.semibold(17.0),
|
font: Font.semibold(17.0),
|
||||||
textColor: theme.list.itemPrimaryTextColor
|
textColor: theme.list.itemPrimaryTextColor
|
||||||
)),
|
)),
|
||||||
|
|
@ -291,7 +291,6 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
//TODO:localize
|
|
||||||
bottomItem: AnyComponent(ButtonComponent(
|
bottomItem: AnyComponent(ButtonComponent(
|
||||||
background: ButtonComponent.Background(
|
background: ButtonComponent.Background(
|
||||||
style: .glass,
|
style: .glass,
|
||||||
|
|
@ -302,7 +301,7 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
|
||||||
content: AnyComponentWithIdentity(
|
content: AnyComponentWithIdentity(
|
||||||
id: AnyHashable("proceed"),
|
id: AnyHashable("proceed"),
|
||||||
component: AnyComponent(ButtonTextContentComponent(
|
component: AnyComponent(ButtonTextContentComponent(
|
||||||
text: "Proceed",
|
text: environment.strings.Checkout_ShippingMethod_Proceed,
|
||||||
badge: 0,
|
badge: 0,
|
||||||
textColor: theme.list.itemCheckColors.foregroundColor,
|
textColor: theme.list.itemCheckColors.foregroundColor,
|
||||||
badgeBackground: theme.list.itemCheckColors.foregroundColor,
|
badgeBackground: theme.list.itemCheckColors.foregroundColor,
|
||||||
|
|
@ -323,16 +322,16 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
|
||||||
animateOut: self.animateOut
|
animateOut: self.animateOut
|
||||||
)),
|
)),
|
||||||
environment: {
|
environment: {
|
||||||
environmentValue
|
environment
|
||||||
ResizableSheetComponentEnvironment(
|
ResizableSheetComponentEnvironment(
|
||||||
theme: theme,
|
theme: theme,
|
||||||
statusBarHeight: environmentValue.statusBarHeight,
|
statusBarHeight: environment.statusBarHeight,
|
||||||
safeInsets: environmentValue.safeInsets,
|
safeInsets: environment.safeInsets,
|
||||||
inputHeight: 0.0,
|
inputHeight: 0.0,
|
||||||
metrics: environmentValue.metrics,
|
metrics: environment.metrics,
|
||||||
deviceMetrics: environmentValue.deviceMetrics,
|
deviceMetrics: environment.deviceMetrics,
|
||||||
isDisplaying: environmentValue.isVisible,
|
isDisplaying: environment.isVisible,
|
||||||
isCentered: environmentValue.metrics.widthClass == .regular,
|
isCentered: environment.metrics.widthClass == .regular,
|
||||||
screenSize: availableSize,
|
screenSize: availableSize,
|
||||||
regularMetricsSize: nil,
|
regularMetricsSize: nil,
|
||||||
dismiss: { animated in
|
dismiss: { animated in
|
||||||
|
|
|
||||||
|
|
@ -5900,8 +5900,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||||
}
|
}
|
||||||
actions.append(.init(title: self.presentationData.strings.Common_Cancel))
|
actions.append(.init(title: self.presentationData.strings.Common_Cancel))
|
||||||
|
|
||||||
//TODO:localize
|
let title: String = self.presentationData.strings.ChatList_DeleteChat
|
||||||
let title: String = "Delete Chat"
|
|
||||||
var text: String
|
var text: String
|
||||||
if mainPeer.id == self.context.account.peerId {
|
if mainPeer.id == self.context.account.peerId {
|
||||||
text = self.presentationData.strings.ChatList_DeleteSavedMessagesConfirmation
|
text = self.presentationData.strings.ChatList_DeleteSavedMessagesConfirmation
|
||||||
|
|
@ -5948,56 +5947,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||||
],
|
],
|
||||||
actions: actions
|
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))
|
self.present(alertScreen, in: .window(.root))
|
||||||
} else if peer.peerId == self.context.account.peerId {
|
} 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: [
|
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 titleFont = Font.regular(11.0)
|
||||||
private let iconlessTitleFont = Font.regular(13.0)
|
private let iconlessTitleFont = Font.regular(13.0)
|
||||||
|
|
||||||
private let optionSpacing: CGFloat = 10.0
|
private let spacing: CGFloat = 10.0
|
||||||
private let optionEdgeInset: CGFloat = 10.0
|
private let edgeInset: CGFloat = 10.0
|
||||||
private let optionTitleSpacing: CGFloat = 4.0
|
private let ritleSpacing: CGFloat = 4.0
|
||||||
private let optionRevealStartOverlap: CGFloat = 12.0
|
private let revealStartOverlap: CGFloat = 12.0
|
||||||
private let optionRevealEndDistance: CGFloat = 10.0
|
private let revealEndDistance: CGFloat = 10.0
|
||||||
private let optionExpandedActivationWidthFactor: CGFloat = 3.0
|
private let expandedActivationWidthFactor: CGFloat = 3.0
|
||||||
private let optionExpandedTransitionDistance: CGFloat = 16.0
|
private let expandedTransitionDistance: CGFloat = 16.0
|
||||||
private let optionPillTitleHorizontalPadding: CGFloat = 6.0
|
private let iconlessTitleExpandedHorizontalPadding: CGFloat = 8.0
|
||||||
private let optionIconlessTitleAdditionalHorizontalPadding: CGFloat = 8.0
|
private let iconlessTitleHorizontalPadding: CGFloat = 8.0
|
||||||
private let optionIconAnimationResponse: CGFloat = 18.0
|
private let iconAnimationResponse: CGFloat = 18.0
|
||||||
private let optionIconAnimationSnapDistance: CGFloat = 0.5
|
private let iconAnimationSnapDistance: CGFloat = 0.5
|
||||||
|
|
||||||
private extension ItemListRevealOptionIcon {
|
private extension ItemListRevealOptionIcon {
|
||||||
var hasVisualIcon: Bool {
|
var hasVisualIcon: Bool {
|
||||||
|
|
@ -108,7 +108,7 @@ private struct ItemListRevealOptionLayoutMetrics {
|
||||||
let expandedIconInset: CGFloat
|
let expandedIconInset: CGFloat
|
||||||
|
|
||||||
var contentHeight: CGFloat {
|
var contentHeight: CGFloat {
|
||||||
return self.shapeSize.height + optionTitleSpacing + ceil(titleFont.lineHeight)
|
return self.shapeSize.height + ritleSpacing + ceil(titleFont.lineHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
var slotShapeInset: CGFloat {
|
var slotShapeInset: CGFloat {
|
||||||
|
|
@ -118,7 +118,7 @@ private struct ItemListRevealOptionLayoutMetrics {
|
||||||
static func metrics(for height: CGFloat, hasVisualIcons: Bool) -> ItemListRevealOptionLayoutMetrics {
|
static func metrics(for height: CGFloat, hasVisualIcons: Bool) -> ItemListRevealOptionLayoutMetrics {
|
||||||
let regularShapeSize = CGSize(width: 50.0, height: 50.0)
|
let regularShapeSize = CGSize(width: 50.0, height: 50.0)
|
||||||
let compactShapeSize = CGSize(width: 60.0, height: 32.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 {
|
if height < regularContentHeight || !hasVisualIcons {
|
||||||
return ItemListRevealOptionLayoutMetrics(shapeSize: compactShapeSize, slotWidth: 70.0, titleWidth: 70.0, iconMaxSide: 24.0, cornerRadius: 16.0, expandedIconInset: 16.0)
|
return ItemListRevealOptionLayoutMetrics(shapeSize: compactShapeSize, slotWidth: 70.0, titleWidth: 70.0, iconMaxSide: 24.0, cornerRadius: 16.0, expandedIconInset: 16.0)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -127,16 +127,16 @@ private struct ItemListRevealOptionLayoutMetrics {
|
||||||
}
|
}
|
||||||
|
|
||||||
func withGroupTitleWidth(_ maxTitleWidth: CGFloat) -> ItemListRevealOptionLayoutMetrics {
|
func withGroupTitleWidth(_ maxTitleWidth: CGFloat) -> ItemListRevealOptionLayoutMetrics {
|
||||||
if maxTitleWidth <= self.shapeSize.width - optionPillTitleHorizontalPadding {
|
if maxTitleWidth <= self.shapeSize.width - iconlessTitleExpandedHorizontalPadding {
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
let updatedShapeWidth = ceil(maxTitleWidth + optionPillTitleHorizontalPadding)
|
let updatedShapeWidth = ceil(maxTitleWidth + iconlessTitleExpandedHorizontalPadding)
|
||||||
let slotWidthDelta = self.slotWidth - self.shapeSize.width
|
let slotWidthDelta = self.slotWidth - self.shapeSize.width
|
||||||
return ItemListRevealOptionLayoutMetrics(
|
return ItemListRevealOptionLayoutMetrics(
|
||||||
shapeSize: CGSize(width: updatedShapeWidth, height: self.shapeSize.height),
|
shapeSize: CGSize(width: updatedShapeWidth, height: self.shapeSize.height),
|
||||||
slotWidth: updatedShapeWidth + slotWidthDelta,
|
slotWidth: updatedShapeWidth + slotWidthDelta,
|
||||||
titleWidth: max(self.titleWidth, updatedShapeWidth - optionPillTitleHorizontalPadding),
|
titleWidth: max(self.titleWidth, updatedShapeWidth - iconlessTitleExpandedHorizontalPadding),
|
||||||
iconMaxSide: self.iconMaxSide,
|
iconMaxSide: self.iconMaxSide,
|
||||||
cornerRadius: self.cornerRadius,
|
cornerRadius: self.cornerRadius,
|
||||||
expandedIconInset: self.expandedIconInset
|
expandedIconInset: self.expandedIconInset
|
||||||
|
|
@ -147,7 +147,7 @@ private struct ItemListRevealOptionLayoutMetrics {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
return 0.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 animationNodeOffset: CGFloat = 0.0
|
||||||
private var animationNodeFlip = false
|
private var animationNodeFlip = false
|
||||||
|
|
||||||
private var iconAnimationLink: SharedDisplayLinkDriver.Link?
|
private var contentAnimationLink: SharedDisplayLinkDriver.Link?
|
||||||
private weak var manuallyAnimatedIconNode: ASDisplayNode?
|
private weak var manuallyAnimatedContentNode: ASDisplayNode?
|
||||||
private var currentIconCenter: CGPoint?
|
private var currentContentCenter: CGPoint?
|
||||||
private var targetIconCenter: CGPoint?
|
private var targetContentCenter: CGPoint?
|
||||||
|
|
||||||
private var didApplyLayout = false
|
private var didApplyLayout = false
|
||||||
var isExpanded: Bool = false
|
var isExpanded: Bool = false
|
||||||
|
|
@ -189,7 +189,7 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
|
||||||
var titleWidthForGroupPillSizing: CGFloat {
|
var titleWidthForGroupPillSizing: CGFloat {
|
||||||
var titleWidth = self.titleNode.updateLayout(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)).width
|
var titleWidth = self.titleNode.updateLayout(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)).width
|
||||||
if self.displaysTitleInsidePill {
|
if self.displaysTitleInsidePill {
|
||||||
titleWidth += optionIconlessTitleAdditionalHorizontalPadding
|
titleWidth += iconlessTitleHorizontalPadding
|
||||||
}
|
}
|
||||||
return titleWidth
|
return titleWidth
|
||||||
}
|
}
|
||||||
|
|
@ -264,7 +264,7 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.stopManualIconAnimation()
|
self.stopManualContentAnimation()
|
||||||
}
|
}
|
||||||
|
|
||||||
func setHighlighted(_ highlighted: Bool) {
|
func setHighlighted(_ highlighted: Bool) {
|
||||||
|
|
@ -280,81 +280,81 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
|
||||||
|
|
||||||
func resetAnimation() {
|
func resetAnimation() {
|
||||||
self.animationNode?.reset()
|
self.animationNode?.reset()
|
||||||
self.stopManualIconAnimation()
|
self.stopManualContentAnimation()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func currentIconPresentationCenter(iconNode: ASDisplayNode) -> CGPoint {
|
private func currentContentPresentationCenter(contentNode: ASDisplayNode) -> CGPoint {
|
||||||
return iconNode.layer.presentation()?.position ?? iconNode.position
|
return contentNode.layer.presentation()?.position ?? contentNode.position
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isManualIconAnimationAtTarget(center: CGPoint) -> Bool {
|
private func isManualContentAnimationAtTarget(center: CGPoint) -> Bool {
|
||||||
guard let targetIconCenter = self.targetIconCenter else {
|
guard let targetContentCenter = self.targetContentCenter else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
let centerDeltaX = targetIconCenter.x - center.x
|
let centerDeltaX = targetContentCenter.x - center.x
|
||||||
let centerDeltaY = targetIconCenter.y - center.y
|
let centerDeltaY = targetContentCenter.y - center.y
|
||||||
let centerDistance = sqrt(centerDeltaX * centerDeltaX + centerDeltaY * centerDeltaY)
|
let centerDistance = sqrt(centerDeltaX * centerDeltaX + centerDeltaY * centerDeltaY)
|
||||||
return centerDistance <= optionIconAnimationSnapDistance
|
return centerDistance <= iconAnimationSnapDistance
|
||||||
}
|
}
|
||||||
|
|
||||||
private func stopManualIconAnimation() {
|
private func stopManualContentAnimation() {
|
||||||
self.iconAnimationLink?.isPaused = true
|
self.contentAnimationLink?.isPaused = true
|
||||||
self.iconAnimationLink?.invalidate()
|
self.contentAnimationLink?.invalidate()
|
||||||
self.iconAnimationLink = nil
|
self.contentAnimationLink = nil
|
||||||
self.manuallyAnimatedIconNode = nil
|
self.manuallyAnimatedContentNode = nil
|
||||||
self.currentIconCenter = nil
|
self.currentContentCenter = nil
|
||||||
self.targetIconCenter = nil
|
self.targetContentCenter = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateManualIconCenter(iconNode: ASDisplayNode, targetCenter: CGPoint, forceImmediate: Bool) {
|
private func updateManualContentCenter(contentNode: ASDisplayNode, targetCenter: CGPoint, forceImmediate: Bool) {
|
||||||
iconNode.layer.removeAnimation(forKey: "position")
|
contentNode.layer.removeAnimation(forKey: "position")
|
||||||
|
|
||||||
if self.manuallyAnimatedIconNode !== iconNode || self.currentIconCenter == nil {
|
if self.manuallyAnimatedContentNode !== contentNode || self.currentContentCenter == nil {
|
||||||
self.currentIconCenter = self.currentIconPresentationCenter(iconNode: iconNode)
|
self.currentContentCenter = self.currentContentPresentationCenter(contentNode: contentNode)
|
||||||
self.manuallyAnimatedIconNode = iconNode
|
self.manuallyAnimatedContentNode = contentNode
|
||||||
}
|
}
|
||||||
|
|
||||||
self.targetIconCenter = targetCenter
|
self.targetContentCenter = targetCenter
|
||||||
|
|
||||||
if forceImmediate {
|
if forceImmediate {
|
||||||
iconNode.position = targetCenter
|
contentNode.position = targetCenter
|
||||||
self.stopManualIconAnimation()
|
self.stopManualContentAnimation()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let currentIconCenter = self.currentIconCenter, self.isManualIconAnimationAtTarget(center: currentIconCenter) {
|
if let currentContentCenter = self.currentContentCenter, self.isManualContentAnimationAtTarget(center: currentContentCenter) {
|
||||||
iconNode.position = targetCenter
|
contentNode.position = targetCenter
|
||||||
self.stopManualIconAnimation()
|
self.stopManualContentAnimation()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.iconAnimationLink == nil {
|
if self.contentAnimationLink == nil {
|
||||||
self.iconAnimationLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] deltaTime in
|
self.contentAnimationLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] deltaTime in
|
||||||
self?.tickManualIconAnimation(deltaTime: deltaTime)
|
self?.tickManualContentAnimation(deltaTime: deltaTime)
|
||||||
})
|
})
|
||||||
self.iconAnimationLink?.isPaused = false
|
self.contentAnimationLink?.isPaused = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func tickManualIconAnimation(deltaTime: CGFloat) {
|
private func tickManualContentAnimation(deltaTime: CGFloat) {
|
||||||
guard let iconNode = self.manuallyAnimatedIconNode, let currentIconCenter = self.currentIconCenter, let targetIconCenter = self.targetIconCenter else {
|
guard let contentNode = self.manuallyAnimatedContentNode, let currentContentCenter = self.currentContentCenter, let targetContentCenter = self.targetContentCenter else {
|
||||||
self.stopManualIconAnimation()
|
self.stopManualContentAnimation()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let clampedDeltaTime = min(0.05, max(0.0, deltaTime))
|
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(
|
let updatedCenter = CGPoint(
|
||||||
x: currentIconCenter.x + (targetIconCenter.x - currentIconCenter.x) * progress,
|
x: currentContentCenter.x + (targetContentCenter.x - currentContentCenter.x) * progress,
|
||||||
y: currentIconCenter.y + (targetIconCenter.y - currentIconCenter.y) * progress
|
y: currentContentCenter.y + (targetContentCenter.y - currentContentCenter.y) * progress
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.isManualIconAnimationAtTarget(center: updatedCenter) {
|
if self.isManualContentAnimationAtTarget(center: updatedCenter) {
|
||||||
iconNode.position = targetIconCenter
|
contentNode.position = targetContentCenter
|
||||||
self.stopManualIconAnimation()
|
self.stopManualContentAnimation()
|
||||||
} else {
|
} else {
|
||||||
self.currentIconCenter = updatedCenter
|
self.currentContentCenter = updatedCenter
|
||||||
iconNode.position = updatedCenter
|
contentNode.position = updatedCenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -375,7 +375,7 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
|
||||||
if self.displaysTitleInsidePill {
|
if self.displaysTitleInsidePill {
|
||||||
shapeY = floorToScreenPixels((bounds.height - pillSize.height) / 2.0)
|
shapeY = floorToScreenPixels((bounds.height - pillSize.height) / 2.0)
|
||||||
} else {
|
} else {
|
||||||
let contentHeight = pillSize.height + optionTitleSpacing + titleSize.height
|
let contentHeight = pillSize.height + ritleSpacing + titleSize.height
|
||||||
shapeY = floorToScreenPixels((bounds.height - contentHeight) / 2.0)
|
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))
|
transition.updateTransform(node: self.contentContainerNode, transform: CGAffineTransform(scaleX: contentScale, y: contentScale))
|
||||||
|
|
||||||
let titleAlpha: CGFloat = isPrimary && !self.displaysTitleInsidePill ? (1.0 - expandedProgress) : 1.0
|
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 centeredIconCenterX = isPrimary ? backgroundFrame.midX : shapeFrame.midX
|
||||||
let iconCenterX: CGFloat
|
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)
|
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 {
|
if isPrimary {
|
||||||
didApplyManualIconCenter = true
|
didApplyManualContentCenter = true
|
||||||
transition.updateBounds(node: animationNode, bounds: CGRect(origin: CGPoint(), size: iconFrame.size))
|
transition.updateBounds(node: animationNode, bounds: CGRect(origin: CGPoint(), size: iconFrame.size))
|
||||||
let targetCenter = frameCenter(iconFrame)
|
let targetCenter = frameCenter(iconFrame)
|
||||||
if didApplyLayout && wasExpanded != isExpanded && revealProgress >= CGFloat.ulpOfOne {
|
if didApplyLayout && wasExpanded != isExpanded && revealProgress >= CGFloat.ulpOfOne {
|
||||||
self.updateManualIconCenter(iconNode: animationNode, targetCenter: targetCenter, forceImmediate: false)
|
self.updateManualContentCenter(contentNode: animationNode, targetCenter: targetCenter, forceImmediate: false)
|
||||||
} else if self.manuallyAnimatedIconNode === animationNode && self.iconAnimationLink != nil && revealProgress >= CGFloat.ulpOfOne {
|
} else if self.manuallyAnimatedContentNode === animationNode && self.contentAnimationLink != nil && revealProgress >= CGFloat.ulpOfOne {
|
||||||
self.updateManualIconCenter(iconNode: animationNode, targetCenter: targetCenter, forceImmediate: false)
|
self.updateManualContentCenter(contentNode: animationNode, targetCenter: targetCenter, forceImmediate: false)
|
||||||
} else {
|
} else {
|
||||||
self.stopManualIconAnimation()
|
self.stopManualContentAnimation()
|
||||||
transition.updatePosition(node: animationNode, position: targetCenter)
|
transition.updatePosition(node: animationNode, position: targetCenter)
|
||||||
}
|
}
|
||||||
} else {
|
} 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)
|
let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(iconCenterX - fittedSize.width / 2.0), y: floorToScreenPixels(iconCenterY - fittedSize.height / 2.0)), size: fittedSize)
|
||||||
if isPrimary {
|
if isPrimary {
|
||||||
didApplyManualIconCenter = true
|
didApplyManualContentCenter = true
|
||||||
transition.updateBounds(node: iconNode, bounds: CGRect(origin: CGPoint(), size: iconFrame.size))
|
transition.updateBounds(node: iconNode, bounds: CGRect(origin: CGPoint(), size: iconFrame.size))
|
||||||
let targetCenter = frameCenter(iconFrame)
|
let targetCenter = frameCenter(iconFrame)
|
||||||
if didApplyLayout && wasExpanded != isExpanded && revealProgress >= CGFloat.ulpOfOne {
|
if didApplyLayout && wasExpanded != isExpanded && revealProgress >= CGFloat.ulpOfOne {
|
||||||
self.updateManualIconCenter(iconNode: iconNode, targetCenter: targetCenter, forceImmediate: false)
|
self.updateManualContentCenter(contentNode: iconNode, targetCenter: targetCenter, forceImmediate: false)
|
||||||
} else if self.manuallyAnimatedIconNode === iconNode && self.iconAnimationLink != nil && revealProgress >= CGFloat.ulpOfOne {
|
} else if self.manuallyAnimatedContentNode === iconNode && self.contentAnimationLink != nil && revealProgress >= CGFloat.ulpOfOne {
|
||||||
self.updateManualIconCenter(iconNode: iconNode, targetCenter: targetCenter, forceImmediate: false)
|
self.updateManualContentCenter(contentNode: iconNode, targetCenter: targetCenter, forceImmediate: false)
|
||||||
} else {
|
} else {
|
||||||
self.stopManualIconAnimation()
|
self.stopManualContentAnimation()
|
||||||
transition.updatePosition(node: iconNode, position: targetCenter)
|
transition.updatePosition(node: iconNode, position: targetCenter)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
transition.updateFrame(node: iconNode, frame: iconFrame)
|
transition.updateFrame(node: iconNode, frame: iconFrame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !didApplyManualIconCenter {
|
|
||||||
self.stopManualIconAnimation()
|
|
||||||
}
|
|
||||||
transition.updateAlpha(node: self.titleNode, alpha: titleAlpha)
|
transition.updateAlpha(node: self.titleNode, alpha: titleAlpha)
|
||||||
|
|
||||||
let titleFrame: CGRect
|
let titleFrame: CGRect
|
||||||
if self.displaysTitleInsidePill {
|
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)
|
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 {
|
} else {
|
||||||
let titleCenterX = isPrimary ? backgroundFrame.midX : shapeFrame.midX
|
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 {
|
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||||
|
|
@ -616,10 +644,10 @@ public final class ItemListRevealOptionsNode: ASDisplayNode {
|
||||||
let revealedDistance = abs(self.revealOffset)
|
let revealedDistance = abs(self.revealOffset)
|
||||||
let boundedRevealedDistance = min(revealedDistance, size.width)
|
let boundedRevealedDistance = min(revealedDistance, size.width)
|
||||||
let overswipeDistance = max(0.0, revealedDistance - size.width)
|
let overswipeDistance = max(0.0, revealedDistance - size.width)
|
||||||
let overswipeProgress = clampToUnitInterval(overswipeDistance / optionExpandedTransitionDistance)
|
let overswipeProgress = clampToUnitInterval(overswipeDistance / expandedTransitionDistance)
|
||||||
let expandedActivationDistance = 50.0 * (optionExpandedActivationWidthFactor - 1.0)
|
let expandedActivationDistance = 50.0 * (expandedActivationWidthFactor - 1.0)
|
||||||
let primaryIndex = self.isLeft ? 0 : self.optionNodes.count - 1
|
let primaryIndex = self.isLeft ? 0 : self.optionNodes.count - 1
|
||||||
let stride = metrics.shapeSize.width + optionSpacing
|
let stride = metrics.shapeSize.width + spacing
|
||||||
|
|
||||||
let clippingFrameX: CGFloat
|
let clippingFrameX: CGFloat
|
||||||
if self.isLeft {
|
if self.isLeft {
|
||||||
|
|
@ -657,17 +685,17 @@ public final class ItemListRevealOptionsNode: ASDisplayNode {
|
||||||
let revealProgress: CGFloat
|
let revealProgress: CGFloat
|
||||||
|
|
||||||
if self.isLeft {
|
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)
|
baseCircleFrame = CGRect(origin: CGPoint(x: baseCircleLeft, y: 0.0), size: metrics.shapeSize)
|
||||||
let distanceFromShutterEdge = size.width - baseCircleFrame.maxX
|
let distanceFromShutterEdge = size.width - baseCircleFrame.maxX
|
||||||
revealProgress = clampToUnitInterval((distanceFromShutterEdge + optionRevealStartOverlap) / (optionRevealStartOverlap + optionRevealEndDistance))
|
revealProgress = clampToUnitInterval((distanceFromShutterEdge + revealStartOverlap) / (revealStartOverlap + revealEndDistance))
|
||||||
|
|
||||||
if isStretched {
|
if isStretched {
|
||||||
let primaryLeft = size.width - boundedRevealedDistance + self.sideInset + optionEdgeInset
|
let primaryLeft = size.width - boundedRevealedDistance + self.sideInset + edgeInset
|
||||||
let primaryRight: CGFloat
|
let primaryRight: CGFloat
|
||||||
if self.optionNodes.count > 1 {
|
if self.optionNodes.count > 1 {
|
||||||
let neighborLeft = primaryLeft + stride + overswipeDistance
|
let neighborLeft = primaryLeft + stride + overswipeDistance
|
||||||
primaryRight = max(primaryLeft + metrics.shapeSize.width, neighborLeft - optionSpacing)
|
primaryRight = max(primaryLeft + metrics.shapeSize.width, neighborLeft - spacing)
|
||||||
} else {
|
} else {
|
||||||
primaryRight = primaryLeft + metrics.shapeSize.width + overswipeDistance
|
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))
|
nodeFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(circleLeft - metrics.slotShapeInset), y: 0.0), size: CGSize(width: metrics.slotWidth, height: size.height))
|
||||||
}
|
}
|
||||||
} else {
|
} 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)
|
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 {
|
if isStretched {
|
||||||
let primaryRight = revealedDistance + self.sideInset - optionEdgeInset
|
let primaryRight = revealedDistance + self.sideInset - edgeInset
|
||||||
let primaryLeft: CGFloat
|
let primaryLeft: CGFloat
|
||||||
if self.optionNodes.count > 1 {
|
if self.optionNodes.count > 1 {
|
||||||
let neighborRight = primaryRight - stride - overswipeDistance
|
let neighborRight = primaryRight - stride - overswipeDistance
|
||||||
primaryLeft = min(primaryRight - metrics.shapeSize.width, neighborRight + optionSpacing)
|
primaryLeft = min(primaryRight - metrics.shapeSize.width, neighborRight + spacing)
|
||||||
} else {
|
} else {
|
||||||
primaryLeft = primaryRight - metrics.shapeSize.width - overswipeDistance
|
primaryLeft = primaryRight - metrics.shapeSize.width - overswipeDistance
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -737,6 +737,14 @@ NSString *suffix = @"";
|
||||||
[platform isEqualToString:@"iPad16,6"])
|
[platform isEqualToString:@"iPad16,6"])
|
||||||
return @"iPad Pro 12.9 inch (7th gen)";
|
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"])
|
if ([platform hasPrefix:@"iPhone"])
|
||||||
return @"Unknown iPhone";
|
return @"Unknown iPhone";
|
||||||
if ([platform hasPrefix:@"iPod"])
|
if ([platform hasPrefix:@"iPod"])
|
||||||
|
|
|
||||||
|
|
@ -760,7 +760,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
|
||||||
var canTransfer = false
|
var canTransfer = false
|
||||||
var canDismiss = false
|
var canDismiss = false
|
||||||
let canEditProcessJoinRequests: Bool
|
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
|
canEditProcessJoinRequests = canEdit && user.id != accountPeerId
|
||||||
} else {
|
} else {
|
||||||
canEditProcessJoinRequests = false
|
canEditProcessJoinRequests = false
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,15 @@ private enum RevealOptionKey: Int32 {
|
||||||
case delete
|
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 {
|
final class WebBrowserDomainExceptionItem: ListViewItem, ItemListItem {
|
||||||
let presentationData: ItemListPresentationData
|
let presentationData: ItemListPresentationData
|
||||||
let systemStyle: ItemListSystemStyle
|
let systemStyle: ItemListSystemStyle
|
||||||
|
|
@ -92,6 +101,8 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
||||||
private let maskNode: ASImageNode
|
private let maskNode: ASImageNode
|
||||||
|
|
||||||
let iconNode: TransformImageNode
|
let iconNode: TransformImageNode
|
||||||
|
private let iconPlaceholderNode: ASDisplayNode
|
||||||
|
private let iconPlaceholderTextNode: TextNode
|
||||||
let titleNode: TextNode
|
let titleNode: TextNode
|
||||||
let labelNode: TextNode
|
let labelNode: TextNode
|
||||||
|
|
||||||
|
|
@ -126,6 +137,15 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
||||||
self.iconNode.isLayerBacked = true
|
self.iconNode.isLayerBacked = true
|
||||||
self.iconNode.displaysAsynchronously = false
|
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 = TextNode()
|
||||||
self.titleNode.isUserInteractionEnabled = false
|
self.titleNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
|
@ -136,6 +156,8 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
||||||
|
|
||||||
super.init(layerBacked: false, rotated: false, seeThrough: false)
|
super.init(layerBacked: false, rotated: false, seeThrough: false)
|
||||||
|
|
||||||
|
self.addSubnode(self.iconPlaceholderNode)
|
||||||
|
self.iconPlaceholderNode.addSubnode(self.iconPlaceholderTextNode)
|
||||||
self.addSubnode(self.iconNode)
|
self.addSubnode(self.iconNode)
|
||||||
self.addSubnode(self.titleNode)
|
self.addSubnode(self.titleNode)
|
||||||
self.addSubnode(self.labelNode)
|
self.addSubnode(self.labelNode)
|
||||||
|
|
@ -150,6 +172,7 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
||||||
func asyncLayout() -> (_ item: WebBrowserDomainExceptionItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
func asyncLayout() -> (_ item: WebBrowserDomainExceptionItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||||
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
||||||
|
let makeIconPlaceholderTextLayout = TextNode.asyncLayout(self.iconPlaceholderTextNode)
|
||||||
|
|
||||||
let currentItem = self.item
|
let currentItem = self.item
|
||||||
|
|
||||||
|
|
@ -163,6 +186,7 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
||||||
let insets: UIEdgeInsets
|
let insets: UIEdgeInsets
|
||||||
let separatorHeight = UIScreenPixel
|
let separatorHeight = UIScreenPixel
|
||||||
let separatorRightInset: CGFloat = item.systemStyle == .glass ? 16.0 : 0.0
|
let separatorRightInset: CGFloat = item.systemStyle == .glass ? 16.0 : 0.0
|
||||||
|
let iconSize = CGSize(width: 30.0, height: 30.0)
|
||||||
|
|
||||||
let itemBackgroundColor: UIColor
|
let itemBackgroundColor: UIColor
|
||||||
let itemSeparatorColor: 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 (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
|
let verticalInset: CGFloat
|
||||||
switch item.systemStyle {
|
switch item.systemStyle {
|
||||||
case .glass:
|
case .glass:
|
||||||
|
|
@ -220,12 +248,13 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
||||||
strongSelf.topStripeNode.backgroundColor = itemSeparatorColor
|
strongSelf.topStripeNode.backgroundColor = itemSeparatorColor
|
||||||
strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
|
strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
|
||||||
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
|
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 {
|
if currentItem?.favicon != item.favicon {
|
||||||
strongSelf.currentIconFile = nil
|
strongSelf.currentIconFile = nil
|
||||||
strongSelf.iconDisposable.set(nil)
|
strongSelf.iconDisposable.set(nil)
|
||||||
|
strongSelf.iconNode.reset()
|
||||||
|
|
||||||
if let favicon = item.favicon {
|
if let favicon = item.favicon {
|
||||||
strongSelf.iconDisposable.set((item.context.engine.stickers.resolveInlineStickers(fileIds: [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.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.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 {
|
if strongSelf.currentIconFile?.fileId.id == item.favicon, let dimensions = strongSelf.currentIconFile?.dimensions?.cgSize {
|
||||||
imageSize = dimensions.aspectFilled(imageSize)
|
imageSize = dimensions.aspectFilled(imageSize)
|
||||||
}
|
}
|
||||||
|
let hasResolvedIcon = item.favicon != nil && strongSelf.currentIconFile?.fileId.id == item.favicon
|
||||||
|
|
||||||
let _ = titleApply()
|
let _ = titleApply()
|
||||||
let _ = labelApply()
|
let _ = labelApply()
|
||||||
|
let _ = iconPlaceholderTextApply()
|
||||||
|
|
||||||
switch item.style {
|
switch item.style {
|
||||||
case .plain:
|
case .plain:
|
||||||
|
|
@ -324,6 +357,10 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It
|
||||||
strongSelf.labelNode.frame = labelFrame
|
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)
|
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.frame = iconFrame
|
||||||
|
|
||||||
strongSelf.iconNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(radius: 7.0), imageSize: imageSize, boundingSize: iconSize, intrinsicInsets: .zero))()
|
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
|
iconFrame.origin.x = params.leftInset + 16.0 + offset
|
||||||
transition.updateFrame(node: self.iconNode, frame: iconFrame)
|
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
|
var titleFrame = self.titleNode.frame
|
||||||
titleFrame.origin.x = leftInset + offset
|
titleFrame.origin.x = leftInset + offset
|
||||||
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import EdgeEffect
|
||||||
import LocationUI
|
import LocationUI
|
||||||
import CountrySelectionUI
|
import CountrySelectionUI
|
||||||
import ShareWithPeersScreen
|
import ShareWithPeersScreen
|
||||||
|
import ChatTextLinkEditUI
|
||||||
|
|
||||||
public final class ComposedPoll {
|
public final class ComposedPoll {
|
||||||
public struct Text {
|
public struct Text {
|
||||||
|
|
@ -918,6 +919,11 @@ final class ComposePollScreenComponent: Component {
|
||||||
|
|
||||||
self.deactivateInput()
|
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 {
|
guard replace || !self.openAttachMediaContextMenu(subject: subject) else {
|
||||||
return
|
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) {
|
private func uploadAttachedMediaIfNeeded(_ media: AttachedMedia) {
|
||||||
guard let component = self.component, media.requiresUpload, media.uploadDisposable == nil else {
|
guard let component = self.component, media.requiresUpload, media.uploadDisposable == nil else {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,34 @@ public enum PollAttachmentSubject {
|
||||||
case option
|
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(
|
public func presentPollAttachmentScreen(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
|
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
|
||||||
|
|
@ -236,11 +264,11 @@ public func presentPollAttachmentScreen(
|
||||||
case .link:
|
case .link:
|
||||||
attachmentController?.dismiss(animated: true)
|
attachmentController?.dismiss(animated: true)
|
||||||
|
|
||||||
//TODO:localize
|
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let controller = chatTextLinkEditController(
|
let controller = chatTextLinkEditController(
|
||||||
context: context,
|
context: context,
|
||||||
updatedPresentationData: updatedPresentationData,
|
updatedPresentationData: updatedPresentationData,
|
||||||
text: "Attach a link to this option.",
|
text: presentationData.strings.CreatePoll_Link_Description,
|
||||||
link: nil,
|
link: nil,
|
||||||
preview: true,
|
preview: true,
|
||||||
apply: { link, webpage in
|
apply: { link, webpage in
|
||||||
|
|
@ -251,31 +279,7 @@ public func presentPollAttachmentScreen(
|
||||||
completion(.standalone(media: webpage))
|
completion(.standalone(media: webpage))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let mediaId = Int64.random(in: Int64.min ... Int64.max)
|
completion(.standalone(media: makePollAttachmentLinkWebpage(link: link)))
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
present(controller, false)
|
present(controller, false)
|
||||||
|
|
|
||||||
|
|
@ -125,9 +125,8 @@ public func openUserGeneratedUrl(
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
let checkState = concealedAlertOption.map { _ in AlertCheckComponent.ExternalState() }
|
let checkState = concealedAlertOption.map { _ in AlertCheckComponent.ExternalState() }
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
var content: [AnyComponentWithIdentity<AlertComponentEnvironment>] = []
|
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.append(AnyComponentWithIdentity(id: "text", component: AnyComponent(AlertTextComponent(
|
||||||
content: .plain(displayUrl),
|
content: .plain(displayUrl),
|
||||||
alignment: .center,
|
alignment: .center,
|
||||||
|
|
@ -158,7 +157,7 @@ public func openUserGeneratedUrl(
|
||||||
content: content,
|
content: content,
|
||||||
actions: [
|
actions: [
|
||||||
.init(title: presentationData.strings.Common_Cancel),
|
.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 {
|
if checkState?.value == true {
|
||||||
concealedAlertOption?.action()
|
concealedAlertOption?.action()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -250,8 +250,7 @@ extension ChatControllerImpl {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if let openMode {
|
if let openMode {
|
||||||
//TODO:localize
|
let reverseText = openMode.shouldOpenInApp ? self.presentationData.strings.Chat_ContextMenu_OpenInBrowser : self.presentationData.strings.Chat_ContextMenu_OpenInApp
|
||||||
let reverseText = openMode.shouldOpenInApp ? "Open in Browser" : "Open In-App"
|
|
||||||
items.append(ActionSheetButtonItem(title: reverseText, color: .accent, action: { [weak self, weak actionSheet] in
|
items.append(ActionSheetButtonItem(title: reverseText, color: .accent, action: { [weak self, weak actionSheet] in
|
||||||
actionSheet?.dismissAnimated()
|
actionSheet?.dismissAnimated()
|
||||||
guard let self else {
|
guard let self else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue