mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Various improvements
This commit is contained in:
parent
7d8920db82
commit
0186f12972
@ -14107,3 +14107,11 @@ Sorry for the inconvenience.";
|
||||
"Login.Fee.Support.Text" = "Sign up for a 1-week Telegram Premium subscription to help cover the SMS costs.";
|
||||
"Login.Fee.SignUp" = "Sign Up for %@";
|
||||
"Login.Fee.GetPremiumForAWeek" = "Get Telegram Premium for 1 week";
|
||||
|
||||
"StoryFeed.AddStory" = "Add Story";
|
||||
|
||||
"WebApp.ImportData.Title" = "Import Data";
|
||||
"WebApp.ImportData.Description" = "**%@** is requesting permission to import data from a previous Telegram account used on this device.";
|
||||
"WebApp.ImportData.AccountHeader" = "ACCOUNT TO IMPORT DATA FROM";
|
||||
"WebApp.ImportData.CreatedOn" = "created on %@";
|
||||
"WebApp.ImportData.Import" = "Import";
|
||||
|
@ -7,6 +7,9 @@ import CoreVideo
|
||||
import Metal
|
||||
import Display
|
||||
import TelegramCore
|
||||
import RLottieBinding
|
||||
import GZip
|
||||
import AppBundle
|
||||
|
||||
let videoMessageDimensions = PixelDimensions(width: 400, height: 400)
|
||||
|
||||
@ -98,7 +101,17 @@ final class CameraRoundVideoFilter {
|
||||
private var resizeFilter: CIFilter?
|
||||
private var overlayFilter: CIFilter?
|
||||
private var compositeFilter: CIFilter?
|
||||
private var borderFilter: CIFilter?
|
||||
private var maskFilter: CIFilter?
|
||||
private var blurFilter: CIFilter?
|
||||
private var darkenFilter: CIFilter?
|
||||
|
||||
private var logoImageFilter: CIFilter?
|
||||
private var logoImage: CIImage?
|
||||
|
||||
private var animationImageFilter: CIFilter?
|
||||
private var animationImage: CIImage?
|
||||
private var animation: LottieInstance?
|
||||
private var animationFrameIndex: Int32 = 0
|
||||
|
||||
private var outputColorSpace: CGColorSpace?
|
||||
private var outputPixelBufferPool: CVPixelBufferPool?
|
||||
@ -122,21 +135,45 @@ final class CameraRoundVideoFilter {
|
||||
}
|
||||
self.inputFormatDescription = formatDescription
|
||||
|
||||
let circleImage = generateImage(videoMessageDimensions.cgSize, opaque: false, scale: 1.0, rotatedContext: { size, context in
|
||||
if let logoImage = UIImage(bundleImageName: "Components/RoundVideoCorner") {
|
||||
self.logoImage = CIImage(image: logoImage)
|
||||
}
|
||||
|
||||
if let path = getAppBundle().path(forResource: "PlaneLogoPlain", ofType: "tgs"), var data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
|
||||
if let unpackedData = TGGUnzipData(data, 5 * 1024 * 1024) {
|
||||
data = unpackedData
|
||||
self.animation = LottieInstance(data: data, fitzModifier: .none, colorReplacements: [:], cacheKey: "")
|
||||
}
|
||||
}
|
||||
|
||||
let circleMaskImage = generateImage(videoMessageDimensions.cgSize, opaque: false, scale: 1.0, rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: .zero, size: size)
|
||||
context.clear(bounds)
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
context.setFillColor(UIColor.black.cgColor)
|
||||
context.fill(bounds)
|
||||
context.setBlendMode(.clear)
|
||||
context.setBlendMode(.normal)
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
context.fillEllipse(in: bounds.insetBy(dx: -2.0, dy: -2.0))
|
||||
})!
|
||||
|
||||
|
||||
self.resizeFilter = CIFilter(name: "CILanczosScaleTransform")
|
||||
self.overlayFilter = CIFilter(name: "CIColorMatrix")
|
||||
self.compositeFilter = CIFilter(name: "CISourceOverCompositing")
|
||||
|
||||
self.borderFilter = CIFilter(name: "CISourceOverCompositing")
|
||||
self.borderFilter?.setValue(CIImage(image: circleImage), forKey: kCIInputImageKey)
|
||||
self.maskFilter = CIFilter(name: "CIBlendWithMask")
|
||||
self.maskFilter?.setValue(CIImage(image: circleMaskImage), forKey: kCIInputMaskImageKey)
|
||||
|
||||
self.blurFilter = CIFilter(name: "CIGaussianBlur")
|
||||
self.blurFilter?.setValue(30.0, forKey: kCIInputRadiusKey)
|
||||
|
||||
self.darkenFilter = CIFilter(name: "CIColorMatrix")
|
||||
let darkenVector = CIVector(x: 0.25, y: 0, z: 0, w: 0)
|
||||
self.darkenFilter?.setValue(darkenVector, forKey: "inputRVector")
|
||||
self.darkenFilter?.setValue(darkenVector, forKey: "inputGVector")
|
||||
self.darkenFilter?.setValue(darkenVector, forKey: "inputBVector")
|
||||
|
||||
self.logoImageFilter = CIFilter(name: "CISourceOverCompositing")
|
||||
self.animationImageFilter = CIFilter(name: "CISourceOverCompositing")
|
||||
|
||||
self.isPrepared = true
|
||||
}
|
||||
@ -145,7 +182,11 @@ final class CameraRoundVideoFilter {
|
||||
self.resizeFilter = nil
|
||||
self.overlayFilter = nil
|
||||
self.compositeFilter = nil
|
||||
self.borderFilter = nil
|
||||
self.maskFilter = nil
|
||||
self.blurFilter = nil
|
||||
self.darkenFilter = nil
|
||||
self.logoImageFilter = nil
|
||||
self.animationImageFilter = nil
|
||||
self.outputColorSpace = nil
|
||||
self.outputPixelBufferPool = nil
|
||||
self.outputFormatDescription = nil
|
||||
@ -159,7 +200,15 @@ final class CameraRoundVideoFilter {
|
||||
private var lastAdditionalSourceImage: CIImage?
|
||||
|
||||
func render(pixelBuffer: CVPixelBuffer, additional: Bool, captureOrientation: AVCaptureVideoOrientation, transitionFactor: CGFloat) -> CVPixelBuffer? {
|
||||
guard let resizeFilter = self.resizeFilter, let overlayFilter = self.overlayFilter, let compositeFilter = self.compositeFilter, let borderFilter = self.borderFilter, self.isPrepared else {
|
||||
guard let resizeFilter = self.resizeFilter,
|
||||
let overlayFilter = self.overlayFilter,
|
||||
let compositeFilter = self.compositeFilter,
|
||||
let maskFilter = self.maskFilter,
|
||||
let blurFilter = self.blurFilter,
|
||||
let darkenFilter = self.darkenFilter,
|
||||
let logoImageFilter = self.logoImageFilter,
|
||||
let animationImageFilter = self.animationImageFilter,
|
||||
self.isPrepared else {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -230,9 +279,62 @@ final class CameraRoundVideoFilter {
|
||||
}
|
||||
}
|
||||
|
||||
borderFilter.setValue(effectiveSourceImage, forKey: kCIInputBackgroundImageKey)
|
||||
let extendedImage = effectiveSourceImage.clampedToExtent()
|
||||
|
||||
blurFilter.setValue(extendedImage, forKey: kCIInputImageKey)
|
||||
let blurredImage = blurFilter.outputImage ?? effectiveSourceImage
|
||||
|
||||
let blurredAndCropped = blurredImage.cropped(to: effectiveSourceImage.extent)
|
||||
|
||||
darkenFilter.setValue(blurredAndCropped, forKey: kCIInputImageKey)
|
||||
let darkenedBlurredBackground = darkenFilter.outputImage ?? blurredAndCropped
|
||||
|
||||
maskFilter.setValue(effectiveSourceImage, forKey: kCIInputImageKey)
|
||||
maskFilter.setValue(darkenedBlurredBackground, forKey: kCIInputBackgroundImageKey)
|
||||
|
||||
var finalImage = maskFilter.outputImage
|
||||
guard let maskedImage = finalImage else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let logoImage = self.logoImage {
|
||||
let overlayWidth: CGFloat = 100.0
|
||||
let xPosition = maskedImage.extent.width - overlayWidth
|
||||
let yPosition = 0.0
|
||||
|
||||
let transformedOverlay = logoImage.transformed(by: CGAffineTransform(translationX: xPosition, y: yPosition))
|
||||
logoImageFilter.setValue(transformedOverlay, forKey: kCIInputImageKey)
|
||||
logoImageFilter.setValue(maskedImage, forKey: kCIInputBackgroundImageKey)
|
||||
|
||||
finalImage = logoImageFilter.outputImage ?? maskedImage
|
||||
} else {
|
||||
finalImage = maskedImage
|
||||
}
|
||||
|
||||
if let animation = self.animation, let renderContext = DrawingContext(size: CGSize(width: 68.0, height: 68.0), scale: 1.0, clear: true) {
|
||||
animation.renderFrame(with: self.animationFrameIndex, into: renderContext.bytes.assumingMemoryBound(to: UInt8.self), width: Int32(renderContext.size.width * renderContext.scale), height: Int32(renderContext.size.height * renderContext.scale), bytesPerRow: Int32(renderContext.bytesPerRow))
|
||||
|
||||
self.animationFrameIndex += 2
|
||||
if self.animationFrameIndex >= animation.frameCount {
|
||||
self.animationFrameIndex = 0
|
||||
}
|
||||
|
||||
if let image = renderContext.generateImage(), let animationImage = CIImage(image: image) {
|
||||
let xPosition = 0.0
|
||||
let yPosition = 0.0
|
||||
|
||||
let transformedOverlay = animationImage.transformed(by: CGAffineTransform(translationX: xPosition, y: yPosition))
|
||||
animationImageFilter.setValue(transformedOverlay, forKey: kCIInputImageKey)
|
||||
animationImageFilter.setValue(finalImage, forKey: kCIInputBackgroundImageKey)
|
||||
|
||||
finalImage = animationImageFilter.outputImage ?? maskedImage
|
||||
} else {
|
||||
finalImage = maskedImage
|
||||
}
|
||||
} else {
|
||||
finalImage = maskedImage
|
||||
}
|
||||
|
||||
let finalImage = borderFilter.outputImage
|
||||
guard let finalImage else {
|
||||
return nil
|
||||
}
|
||||
|
@ -2291,7 +2291,11 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
dateReplies = Int(attribute.count)
|
||||
}
|
||||
} else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
starsCount = attribute.stars.value
|
||||
var messageCount: Int = 1
|
||||
if case let .group(messages) = item.content {
|
||||
messageCount = messages.count
|
||||
}
|
||||
starsCount = attribute.stars.value * Int64(messageCount)
|
||||
}
|
||||
}
|
||||
|
||||
|
21
submodules/TelegramUI/Images.xcassets/Components/RoundVideoCorner.imageset/Contents.json
vendored
Normal file
21
submodules/TelegramUI/Images.xcassets/Components/RoundVideoCorner.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "vlog.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Components/RoundVideoCorner.imageset/vlog.png
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Components/RoundVideoCorner.imageset/vlog.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
@ -53,6 +53,7 @@ swift_library(
|
||||
"//submodules/DeviceLocationManager",
|
||||
"//submodules/DeviceAccess",
|
||||
"//submodules/TelegramUI/Components/Utils/GenerateStickerPlaceholderImage",
|
||||
"//submodules/AvatarNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -2992,39 +2992,46 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
return
|
||||
}
|
||||
|
||||
let transferController = WebAppSecureStorageTransferScreen(
|
||||
context: self.context,
|
||||
existingKeys: storedKeys,
|
||||
completion: { [weak self] uuid in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let uuid else {
|
||||
let data: JSON = [
|
||||
"req_id": requestId,
|
||||
"error": "RESTORE_CANCELLED"
|
||||
]
|
||||
self.webView?.sendEvent(name: "secure_storage_failed", data: data.string)
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (WebAppSecureStorage.transferAllValues(context: self.context, fromUuid: uuid, botId: controller.botId)
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: controller.botId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] botPeer in
|
||||
guard let self, let botPeer, let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
let transferController = WebAppSecureStorageTransferScreen(
|
||||
context: self.context,
|
||||
peer: botPeer,
|
||||
existingKeys: storedKeys,
|
||||
completion: { [weak self] uuid in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = (WebAppSecureStorage.getValue(context: self.context, botId: controller.botId, key: key)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let uuid else {
|
||||
let data: JSON = [
|
||||
"req_id": requestId,
|
||||
"value": value ?? NSNull()
|
||||
"error": "RESTORE_CANCELLED"
|
||||
]
|
||||
self?.webView?.sendEvent(name: "secure_storage_key_restored", data: data.string)
|
||||
self.webView?.sendEvent(name: "secure_storage_failed", data: data.string)
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (WebAppSecureStorage.transferAllValues(context: self.context, fromUuid: uuid, botId: controller.botId)
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = (WebAppSecureStorage.getValue(context: self.context, botId: controller.botId, key: key)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
let data: JSON = [
|
||||
"req_id": requestId,
|
||||
"value": value ?? NSNull()
|
||||
]
|
||||
self?.webView?.sendEvent(name: "secure_storage_key_restored", data: data.string)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
controller.parentController()?.push(transferController)
|
||||
}
|
||||
)
|
||||
controller.parentController()?.push(transferController)
|
||||
})
|
||||
}
|
||||
|
||||
fileprivate func openLocationSettings() {
|
||||
|
@ -11,28 +11,31 @@ import TelegramStringFormatting
|
||||
import ViewControllerComponent
|
||||
import SheetComponent
|
||||
import BundleIconComponent
|
||||
import BalancedTextComponent
|
||||
import MultilineTextComponent
|
||||
import ButtonComponent
|
||||
import ListSectionComponent
|
||||
import ListActionItemComponent
|
||||
import AccountContext
|
||||
import AvatarNode
|
||||
|
||||
private final class SheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let peer: EnginePeer
|
||||
let existingKeys: [WebAppSecureStorage.ExistingKey]
|
||||
let completion: (String) -> Void
|
||||
let dismiss: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
peer: EnginePeer,
|
||||
existingKeys: [WebAppSecureStorage.ExistingKey],
|
||||
completion: @escaping (String) -> Void,
|
||||
dismiss: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
self.existingKeys = existingKeys
|
||||
self.completion = completion
|
||||
self.dismiss = dismiss
|
||||
@ -42,6 +45,9 @@ private final class SheetContent: CombinedComponent {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
if lhs.existingKeys != rhs.existingKeys {
|
||||
return false
|
||||
}
|
||||
@ -59,8 +65,9 @@ private final class SheetContent: CombinedComponent {
|
||||
static var body: Body {
|
||||
let closeButton = Child(Button.self)
|
||||
|
||||
let title = Child(BalancedTextComponent.self)
|
||||
let text = Child(BalancedTextComponent.self)
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let avatar = Child(AvatarComponent.self)
|
||||
let text = Child(MultilineTextComponent.self)
|
||||
let keys = Child(ListSectionComponent.self)
|
||||
let button = Child(ButtonComponent.self)
|
||||
|
||||
@ -76,11 +83,11 @@ private final class SheetContent: CombinedComponent {
|
||||
let textSideInset: CGFloat = 32.0 + environment.safeInsets.left
|
||||
|
||||
let titleFont = Font.semibold(17.0)
|
||||
let subtitleFont = Font.regular(12.0)
|
||||
let textFont = Font.regular(13.0)
|
||||
let boldTextFont = Font.semibold(13.0)
|
||||
let textColor = theme.actionSheet.primaryTextColor
|
||||
let secondaryTextColor = theme.actionSheet.secondaryTextColor
|
||||
|
||||
var contentSize = CGSize(width: context.availableSize.width, height: 10.0)
|
||||
var contentSize = CGSize(width: context.availableSize.width, height: 18.0)
|
||||
|
||||
let closeButton = closeButton.update(
|
||||
component: Button(
|
||||
@ -98,8 +105,8 @@ private final class SheetContent: CombinedComponent {
|
||||
|
||||
//TODO:localize
|
||||
let title = title.update(
|
||||
component: BalancedTextComponent(
|
||||
text: .plain(NSAttributedString(string: "Data Transfer Requested", font: titleFont, textColor: textColor)),
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: strings.WebApp_ImportData_Title, font: titleFont, textColor: textColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 1,
|
||||
lineSpacing: 0.1
|
||||
@ -111,12 +118,36 @@ private final class SheetContent: CombinedComponent {
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + title.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += title.size.height
|
||||
contentSize.height += 24.0
|
||||
|
||||
let avatar = avatar.update(
|
||||
component: AvatarComponent(
|
||||
context: component.context,
|
||||
peer: component.peer,
|
||||
size: CGSize(width: 80.0, height: 80.0)
|
||||
),
|
||||
availableSize: CGSize(width: 80.0, height: 80.0),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(avatar
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + avatar.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += avatar.size.height
|
||||
contentSize.height += 22.0
|
||||
|
||||
let text = text.update(
|
||||
component: BalancedTextComponent(
|
||||
text: .plain(NSAttributedString(string: "Choose account to transfer data from:", font: subtitleFont, textColor: secondaryTextColor)),
|
||||
component: MultilineTextComponent(
|
||||
text: .markdown(
|
||||
text: strings.WebApp_ImportData_Description(component.peer.compactDisplayTitle).string,
|
||||
attributes: MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: textFont, textColor: textColor),
|
||||
bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor),
|
||||
link: MarkdownAttributeSet(font: textFont, textColor: textColor),
|
||||
linkAttribute: { _ in return nil }
|
||||
)
|
||||
),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 1,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.2
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
||||
@ -126,7 +157,7 @@ private final class SheetContent: CombinedComponent {
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + text.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += text.size.height
|
||||
contentSize.height += 17.0
|
||||
contentSize.height += 29.0
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
@ -146,8 +177,8 @@ private final class SheetContent: CombinedComponent {
|
||||
titleComponents.append(
|
||||
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "Created on \(stringForMediumCompactDate(timestamp: key.timestamp, strings: strings, dateTimeFormat: environment.dateTimeFormat))",
|
||||
font: Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize * 14.0 / 17.0)),
|
||||
string: strings.WebApp_ImportData_CreatedOn(stringForMediumCompactDate(timestamp: key.timestamp, strings: strings, dateTimeFormat: environment.dateTimeFormat)).string,
|
||||
font: Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize * 15.0 / 17.0)),
|
||||
textColor: environment.theme.list.itemSecondaryTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 1
|
||||
@ -155,7 +186,8 @@ private final class SheetContent: CombinedComponent {
|
||||
)
|
||||
items.append(AnyComponentWithIdentity(id: key.uuid, component: AnyComponent(ListActionItemComponent(
|
||||
theme: theme,
|
||||
title: AnyComponent(VStack(titleComponents, alignment: .left, spacing: 2.0)),
|
||||
title: AnyComponent(VStack(titleComponents, alignment: .left, spacing: 3.0)),
|
||||
contentInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0),
|
||||
leftIcon: .check(ListActionItemComponent.LeftIcon.Check(isSelected: key.uuid == state.selectedUuid, isEnabled: true, toggle: nil)),
|
||||
accessory: nil,
|
||||
action: { [weak state] _ in
|
||||
@ -170,7 +202,14 @@ private final class SheetContent: CombinedComponent {
|
||||
let keys = keys.update(
|
||||
component: ListSectionComponent(
|
||||
theme: environment.theme,
|
||||
header: nil,
|
||||
header: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: strings.WebApp_ImportData_AccountHeader.uppercased(),
|
||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||
textColor: environment.theme.list.freeTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 0
|
||||
)),
|
||||
footer: nil,
|
||||
items: items
|
||||
),
|
||||
@ -181,9 +220,8 @@ private final class SheetContent: CombinedComponent {
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + keys.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += keys.size.height
|
||||
contentSize.height += 17.0
|
||||
contentSize.height += 24.0
|
||||
|
||||
//TODO:localize
|
||||
let button = button.update(
|
||||
component: ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
@ -192,12 +230,13 @@ private final class SheetContent: CombinedComponent {
|
||||
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
|
||||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable("transfer"),
|
||||
id: AnyHashable("import"),
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "Transfer", font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)))
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: strings.WebApp_ImportData_Import, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)))
|
||||
)
|
||||
),
|
||||
isEnabled: state.selectedUuid != nil,
|
||||
allowActionWhenDisabled: true,
|
||||
displaysProgress: false,
|
||||
action: { [weak state] in
|
||||
guard let state else {
|
||||
@ -231,15 +270,18 @@ private final class SheetContainerComponent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let peer: EnginePeer
|
||||
let existingKeys: [WebAppSecureStorage.ExistingKey]
|
||||
let completion: (String) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
peer: EnginePeer,
|
||||
existingKeys: [WebAppSecureStorage.ExistingKey],
|
||||
completion: @escaping (String) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
self.existingKeys = existingKeys
|
||||
self.completion = completion
|
||||
}
|
||||
@ -248,6 +290,9 @@ private final class SheetContainerComponent: CombinedComponent {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
if lhs.existingKeys != rhs.existingKeys {
|
||||
return false
|
||||
}
|
||||
@ -270,6 +315,7 @@ private final class SheetContainerComponent: CombinedComponent {
|
||||
component: SheetComponent<EnvironmentType>(
|
||||
content: AnyComponent<EnvironmentType>(SheetContent(
|
||||
context: context.component.context,
|
||||
peer: context.component.peer,
|
||||
existingKeys: context.component.existingKeys,
|
||||
completion: context.component.completion,
|
||||
dismiss: {
|
||||
@ -340,6 +386,7 @@ private final class SheetContainerComponent: CombinedComponent {
|
||||
final class WebAppSecureStorageTransferScreen: ViewControllerComponentContainer {
|
||||
init(
|
||||
context: AccountContext,
|
||||
peer: EnginePeer,
|
||||
existingKeys: [WebAppSecureStorage.ExistingKey],
|
||||
completion: @escaping (String?) -> Void
|
||||
) {
|
||||
@ -347,6 +394,7 @@ final class WebAppSecureStorageTransferScreen: ViewControllerComponentContainer
|
||||
context: context,
|
||||
component: SheetContainerComponent(
|
||||
context: context,
|
||||
peer: peer,
|
||||
existingKeys: existingKeys,
|
||||
completion: completion
|
||||
),
|
||||
@ -368,3 +416,79 @@ final class WebAppSecureStorageTransferScreen: ViewControllerComponentContainer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class AvatarComponent: Component {
|
||||
let context: AccountContext
|
||||
let peer: EnginePeer
|
||||
let size: CGSize?
|
||||
|
||||
init(context: AccountContext, peer: EnginePeer, size: CGSize? = nil) {
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
self.size = size
|
||||
}
|
||||
|
||||
static func ==(lhs: AvatarComponent, rhs: AvatarComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
if lhs.size != rhs.size {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private var avatarNode: AvatarNode?
|
||||
|
||||
private var component: AvatarComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: AvatarComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let size = component.size ?? availableSize
|
||||
|
||||
let avatarNode: AvatarNode
|
||||
if let current = self.avatarNode {
|
||||
avatarNode = current
|
||||
} else {
|
||||
avatarNode = AvatarNode(font: avatarPlaceholderFont(size: floor(size.width * 0.5)))
|
||||
avatarNode.displaysAsynchronously = false
|
||||
self.avatarNode = avatarNode
|
||||
self.addSubview(avatarNode.view)
|
||||
}
|
||||
avatarNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
avatarNode.setPeer(
|
||||
context: component.context,
|
||||
theme: component.context.sharedContext.currentPresentationData.with({ $0 }).theme,
|
||||
peer: component.peer,
|
||||
synchronousLoad: true,
|
||||
displayDimensions: size
|
||||
)
|
||||
avatarNode.updateSize(size: size)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user