mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-08 17:53:38 +00:00
Various improvements
This commit is contained in:
parent
1387bb5416
commit
7b0bbd0cce
@ -2356,7 +2356,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
|
|
||||||
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 8.0), size: CGSize())
|
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 8.0), size: CGSize())
|
||||||
|
|
||||||
parentController.present(TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: .plain(text: text), icon: .animation(name: "ChatListFoldersTooltip", delay: 0.6), location: .point(location, .bottom), shouldDismissOnTouch: { point in
|
parentController.present(TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: .plain(text: text), icon: .animation(name: "ChatListFoldersTooltip", delay: 0.6, tintColor: nil), location: .point(location, .bottom), shouldDismissOnTouch: { point in
|
||||||
guard let strongSelf = self, let parentController = strongSelf.parent as? TabBarController else {
|
guard let strongSelf = self, let parentController = strongSelf.parent as? TabBarController else {
|
||||||
return .dismiss(consume: false)
|
return .dismiss(consume: false)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -128,16 +128,23 @@ public final class LottieAnimationComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func playOnce() {
|
public func playOnce() {
|
||||||
guard let animationView = self.animationView else {
|
guard let animationView = self.animationView, let component = self.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
animationView.stop()
|
animationView.stop()
|
||||||
animationView.loopMode = .playOnce
|
animationView.loopMode = .playOnce
|
||||||
|
|
||||||
|
if let range = component.animation.range {
|
||||||
|
animationView.play(fromProgress: range.0, toProgress: range.1, completion: { [weak self] _ in
|
||||||
|
self?.currentCompletion?()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
animationView.play { [weak self] _ in
|
animationView.play { [weak self] _ in
|
||||||
self?.currentCompletion?()
|
self?.currentCompletion?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func update(component: LottieAnimationComponent, availableSize: CGSize, transition: Transition) -> CGSize {
|
func update(component: LottieAnimationComponent, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||||
var updatePlayback = false
|
var updatePlayback = false
|
||||||
|
|||||||
@ -485,6 +485,7 @@ private let color6Tag = GenericComponentViewTag()
|
|||||||
private let color7Tag = GenericComponentViewTag()
|
private let color7Tag = GenericComponentViewTag()
|
||||||
private let color8Tag = GenericComponentViewTag()
|
private let color8Tag = GenericComponentViewTag()
|
||||||
private let colorTags = [color1Tag, color2Tag, color3Tag, color4Tag, color5Tag, color6Tag, color7Tag, color8Tag]
|
private let colorTags = [color1Tag, color2Tag, color3Tag, color4Tag, color5Tag, color6Tag, color7Tag, color8Tag]
|
||||||
|
private let cancelButtonTag = GenericComponentViewTag()
|
||||||
private let doneButtonTag = GenericComponentViewTag()
|
private let doneButtonTag = GenericComponentViewTag()
|
||||||
|
|
||||||
private final class DrawingScreenComponent: CombinedComponent {
|
private final class DrawingScreenComponent: CombinedComponent {
|
||||||
@ -2074,6 +2075,9 @@ private final class DrawingScreenComponent: CombinedComponent {
|
|||||||
animatingOut = true
|
animatingOut = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if animatingOut && component.sourceHint == .storyEditor {
|
||||||
|
|
||||||
|
} else {
|
||||||
let backButton = backButton.update(
|
let backButton = backButton.update(
|
||||||
component: Button(
|
component: Button(
|
||||||
content: AnyComponent(
|
content: AnyComponent(
|
||||||
@ -2094,7 +2098,7 @@ private final class DrawingScreenComponent: CombinedComponent {
|
|||||||
dismiss.invoke(Void())
|
dismiss.invoke(Void())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
).minSize(CGSize(width: 44.0, height: 44.0)),
|
).minSize(CGSize(width: 44.0, height: 44.0)).tagged(cancelButtonTag),
|
||||||
availableSize: CGSize(width: 33.0, height: 33.0),
|
availableSize: CGSize(width: 33.0, height: 33.0),
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
)
|
)
|
||||||
@ -2110,6 +2114,7 @@ private final class DrawingScreenComponent: CombinedComponent {
|
|||||||
.position(backButtonPosition)
|
.position(backButtonPosition)
|
||||||
.opacity(controlsAreVisible ? 1.0 : 0.0)
|
.opacity(controlsAreVisible ? 1.0 : 0.0)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return context.availableSize
|
return context.availableSize
|
||||||
}
|
}
|
||||||
@ -2497,6 +2502,9 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U
|
|||||||
view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
if let view = self.componentHost.findTaggedView(tag: bottomGradientTag) {
|
if let view = self.componentHost.findTaggedView(tag: bottomGradientTag) {
|
||||||
|
if self.controller?.sourceHint == .storyEditor {
|
||||||
|
view.isHidden = true
|
||||||
|
}
|
||||||
view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
if let buttonView = self.componentHost.findTaggedView(tag: undoButtonTag) {
|
if let buttonView = self.componentHost.findTaggedView(tag: undoButtonTag) {
|
||||||
|
|||||||
@ -206,7 +206,7 @@ private enum SaveIncomingMediaEntry: ItemListNodeEntry {
|
|||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
|
||||||
case let .addException(title):
|
case let .addException(title):
|
||||||
let icon: UIImage? = PresentationResourcesItemList.createGroupIcon(presentationData.theme)
|
let icon: UIImage? = PresentationResourcesItemList.createGroupIcon(presentationData.theme)
|
||||||
return ItemListPeerActionItem(presentationData: presentationData, icon: icon, title: title, alwaysPlain: false, sectionId: self.section, editing: false, action: {
|
return ItemListPeerActionItem(presentationData: presentationData, icon: icon, title: title, alwaysPlain: false, sectionId: self.section, height: .generic, editing: false, action: {
|
||||||
arguments.openAddException()
|
arguments.openAddException()
|
||||||
})
|
})
|
||||||
case let .exceptionItem(_, peer, label):
|
case let .exceptionItem(_, peer, label):
|
||||||
|
|||||||
@ -1958,10 +1958,6 @@ public class CameraScreen: ViewController {
|
|||||||
self.navigationPresentation = .flatModal
|
self.navigationPresentation = .flatModal
|
||||||
|
|
||||||
self.requestAudioSession()
|
self.requestAudioSession()
|
||||||
|
|
||||||
if #available(iOS 13.0, *) {
|
|
||||||
try? AVAudioSession.sharedInstance().setAllowHapticsAndSystemSoundsDuringRecording(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init(coder: NSCoder) {
|
required public init(coder: NSCoder) {
|
||||||
@ -1982,7 +1978,11 @@ public class CameraScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func requestAudioSession() {
|
private func requestAudioSession() {
|
||||||
self.audioSessionDisposable = self.context.sharedContext.mediaManager.audioSession.push(audioSessionType: .recordWithOthers, activate: { _ in }, deactivate: { _ in
|
self.audioSessionDisposable = self.context.sharedContext.mediaManager.audioSession.push(audioSessionType: .recordWithOthers, activate: { _ in
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
try? AVAudioSession.sharedInstance().setAllowHapticsAndSystemSoundsDuringRecording(true)
|
||||||
|
}
|
||||||
|
}, deactivate: { _ in
|
||||||
return .single(Void())
|
return .single(Void())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,6 +32,7 @@ swift_library(
|
|||||||
"//submodules/Components/PagerComponent:PagerComponent",
|
"//submodules/Components/PagerComponent:PagerComponent",
|
||||||
"//submodules/PremiumUI",
|
"//submodules/PremiumUI",
|
||||||
"//submodules/ProgressNavigationButtonNode",
|
"//submodules/ProgressNavigationButtonNode",
|
||||||
|
"//submodules/TelegramUI/Components/SwitchComponent",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -16,72 +16,7 @@ import EmojiStatusComponent
|
|||||||
import PremiumUI
|
import PremiumUI
|
||||||
import ProgressNavigationButtonNode
|
import ProgressNavigationButtonNode
|
||||||
import Postbox
|
import Postbox
|
||||||
|
import SwitchComponent
|
||||||
private final class SwitchComponent: Component {
|
|
||||||
typealias EnvironmentType = Empty
|
|
||||||
|
|
||||||
let value: Bool
|
|
||||||
let valueUpdated: (Bool) -> Void
|
|
||||||
|
|
||||||
init(
|
|
||||||
value: Bool,
|
|
||||||
valueUpdated: @escaping (Bool) -> Void
|
|
||||||
) {
|
|
||||||
self.value = value
|
|
||||||
self.valueUpdated = valueUpdated
|
|
||||||
}
|
|
||||||
|
|
||||||
static func ==(lhs: SwitchComponent, rhs: SwitchComponent) -> Bool {
|
|
||||||
if lhs.value != rhs.value {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
final class View: UIView {
|
|
||||||
private let switchView: UISwitch
|
|
||||||
|
|
||||||
private var component: SwitchComponent?
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
self.switchView = UISwitch()
|
|
||||||
|
|
||||||
super.init(frame: frame)
|
|
||||||
|
|
||||||
self.addSubview(self.switchView)
|
|
||||||
|
|
||||||
self.switchView.addTarget(self, action: #selector(self.valueChanged(_:)), for: .valueChanged)
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func valueChanged(_ sender: Any) {
|
|
||||||
self.component?.valueUpdated(self.switchView.isOn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func update(component: SwitchComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
|
|
||||||
self.component = component
|
|
||||||
|
|
||||||
self.switchView.setOn(component.value, animated: !transition.animation.isImmediate)
|
|
||||||
|
|
||||||
self.switchView.sizeToFit()
|
|
||||||
self.switchView.frame = CGRect(origin: .zero, size: self.switchView.frame.size)
|
|
||||||
|
|
||||||
return self.switchView.frame.size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func makeView() -> View {
|
|
||||||
return View(frame: CGRect())
|
|
||||||
}
|
|
||||||
|
|
||||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
|
|
||||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private final class TitleFieldComponent: Component {
|
private final class TitleFieldComponent: Component {
|
||||||
typealias EnvironmentType = Empty
|
typealias EnvironmentType = Empty
|
||||||
|
|||||||
@ -38,5 +38,10 @@ fragment half4 dualFragmentShader(RasterizerData in [[stage_in]],
|
|||||||
float side = 1.0 * aspectRatio;
|
float side = 1.0 * aspectRatio;
|
||||||
float distance = smoothstep(t, -t, sdfRoundedRectangle(uv, float2(0.0, 0.0), float2(side, mix(1.0, side, roundness)), side * roundness));
|
float distance = smoothstep(t, -t, sdfRoundedRectangle(uv, float2(0.0, 0.0), float2(side, mix(1.0, side, roundness)), side * roundness));
|
||||||
|
|
||||||
|
if (roundness > 0.0) {
|
||||||
|
return half4(in.localPos.x, 0.0, 0.0, 1.0);
|
||||||
|
} else {
|
||||||
|
|
||||||
return mix(half4(color, 0.0), half4(color, 1.0 * alpha), distance);
|
return mix(half4(color, 0.0), half4(color, 1.0 * alpha), distance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,25 +9,27 @@ import AccountContext
|
|||||||
|
|
||||||
public struct MediaEditorResultPrivacy: Codable, Equatable {
|
public struct MediaEditorResultPrivacy: Codable, Equatable {
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case type
|
|
||||||
case privacy
|
case privacy
|
||||||
case peers
|
|
||||||
case timeout
|
case timeout
|
||||||
|
case disableForwarding
|
||||||
case archive
|
case archive
|
||||||
}
|
}
|
||||||
|
|
||||||
public let privacy: EngineStoryPrivacy
|
public let privacy: EngineStoryPrivacy
|
||||||
public let timeout: Int
|
public let timeout: Int
|
||||||
public let archive: Bool
|
public let isForwardingDisabled: Bool
|
||||||
|
public let pin: Bool
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
privacy: EngineStoryPrivacy,
|
privacy: EngineStoryPrivacy,
|
||||||
timeout: Int,
|
timeout: Int,
|
||||||
archive: Bool
|
isForwardingDisabled: Bool,
|
||||||
|
pin: Bool
|
||||||
) {
|
) {
|
||||||
self.privacy = privacy
|
self.privacy = privacy
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.archive = archive
|
self.isForwardingDisabled = isForwardingDisabled
|
||||||
|
self.pin = pin
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
@ -35,7 +37,8 @@ public struct MediaEditorResultPrivacy: Codable, Equatable {
|
|||||||
|
|
||||||
self.privacy = try container.decode(EngineStoryPrivacy.self, forKey: .privacy)
|
self.privacy = try container.decode(EngineStoryPrivacy.self, forKey: .privacy)
|
||||||
self.timeout = Int(try container.decode(Int32.self, forKey: .timeout))
|
self.timeout = Int(try container.decode(Int32.self, forKey: .timeout))
|
||||||
self.archive = try container.decode(Bool.self, forKey: .archive)
|
self.isForwardingDisabled = try container.decodeIfPresent(Bool.self, forKey: .disableForwarding) ?? false
|
||||||
|
self.pin = try container.decode(Bool.self, forKey: .archive)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
@ -43,7 +46,8 @@ public struct MediaEditorResultPrivacy: Codable, Equatable {
|
|||||||
|
|
||||||
try container.encode(self.privacy, forKey: .privacy)
|
try container.encode(self.privacy, forKey: .privacy)
|
||||||
try container.encode(Int32(self.timeout), forKey: .timeout)
|
try container.encode(Int32(self.timeout), forKey: .timeout)
|
||||||
try container.encode(self.archive, forKey: .archive)
|
try container.encode(self.isForwardingDisabled, forKey: .disableForwarding)
|
||||||
|
try container.encode(self.pin, forKey: .archive)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -342,8 +342,8 @@ private func verticesData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let relativeSize = CGSize(
|
let relativeSize = CGSize(
|
||||||
width: size.width / containerSize.width,
|
width: size.width / containerSize.width * 2.0,
|
||||||
height: size.height / containerSize.height
|
height: size.height / containerSize.height * 2.0
|
||||||
)
|
)
|
||||||
let relativeOffset = CGPoint(
|
let relativeOffset = CGPoint(
|
||||||
x: position.x / containerSize.width,
|
x: position.x / containerSize.width,
|
||||||
@ -358,24 +358,45 @@ private func verticesData(
|
|||||||
size: relativeSize
|
size: relativeSize
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let cosAngle = Float(cos(rotation))
|
||||||
|
let sinAngle = Float(sin(rotation))
|
||||||
|
|
||||||
|
let centerX = Float(rect.midX)
|
||||||
|
let centerY = Float(rect.midY)
|
||||||
|
|
||||||
|
let halfWidth = Float(rect.width) / 2.0
|
||||||
|
let halfHeight = Float(rect.height) / 2.0
|
||||||
|
|
||||||
|
let x1 = centerX + (halfWidth * cosAngle) - (halfHeight * sinAngle)
|
||||||
|
let y1 = centerY + (halfWidth * sinAngle) + (halfHeight * cosAngle)
|
||||||
|
|
||||||
|
let x2 = centerX - (halfWidth * cosAngle) - (halfHeight * sinAngle)
|
||||||
|
let y2 = centerY - (halfWidth * sinAngle) + (halfHeight * cosAngle)
|
||||||
|
|
||||||
|
let x3 = centerX + (halfWidth * cosAngle) + (halfHeight * sinAngle)
|
||||||
|
let y3 = centerY + (halfWidth * sinAngle) - (halfHeight * cosAngle)
|
||||||
|
|
||||||
|
let x4 = centerX - (halfWidth * cosAngle) + (halfHeight * sinAngle)
|
||||||
|
let y4 = centerY - (halfWidth * sinAngle) - (halfHeight * cosAngle)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
VertexData(
|
VertexData(
|
||||||
pos: simd_float4(x: Float(rect.minX) * 2.0, y: Float(rect.minY) * 2.0, z: z, w: 1),
|
pos: simd_float4(x: x1, y: y1, z: z, w: 1),
|
||||||
texCoord: topLeft,
|
texCoord: topLeft,
|
||||||
localPos: simd_float2(0.0, 0.0)
|
localPos: simd_float2(0.0, 0.0)
|
||||||
),
|
),
|
||||||
VertexData(
|
VertexData(
|
||||||
pos: simd_float4(x: Float(rect.maxX) * 2.0, y: Float(rect.minY) * 2.0, z: z, w: 1),
|
pos: simd_float4(x: x2, y: y2, z: z, w: 1),
|
||||||
texCoord: topRight,
|
texCoord: topRight,
|
||||||
localPos: simd_float2(1.0, 0.0)
|
localPos: simd_float2(1.0, 0.0)
|
||||||
),
|
),
|
||||||
VertexData(
|
VertexData(
|
||||||
pos: simd_float4(x: Float(rect.minX) * 2.0, y: Float(rect.maxY) * 2.0, z: z, w: 1),
|
pos: simd_float4(x: x3, y: y3, z: z, w: 1),
|
||||||
texCoord: bottomLeft,
|
texCoord: bottomLeft,
|
||||||
localPos: simd_float2(0.0, 1.0)
|
localPos: simd_float2(0.0, 1.0)
|
||||||
),
|
),
|
||||||
VertexData(
|
VertexData(
|
||||||
pos: simd_float4(x: Float(rect.maxX) * 2.0, y: Float(rect.maxY) * 2.0, z: z, w: 1),
|
pos: simd_float4(x: x4, y: y4, z: z, w: 1),
|
||||||
texCoord: bottomRight,
|
texCoord: bottomRight,
|
||||||
localPos: simd_float2(1.0, 1.0)
|
localPos: simd_float2(1.0, 1.0)
|
||||||
),
|
),
|
||||||
|
|||||||
@ -562,6 +562,10 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
view.alpha = 1.0
|
view.alpha = 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let buttonView = self.cancelButton.view as? Button.View, let view = buttonView.content as? LottieAnimationComponent.View {
|
||||||
|
view.playOnce()
|
||||||
|
}
|
||||||
|
|
||||||
let buttons = [
|
let buttons = [
|
||||||
self.drawButton,
|
self.drawButton,
|
||||||
self.textButton,
|
self.textButton,
|
||||||
@ -660,8 +664,8 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
LottieAnimationComponent(
|
LottieAnimationComponent(
|
||||||
animation: LottieAnimationComponent.AnimationItem(
|
animation: LottieAnimationComponent.AnimationItem(
|
||||||
name: "media_backToCancel",
|
name: "media_backToCancel",
|
||||||
mode: .still(position: .begin),
|
mode: .still(position: .end),
|
||||||
range: nil
|
range: (0.5, 1.0)
|
||||||
),
|
),
|
||||||
colors: ["__allcolors__": .white],
|
colors: ["__allcolors__": .white],
|
||||||
size: CGSize(width: 33.0, height: 33.0)
|
size: CGSize(width: 33.0, height: 33.0)
|
||||||
@ -693,10 +697,10 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
let doneButtonSize = self.doneButton.update(
|
let doneButtonSize = self.doneButton.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(Button(
|
component: AnyComponent(Button(
|
||||||
content: AnyComponent(Image(
|
content: AnyComponent(DoneButtonComponent(
|
||||||
image: state.image(.done),
|
backgroundColor: UIColor(rgb: 0x007aff),
|
||||||
size: CGSize(width: 33.0, height: 33.0)
|
icon: UIImage(bundleImageName: "Media Editor/Next")!,
|
||||||
)),
|
title: "NEXT")),
|
||||||
action: {
|
action: {
|
||||||
guard let controller = environment.controller() as? MediaEditorScreen else {
|
guard let controller = environment.controller() as? MediaEditorScreen else {
|
||||||
return
|
return
|
||||||
@ -707,7 +711,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: 44.0, height: 44.0)
|
containerSize: CGSize(width: availableSize.width, height: 44.0)
|
||||||
)
|
)
|
||||||
let doneButtonFrame = CGRect(
|
let doneButtonFrame = CGRect(
|
||||||
origin: CGPoint(x: availableSize.width - buttonSideInset - doneButtonSize.width, y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset),
|
origin: CGPoint(x: availableSize.width - buttonSideInset - doneButtonSize.width, y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset),
|
||||||
@ -728,8 +732,8 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
buttonsAvailableWidth = previewSize.width + 260.0
|
buttonsAvailableWidth = previewSize.width + 260.0
|
||||||
buttonsLeftOffset = floorToScreenPixels((availableSize.width - buttonsAvailableWidth) / 2.0)
|
buttonsLeftOffset = floorToScreenPixels((availableSize.width - buttonsAvailableWidth) / 2.0)
|
||||||
} else {
|
} else {
|
||||||
buttonsAvailableWidth = availableSize.width
|
buttonsAvailableWidth = floor(availableSize.width - cancelButtonSize.width * 0.66 - (doneButtonSize.width - cancelButtonSize.width * 0.33) - buttonSideInset * 2.0)
|
||||||
buttonsLeftOffset = 0.0
|
buttonsLeftOffset = floorToScreenPixels(buttonSideInset + cancelButtonSize.width * 0.66)
|
||||||
}
|
}
|
||||||
|
|
||||||
let drawButtonSize = self.drawButton.update(
|
let drawButtonSize = self.drawButton.update(
|
||||||
@ -747,7 +751,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
containerSize: CGSize(width: 40.0, height: 40.0)
|
containerSize: CGSize(width: 40.0, height: 40.0)
|
||||||
)
|
)
|
||||||
let drawButtonFrame = CGRect(
|
let drawButtonFrame = CGRect(
|
||||||
origin: CGPoint(x: buttonsLeftOffset + floorToScreenPixels(buttonsAvailableWidth / 4.0 - 3.0 - drawButtonSize.width / 2.0), y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 1.0),
|
origin: CGPoint(x: buttonsLeftOffset + floorToScreenPixels(buttonsAvailableWidth / 5.0 - drawButtonSize.width / 2.0 - 3.0), y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 1.0),
|
||||||
size: drawButtonSize
|
size: drawButtonSize
|
||||||
)
|
)
|
||||||
if let drawButtonView = self.drawButton.view {
|
if let drawButtonView = self.drawButton.view {
|
||||||
@ -776,7 +780,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
containerSize: CGSize(width: 40.0, height: 40.0)
|
containerSize: CGSize(width: 40.0, height: 40.0)
|
||||||
)
|
)
|
||||||
let textButtonFrame = CGRect(
|
let textButtonFrame = CGRect(
|
||||||
origin: CGPoint(x: buttonsLeftOffset + floorToScreenPixels(buttonsAvailableWidth / 2.5 + 5.0 - textButtonSize.width / 2.0), y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 2.0),
|
origin: CGPoint(x: buttonsLeftOffset + floorToScreenPixels(buttonsAvailableWidth / 5.0 * 2.0 - textButtonSize.width / 2.0), y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 2.0),
|
||||||
size: textButtonSize
|
size: textButtonSize
|
||||||
)
|
)
|
||||||
if let textButtonView = self.textButton.view {
|
if let textButtonView = self.textButton.view {
|
||||||
@ -805,7 +809,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
containerSize: CGSize(width: 40.0, height: 40.0)
|
containerSize: CGSize(width: 40.0, height: 40.0)
|
||||||
)
|
)
|
||||||
let stickerButtonFrame = CGRect(
|
let stickerButtonFrame = CGRect(
|
||||||
origin: CGPoint(x: floorToScreenPixels(availableSize.width - buttonsLeftOffset - buttonsAvailableWidth / 2.5 - 5.0 - stickerButtonSize.width / 2.0), y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 2.0),
|
origin: CGPoint(x: buttonsLeftOffset + floorToScreenPixels(buttonsAvailableWidth / 5.0 * 3.0 - stickerButtonSize.width / 2.0), y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 2.0),
|
||||||
size: stickerButtonSize
|
size: stickerButtonSize
|
||||||
)
|
)
|
||||||
if let stickerButtonView = self.stickerButton.view {
|
if let stickerButtonView = self.stickerButton.view {
|
||||||
@ -834,7 +838,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
containerSize: CGSize(width: 40.0, height: 40.0)
|
containerSize: CGSize(width: 40.0, height: 40.0)
|
||||||
)
|
)
|
||||||
let toolsButtonFrame = CGRect(
|
let toolsButtonFrame = CGRect(
|
||||||
origin: CGPoint(x: buttonsLeftOffset + floorToScreenPixels(buttonsAvailableWidth / 4.0 * 3.0 + 3.0 - toolsButtonSize.width / 2.0), y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 1.0),
|
origin: CGPoint(x: buttonsLeftOffset + floorToScreenPixels(buttonsAvailableWidth / 5.0 * 4.0 - toolsButtonSize.width / 2.0 + 3.0), y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 1.0),
|
||||||
size: toolsButtonSize
|
size: toolsButtonSize
|
||||||
)
|
)
|
||||||
if let toolsButtonView = self.toolsButton.view {
|
if let toolsButtonView = self.toolsButton.view {
|
||||||
@ -923,9 +927,6 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
default:
|
default:
|
||||||
timeoutValue = "24"
|
timeoutValue = "24"
|
||||||
}
|
}
|
||||||
if component.privacy.archive {
|
|
||||||
timeoutValue = "∞"
|
|
||||||
}
|
|
||||||
timeoutSelected = false
|
timeoutSelected = false
|
||||||
|
|
||||||
var inputPanelAvailableWidth = previewSize.width
|
var inputPanelAvailableWidth = previewSize.width
|
||||||
@ -1530,7 +1531,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
var privacy: MediaEditorResultPrivacy = MediaEditorResultPrivacy(privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 86400, archive: false)
|
var privacy: MediaEditorResultPrivacy = MediaEditorResultPrivacy(
|
||||||
|
privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []),
|
||||||
|
timeout: 86400,
|
||||||
|
isForwardingDisabled: false,
|
||||||
|
pin: false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var state = State() {
|
var state = State() {
|
||||||
@ -1846,8 +1852,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if case let .asset(asset) = subject, asset.mediaType == .video {
|
}
|
||||||
//#if DEBUG
|
//#if DEBUG
|
||||||
|
// if case let .asset(asset) = subject, asset.mediaType == .video {
|
||||||
// let videoEntity = DrawingStickerEntity(content: .dualVideoReference)
|
// let videoEntity = DrawingStickerEntity(content: .dualVideoReference)
|
||||||
// videoEntity.referenceDrawingSize = storyDimensions
|
// videoEntity.referenceDrawingSize = storyDimensions
|
||||||
// videoEntity.scale = 1.49
|
// videoEntity.scale = 1.49
|
||||||
@ -1863,8 +1870,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
// }
|
||||||
//#endif
|
//#endif
|
||||||
}
|
|
||||||
|
|
||||||
self.gradientColorsDisposable = mediaEditor.gradientColors.start(next: { [weak self] colors in
|
self.gradientColorsDisposable = mediaEditor.gradientColors.start(next: { [weak self] colors in
|
||||||
if let self, let colors {
|
if let self, let colors {
|
||||||
@ -2571,7 +2578,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 5.0), size: CGSize())
|
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 5.0), size: CGSize())
|
||||||
|
|
||||||
let text: String
|
let text: String
|
||||||
if controller.state.privacy.archive {
|
if controller.state.privacy.pin {
|
||||||
text = "Story will be kept on your page."
|
text = "Story will be kept on your page."
|
||||||
} else {
|
} else {
|
||||||
text = "Story will disappear in 24 hours."
|
text = "Story will disappear in 24 hours."
|
||||||
@ -3040,7 +3047,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
|
|
||||||
if isEditing {
|
if isEditing {
|
||||||
if let initialPrivacy {
|
if let initialPrivacy {
|
||||||
self.state.privacy = MediaEditorResultPrivacy(privacy: initialPrivacy, timeout: 86400, archive: false)
|
self.state.privacy = MediaEditorResultPrivacy(
|
||||||
|
privacy: initialPrivacy,
|
||||||
|
timeout: 86400,
|
||||||
|
isForwardingDisabled: false,
|
||||||
|
pin: false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let _ = combineLatest(
|
let _ = combineLatest(
|
||||||
@ -3050,7 +3062,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
).start(next: { [weak self] state, peer in
|
).start(next: { [weak self] state, peer in
|
||||||
if let self, var privacy = state?.privacy {
|
if let self, var privacy = state?.privacy {
|
||||||
if case let .user(user) = peer, !user.isPremium && privacy.timeout != 86400 {
|
if case let .user(user) = peer, !user.isPremium && privacy.timeout != 86400 {
|
||||||
privacy = MediaEditorResultPrivacy(privacy: privacy.privacy, timeout: 86400, archive: false)
|
privacy = MediaEditorResultPrivacy(privacy: privacy.privacy, timeout: 86400, isForwardingDisabled: privacy.isForwardingDisabled, pin: privacy.pin)
|
||||||
}
|
}
|
||||||
self.state.privacy = privacy
|
self.state.privacy = privacy
|
||||||
}
|
}
|
||||||
@ -3080,36 +3092,41 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
|
|
||||||
let privacy = privacy ?? self.state.privacy
|
let privacy = privacy ?? self.state.privacy
|
||||||
|
|
||||||
let stateContext = ShareWithPeersScreen.StateContext(context: self.context, subject: .stories, initialPeerIds: Set(privacy.privacy.additionallyIncludePeers))
|
let stateContext = ShareWithPeersScreen.StateContext(context: self.context, subject: .stories(editing: false), initialPeerIds: Set(privacy.privacy.additionallyIncludePeers))
|
||||||
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
|
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let initialPrivacy = privacy.privacy
|
let initialPrivacy = privacy.privacy
|
||||||
let timeout = privacy.timeout
|
let timeout = privacy.timeout
|
||||||
let archive = privacy.archive
|
|
||||||
self.push(
|
self.push(
|
||||||
ShareWithPeersScreen(
|
ShareWithPeersScreen(
|
||||||
context: self.context,
|
context: self.context,
|
||||||
initialPrivacy: initialPrivacy,
|
initialPrivacy: initialPrivacy,
|
||||||
timeout: timeout,
|
allowScreenshots: !privacy.isForwardingDisabled,
|
||||||
|
pin: privacy.pin,
|
||||||
stateContext: stateContext,
|
stateContext: stateContext,
|
||||||
completion: { [weak self] privacy in
|
completion: { [weak self] privacy, allowScreenshots, pin in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.state.privacy = MediaEditorResultPrivacy(privacy: privacy, timeout: timeout, archive: archive)
|
self.state.privacy = MediaEditorResultPrivacy(privacy: privacy, timeout: timeout, isForwardingDisabled: !allowScreenshots, pin: pin)
|
||||||
completion()
|
completion()
|
||||||
},
|
},
|
||||||
editCategory: { [weak self] privacy in
|
editCategory: { [weak self] privacy, allowScreenshots, pin in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.openEditCategory(privacy: privacy, completion: { [weak self] privacy in
|
self.openEditCategory(privacy: privacy, isForwardingDisabled: !allowScreenshots, pin: pin, completion: { [weak self] privacy in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.openPrivacySettings(MediaEditorResultPrivacy(privacy: privacy, timeout: timeout, archive: archive), completion: completion)
|
self.openPrivacySettings(MediaEditorResultPrivacy(
|
||||||
|
privacy: privacy,
|
||||||
|
timeout: timeout,
|
||||||
|
isForwardingDisabled: !allowScreenshots,
|
||||||
|
pin: pin
|
||||||
|
), completion: completion)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -3117,7 +3134,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private func openEditCategory(privacy: EngineStoryPrivacy, completion: @escaping (EngineStoryPrivacy) -> Void) {
|
private func openEditCategory(privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, pin: Bool, completion: @escaping (EngineStoryPrivacy) -> Void) {
|
||||||
let stateContext = ShareWithPeersScreen.StateContext(context: self.context, subject: .contacts(privacy.base), initialPeerIds: Set(privacy.additionallyIncludePeers))
|
let stateContext = ShareWithPeersScreen.StateContext(context: self.context, subject: .contacts(privacy.base), initialPeerIds: Set(privacy.additionallyIncludePeers))
|
||||||
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
|
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -3128,9 +3145,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
ShareWithPeersScreen(
|
ShareWithPeersScreen(
|
||||||
context: self.context,
|
context: self.context,
|
||||||
initialPrivacy: privacy,
|
initialPrivacy: privacy,
|
||||||
timeout: 0,
|
allowScreenshots: !isForwardingDisabled,
|
||||||
|
pin: pin,
|
||||||
stateContext: stateContext,
|
stateContext: stateContext,
|
||||||
completion: { [weak self] result in
|
completion: { [weak self] result, isForwardingDisabled, pin in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -3141,7 +3159,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
completion(result)
|
completion(result)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
editCategory: { _ in }
|
editCategory: { _, _, _ in }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -3152,16 +3170,16 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
|
|
||||||
var items: [ContextMenuItem] = []
|
var items: [ContextMenuItem] = []
|
||||||
|
|
||||||
let updateTimeout: (Int?, Bool) -> Void = { [weak self] timeout, archive in
|
let updateTimeout: (Int?) -> Void = { [weak self] timeout in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.state.privacy = MediaEditorResultPrivacy(privacy: self.state.privacy.privacy, timeout: timeout ?? 86400, archive: archive)
|
self.state.privacy = MediaEditorResultPrivacy(privacy: self.state.privacy.privacy, timeout: timeout ?? 86400, isForwardingDisabled: self.state.privacy.isForwardingDisabled, pin: self.state.privacy.pin)
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = "Choose how long the story will be visible."
|
let title = "Choose how long the story will be visible."
|
||||||
let currentValue = self.state.privacy.timeout
|
let currentValue = self.state.privacy.timeout
|
||||||
let currentArchived = self.state.privacy.archive
|
let currentArchived = self.state.privacy.pin
|
||||||
let emptyAction: ((ContextMenuActionItem.Action) -> Void)? = nil
|
let emptyAction: ((ContextMenuActionItem.Action) -> Void)? = nil
|
||||||
|
|
||||||
items.append(.action(ContextMenuActionItem(text: title, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: emptyAction)))
|
items.append(.action(ContextMenuActionItem(text: title, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: emptyAction)))
|
||||||
@ -3176,7 +3194,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
a(.default)
|
a(.default)
|
||||||
|
|
||||||
if hasPremium {
|
if hasPremium {
|
||||||
updateTimeout(3600 * 6, false)
|
updateTimeout(3600 * 6)
|
||||||
} else {
|
} else {
|
||||||
self?.presentTimeoutPremiumSuggestion(3600 * 6)
|
self?.presentTimeoutPremiumSuggestion(3600 * 6)
|
||||||
}
|
}
|
||||||
@ -3191,7 +3209,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
a(.default)
|
a(.default)
|
||||||
|
|
||||||
if hasPremium {
|
if hasPremium {
|
||||||
updateTimeout(3600 * 12, false)
|
updateTimeout(3600 * 12)
|
||||||
} else {
|
} else {
|
||||||
self?.presentTimeoutPremiumSuggestion(3600 * 12)
|
self?.presentTimeoutPremiumSuggestion(3600 * 12)
|
||||||
}
|
}
|
||||||
@ -3201,7 +3219,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}, action: { _, a in
|
}, action: { _, a in
|
||||||
a(.default)
|
a(.default)
|
||||||
|
|
||||||
updateTimeout(86400, false)
|
updateTimeout(86400)
|
||||||
})))
|
})))
|
||||||
items.append(.action(ContextMenuActionItem(text: "48 Hours", icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: "48 Hours", icon: { theme in
|
||||||
if !hasPremium {
|
if !hasPremium {
|
||||||
@ -3213,7 +3231,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
a(.default)
|
a(.default)
|
||||||
|
|
||||||
if hasPremium {
|
if hasPremium {
|
||||||
updateTimeout(86400 * 2, false)
|
updateTimeout(86400 * 2)
|
||||||
} else {
|
} else {
|
||||||
self?.presentTimeoutPremiumSuggestion(86400 * 2)
|
self?.presentTimeoutPremiumSuggestion(86400 * 2)
|
||||||
}
|
}
|
||||||
@ -3873,7 +3891,7 @@ final class PrivacyButtonComponent: CombinedComponent {
|
|||||||
|
|
||||||
let text = text.update(
|
let text = text.update(
|
||||||
component: Text(
|
component: Text(
|
||||||
text: "\(context.component.text)",
|
text: context.component.text,
|
||||||
font: Font.medium(14.0),
|
font: Font.medium(14.0),
|
||||||
color: .white
|
color: .white
|
||||||
),
|
),
|
||||||
@ -3907,6 +3925,92 @@ final class PrivacyButtonComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class DoneButtonComponent: CombinedComponent {
|
||||||
|
let backgroundColor: UIColor
|
||||||
|
let icon: UIImage
|
||||||
|
let title: String?
|
||||||
|
|
||||||
|
init(
|
||||||
|
backgroundColor: UIColor,
|
||||||
|
icon: UIImage,
|
||||||
|
title: String?
|
||||||
|
) {
|
||||||
|
self.backgroundColor = backgroundColor
|
||||||
|
self.icon = icon
|
||||||
|
self.title = title
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: DoneButtonComponent, rhs: DoneButtonComponent) -> Bool {
|
||||||
|
if lhs.backgroundColor != rhs.backgroundColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.title != rhs.title {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
static var body: Body {
|
||||||
|
let background = Child(RoundedRectangle.self)
|
||||||
|
let icon = Child(Image.self)
|
||||||
|
let text = Child(Text.self)
|
||||||
|
|
||||||
|
return { context in
|
||||||
|
let icon = icon.update(
|
||||||
|
component: Image(image: context.component.icon, tintColor: .white, size: CGSize(width: 10.0, height: 16.0)),
|
||||||
|
availableSize: CGSize(width: 180.0, height: 100.0),
|
||||||
|
transition: .immediate
|
||||||
|
)
|
||||||
|
|
||||||
|
let backgroundHeight: CGFloat = 33.0
|
||||||
|
|
||||||
|
var textWidth: CGFloat = 0.0
|
||||||
|
var title: _UpdatedChildComponent?
|
||||||
|
if let titleText = context.component.title {
|
||||||
|
title = text.update(
|
||||||
|
component: Text(
|
||||||
|
text: titleText,
|
||||||
|
font: Font.with(size: 16.0, design: .round, weight: .semibold),
|
||||||
|
color: .white
|
||||||
|
),
|
||||||
|
availableSize: CGSize(width: 180.0, height: 100.0),
|
||||||
|
transition: .immediate
|
||||||
|
)
|
||||||
|
textWidth = title!.size.width
|
||||||
|
}
|
||||||
|
|
||||||
|
var backgroundSize = CGSize(width: 33.0, height: backgroundHeight)
|
||||||
|
if !textWidth.isZero {
|
||||||
|
backgroundSize.width += textWidth + 7.0
|
||||||
|
}
|
||||||
|
|
||||||
|
let background = background.update(
|
||||||
|
component: RoundedRectangle(color: context.component.backgroundColor, cornerRadius: backgroundHeight / 2.0),
|
||||||
|
availableSize: backgroundSize,
|
||||||
|
transition: .immediate
|
||||||
|
)
|
||||||
|
context.add(background
|
||||||
|
.position(CGPoint(x: backgroundSize.width / 2.0, y: backgroundSize.height / 2.0))
|
||||||
|
.cornerRadius(min(backgroundSize.width, backgroundSize.height) / 2.0)
|
||||||
|
.clipsToBounds(true)
|
||||||
|
)
|
||||||
|
|
||||||
|
if let title {
|
||||||
|
context.add(title
|
||||||
|
.position(CGPoint(x: title.size.width / 2.0 + 15.0, y: backgroundHeight / 2.0))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.add(icon
|
||||||
|
.position(CGPoint(x: background.size.width - 16.0, y: backgroundSize.height / 2.0))
|
||||||
|
)
|
||||||
|
|
||||||
|
return backgroundSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
|
private final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
|
||||||
private let controller: ViewController
|
private let controller: ViewController
|
||||||
private let sourceView: UIView
|
private let sourceView: UIView
|
||||||
|
|||||||
@ -284,6 +284,8 @@ private final class MediaToolsScreenComponent: Component {
|
|||||||
func animateOutToEditor(completion: @escaping () -> Void) {
|
func animateOutToEditor(completion: @escaping () -> Void) {
|
||||||
self.animatingOut = true
|
self.animatingOut = true
|
||||||
|
|
||||||
|
self.cancelButton.view?.isHidden = true
|
||||||
|
|
||||||
let buttons = [
|
let buttons = [
|
||||||
self.adjustmentsButton,
|
self.adjustmentsButton,
|
||||||
self.tintButton,
|
self.tintButton,
|
||||||
|
|||||||
@ -35,6 +35,8 @@ swift_library(
|
|||||||
"//submodules/LocalizedPeerData",
|
"//submodules/LocalizedPeerData",
|
||||||
"//submodules/TelegramUI/Components/Stories/PeerListItemComponent",
|
"//submodules/TelegramUI/Components/Stories/PeerListItemComponent",
|
||||||
"//submodules/TelegramUI/Components/LottieComponent",
|
"//submodules/TelegramUI/Components/LottieComponent",
|
||||||
|
"//submodules/TelegramUI/Components/SwitchComponent",
|
||||||
|
"//submodules/TooltipUI",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -20,7 +20,6 @@ final class CategoryListItemComponent: Component {
|
|||||||
|
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let sideInset: CGFloat
|
|
||||||
let title: String
|
let title: String
|
||||||
let color: ShareWithPeersScreenComponent.CategoryColor
|
let color: ShareWithPeersScreenComponent.CategoryColor
|
||||||
let iconName: String?
|
let iconName: String?
|
||||||
@ -33,7 +32,6 @@ final class CategoryListItemComponent: Component {
|
|||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
sideInset: CGFloat,
|
|
||||||
title: String,
|
title: String,
|
||||||
color: ShareWithPeersScreenComponent.CategoryColor,
|
color: ShareWithPeersScreenComponent.CategoryColor,
|
||||||
iconName: String?,
|
iconName: String?,
|
||||||
@ -45,7 +43,6 @@ final class CategoryListItemComponent: Component {
|
|||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.sideInset = sideInset
|
|
||||||
self.title = title
|
self.title = title
|
||||||
self.color = color
|
self.color = color
|
||||||
self.iconName = iconName
|
self.iconName = iconName
|
||||||
@ -63,9 +60,6 @@ final class CategoryListItemComponent: Component {
|
|||||||
if lhs.theme !== rhs.theme {
|
if lhs.theme !== rhs.theme {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.sideInset != rhs.sideInset {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if lhs.title != rhs.title {
|
if lhs.title != rhs.title {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -179,11 +173,11 @@ final class CategoryListItemComponent: Component {
|
|||||||
|
|
||||||
let contextInset: CGFloat = 0.0
|
let contextInset: CGFloat = 0.0
|
||||||
|
|
||||||
let height: CGFloat = 60.0
|
let height: CGFloat = 56.0
|
||||||
let verticalInset: CGFloat = 1.0
|
let verticalInset: CGFloat = 0.0
|
||||||
var leftInset: CGFloat = 62.0 + component.sideInset
|
var leftInset: CGFloat = 62.0
|
||||||
let rightInset: CGFloat = contextInset * 2.0 + 8.0 + component.sideInset
|
let rightInset: CGFloat = contextInset * 2.0 + 8.0
|
||||||
var avatarLeftInset: CGFloat = component.sideInset + 10.0
|
var avatarLeftInset: CGFloat = 10.0
|
||||||
|
|
||||||
if case let .editing(isSelected, isTinted) = component.selectionState {
|
if case let .editing(isSelected, isTinted) = component.selectionState {
|
||||||
leftInset += 44.0
|
leftInset += 44.0
|
||||||
@ -310,7 +304,7 @@ final class CategoryListItemComponent: Component {
|
|||||||
labelArrowView.isUserInteractionEnabled = false
|
labelArrowView.isUserInteractionEnabled = false
|
||||||
self.containerButton.addSubview(labelArrowView)
|
self.containerButton.addSubview(labelArrowView)
|
||||||
}
|
}
|
||||||
transition.setFrame(view: labelArrowView, frame: CGRect(origin: CGPoint(x: titleFrame.minX + labelSize.width + 5.0, y: titleFrame.maxY + titleSpacing + floorToScreenPixels(labelSize.height / 2.0 - labelArrowSize.height / 2.0)), size: labelArrowSize))
|
transition.setFrame(view: labelArrowView, frame: CGRect(origin: CGPoint(x: titleFrame.minX + labelSize.width + 5.0, y: titleFrame.maxY + titleSpacing + floorToScreenPixels(labelSize.height / 2.0 - labelArrowSize.height / 2.0) + 1.0 ), size: labelArrowSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
if themeUpdated {
|
if themeUpdated {
|
||||||
|
|||||||
@ -0,0 +1,157 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import ComponentFlow
|
||||||
|
import MultilineTextComponent
|
||||||
|
import TelegramPresentationData
|
||||||
|
import SwitchComponent
|
||||||
|
|
||||||
|
final class OptionListItemComponent: Component {
|
||||||
|
enum SelectionState: Equatable {
|
||||||
|
case none
|
||||||
|
case editing(isSelected: Bool, isTinted: Bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
let theme: PresentationTheme
|
||||||
|
let title: String
|
||||||
|
let hasNext: Bool
|
||||||
|
let selected: Bool
|
||||||
|
let selectionChanged: (Bool) -> Void
|
||||||
|
|
||||||
|
init(
|
||||||
|
theme: PresentationTheme,
|
||||||
|
title: String,
|
||||||
|
hasNext: Bool,
|
||||||
|
selected: Bool,
|
||||||
|
selectionChanged: @escaping (Bool) -> Void
|
||||||
|
) {
|
||||||
|
self.theme = theme
|
||||||
|
self.title = title
|
||||||
|
self.hasNext = hasNext
|
||||||
|
self.selected = selected
|
||||||
|
self.selectionChanged = selectionChanged
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: OptionListItemComponent, rhs: OptionListItemComponent) -> Bool {
|
||||||
|
if lhs.theme !== rhs.theme {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.title != rhs.title {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.selected != rhs.selected {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.hasNext != rhs.hasNext {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
final class View: UIView {
|
||||||
|
private let containerButton: HighlightTrackingButton
|
||||||
|
|
||||||
|
private let title = ComponentView<Empty>()
|
||||||
|
private let switchComponent = ComponentView<Empty>()
|
||||||
|
private let separatorLayer: SimpleLayer
|
||||||
|
|
||||||
|
private var component: OptionListItemComponent?
|
||||||
|
private weak var state: EmptyComponentState?
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
self.separatorLayer = SimpleLayer()
|
||||||
|
|
||||||
|
self.containerButton = HighlightTrackingButton()
|
||||||
|
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.layer.addSublayer(self.separatorLayer)
|
||||||
|
self.addSubview(self.containerButton)
|
||||||
|
|
||||||
|
self.containerButton.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func pressed() {
|
||||||
|
// guard let component = self.component else {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// if case .editing(true, _) = component.selectionState {
|
||||||
|
// component.secondaryAction()
|
||||||
|
// } else {
|
||||||
|
// component.action()
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(component: OptionListItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
|
let themeUpdated = self.component?.theme !== component.theme
|
||||||
|
|
||||||
|
self.component = component
|
||||||
|
self.state = state
|
||||||
|
|
||||||
|
let height: CGFloat = 44.0
|
||||||
|
let verticalInset: CGFloat = 0.0
|
||||||
|
let leftInset: CGFloat = 16.0
|
||||||
|
let rightInset: CGFloat = 16.0
|
||||||
|
|
||||||
|
let switchSize = self.switchComponent.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(SwitchComponent(
|
||||||
|
tintColor: nil,
|
||||||
|
value: component.selected,
|
||||||
|
valueUpdated: { selected in
|
||||||
|
component.selectionChanged(selected)
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
let titleSize = self.title.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(string: component.title, font: Font.regular(17.0), textColor: component.theme.list.itemPrimaryTextColor))
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floorToScreenPixels((height - titleSize.height) / 2.0)), size: titleSize)
|
||||||
|
if let titleView = self.title.view {
|
||||||
|
if titleView.superview == nil {
|
||||||
|
titleView.isUserInteractionEnabled = false
|
||||||
|
self.containerButton.addSubview(titleView)
|
||||||
|
}
|
||||||
|
titleView.frame = titleFrame
|
||||||
|
}
|
||||||
|
if let switchView = self.switchComponent.view {
|
||||||
|
if switchView.superview == nil {
|
||||||
|
self.containerButton.addSubview(switchView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: switchView, frame: CGRect(origin: CGPoint(x: availableSize.width - rightInset - switchSize.width, y: floorToScreenPixels((height - switchSize.height) / 2.0)), size: switchSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
if themeUpdated {
|
||||||
|
self.separatorLayer.backgroundColor = component.theme.list.itemPlainSeparatorColor.cgColor
|
||||||
|
}
|
||||||
|
transition.setFrame(layer: self.separatorLayer, frame: CGRect(origin: CGPoint(x: leftInset, y: height), size: CGSize(width: availableSize.width - leftInset, height: UIScreenPixel)))
|
||||||
|
self.separatorLayer.isHidden = !component.hasNext
|
||||||
|
|
||||||
|
let containerFrame = CGRect(origin: CGPoint(x: 0.0, y: verticalInset), size: CGSize(width: availableSize.width, height: height - verticalInset * 2.0))
|
||||||
|
transition.setFrame(view: self.containerButton, frame: containerFrame)
|
||||||
|
|
||||||
|
return CGSize(width: availableSize.width, height: height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeView() -> View {
|
||||||
|
return View(frame: CGRect())
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
|
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,344 +0,0 @@
|
|||||||
//import Foundation
|
|
||||||
//import UIKit
|
|
||||||
//import Display
|
|
||||||
//import AsyncDisplayKit
|
|
||||||
//import ComponentFlow
|
|
||||||
//import SwiftSignalKit
|
|
||||||
//import AccountContext
|
|
||||||
//import TelegramCore
|
|
||||||
//import MultilineTextComponent
|
|
||||||
//import AvatarNode
|
|
||||||
//import TelegramPresentationData
|
|
||||||
//import CheckNode
|
|
||||||
//import TelegramStringFormatting
|
|
||||||
//import PeerPresenceStatusManager
|
|
||||||
//
|
|
||||||
//private let avatarFont = avatarPlaceholderFont(size: 15.0)
|
|
||||||
//
|
|
||||||
//final class PeerListItemComponent: Component {
|
|
||||||
// enum SelectionState: Equatable {
|
|
||||||
// case none
|
|
||||||
// case editing(isSelected: Bool, isTinted: Bool)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let context: AccountContext
|
|
||||||
// let theme: PresentationTheme
|
|
||||||
// let strings: PresentationStrings
|
|
||||||
// let sideInset: CGFloat
|
|
||||||
// let title: String
|
|
||||||
// let peer: EnginePeer?
|
|
||||||
// let subtitle: String?
|
|
||||||
// let presence: EnginePeer.Presence?
|
|
||||||
// let selectionState: SelectionState
|
|
||||||
// let hasNext: Bool
|
|
||||||
// let action: (EnginePeer) -> Void
|
|
||||||
//
|
|
||||||
// init(
|
|
||||||
// context: AccountContext,
|
|
||||||
// theme: PresentationTheme,
|
|
||||||
// strings: PresentationStrings,
|
|
||||||
// sideInset: CGFloat,
|
|
||||||
// title: String,
|
|
||||||
// peer: EnginePeer?,
|
|
||||||
// subtitle: String?,
|
|
||||||
// presence: EnginePeer.Presence?,
|
|
||||||
// selectionState: SelectionState,
|
|
||||||
// hasNext: Bool,
|
|
||||||
// action: @escaping (EnginePeer) -> Void
|
|
||||||
// ) {
|
|
||||||
// self.context = context
|
|
||||||
// self.theme = theme
|
|
||||||
// self.strings = strings
|
|
||||||
// self.sideInset = sideInset
|
|
||||||
// self.title = title
|
|
||||||
// self.peer = peer
|
|
||||||
// self.subtitle = subtitle
|
|
||||||
// self.presence = presence
|
|
||||||
// self.selectionState = selectionState
|
|
||||||
// self.hasNext = hasNext
|
|
||||||
// self.action = action
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// static func ==(lhs: PeerListItemComponent, rhs: PeerListItemComponent) -> Bool {
|
|
||||||
// if lhs.context !== rhs.context {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// if lhs.theme !== rhs.theme {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// if lhs.strings !== rhs.strings {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// if lhs.sideInset != rhs.sideInset {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// if lhs.title != rhs.title {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// if lhs.peer != rhs.peer {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// if lhs.subtitle != rhs.subtitle {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// if lhs.presence != rhs.presence {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// if lhs.selectionState != rhs.selectionState {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// if lhs.hasNext != rhs.hasNext {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// return true
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// final class View: UIView {
|
|
||||||
// private let containerButton: HighlightTrackingButton
|
|
||||||
//
|
|
||||||
// private let title = ComponentView<Empty>()
|
|
||||||
// private let label = ComponentView<Empty>()
|
|
||||||
// private let separatorLayer: SimpleLayer
|
|
||||||
// private let avatarNode: AvatarNode
|
|
||||||
//
|
|
||||||
// private var checkLayer: CheckLayer?
|
|
||||||
//
|
|
||||||
// private var component: PeerListItemComponent?
|
|
||||||
// private weak var state: EmptyComponentState?
|
|
||||||
//
|
|
||||||
// private var presenceManager: PeerPresenceStatusManager?
|
|
||||||
//
|
|
||||||
// override init(frame: CGRect) {
|
|
||||||
// self.separatorLayer = SimpleLayer()
|
|
||||||
//
|
|
||||||
// self.containerButton = HighlightTrackingButton()
|
|
||||||
//
|
|
||||||
// self.avatarNode = AvatarNode(font: avatarFont)
|
|
||||||
// self.avatarNode.isLayerBacked = true
|
|
||||||
//
|
|
||||||
// super.init(frame: frame)
|
|
||||||
//
|
|
||||||
// self.layer.addSublayer(self.separatorLayer)
|
|
||||||
// self.addSubview(self.containerButton)
|
|
||||||
// self.containerButton.layer.addSublayer(self.avatarNode.layer)
|
|
||||||
//
|
|
||||||
// self.containerButton.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// required init?(coder: NSCoder) {
|
|
||||||
// fatalError("init(coder:) has not been implemented")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @objc private func pressed() {
|
|
||||||
// guard let component = self.component, let peer = component.peer else {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// component.action(peer)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func update(component: PeerListItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
|
||||||
// let animationHint = transition.userData(ShareWithPeersScreenComponent.AnimationHint.self)
|
|
||||||
// var synchronousLoad = false
|
|
||||||
// if let animationHint, animationHint.contentReloaded {
|
|
||||||
// synchronousLoad = true
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let themeUpdated = self.component?.theme !== component.theme
|
|
||||||
//
|
|
||||||
// var hasSelectionUpdated = false
|
|
||||||
// if let previousComponent = self.component {
|
|
||||||
// switch previousComponent.selectionState {
|
|
||||||
// case .none:
|
|
||||||
// if case .none = component.selectionState {
|
|
||||||
// } else {
|
|
||||||
// hasSelectionUpdated = true
|
|
||||||
// }
|
|
||||||
// case .editing:
|
|
||||||
// if case .editing = component.selectionState {
|
|
||||||
// } else {
|
|
||||||
// hasSelectionUpdated = true
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if let presence = component.presence {
|
|
||||||
// let presenceManager: PeerPresenceStatusManager
|
|
||||||
// if let current = self.presenceManager {
|
|
||||||
// presenceManager = current
|
|
||||||
// } else {
|
|
||||||
// presenceManager = PeerPresenceStatusManager(update: { [weak self] in
|
|
||||||
// self?.state?.updated(transition: .immediate)
|
|
||||||
// })
|
|
||||||
// self.presenceManager = presenceManager
|
|
||||||
// }
|
|
||||||
// presenceManager.reset(presence: presence)
|
|
||||||
// } else {
|
|
||||||
// if self.presenceManager != nil {
|
|
||||||
// self.presenceManager = nil
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// self.component = component
|
|
||||||
// self.state = state
|
|
||||||
//
|
|
||||||
// let contextInset: CGFloat = 0.0
|
|
||||||
//
|
|
||||||
// let height: CGFloat = 60.0
|
|
||||||
// let verticalInset: CGFloat = 1.0
|
|
||||||
// var leftInset: CGFloat = 62.0 + component.sideInset
|
|
||||||
// let rightInset: CGFloat = contextInset * 2.0 + 8.0 + component.sideInset
|
|
||||||
// var avatarLeftInset: CGFloat = component.sideInset + 10.0
|
|
||||||
//
|
|
||||||
// if case let .editing(isSelected, isTinted) = component.selectionState {
|
|
||||||
// leftInset += 44.0
|
|
||||||
// avatarLeftInset += 44.0
|
|
||||||
// let checkSize: CGFloat = 22.0
|
|
||||||
//
|
|
||||||
// let checkLayer: CheckLayer
|
|
||||||
// if let current = self.checkLayer {
|
|
||||||
// checkLayer = current
|
|
||||||
// if themeUpdated {
|
|
||||||
// var theme = CheckNodeTheme(theme: component.theme, style: .plain)
|
|
||||||
// if isTinted {
|
|
||||||
// theme.backgroundColor = theme.backgroundColor.mixedWith(component.theme.list.itemBlocksBackgroundColor, alpha: 0.5)
|
|
||||||
// }
|
|
||||||
// checkLayer.theme = theme
|
|
||||||
// }
|
|
||||||
// checkLayer.setSelected(isSelected, animated: !transition.animation.isImmediate)
|
|
||||||
// } else {
|
|
||||||
// var theme = CheckNodeTheme(theme: component.theme, style: .plain)
|
|
||||||
// if isTinted {
|
|
||||||
// theme.backgroundColor = theme.backgroundColor.mixedWith(component.theme.list.itemBlocksBackgroundColor, alpha: 0.5)
|
|
||||||
// }
|
|
||||||
// checkLayer = CheckLayer(theme: theme)
|
|
||||||
// self.checkLayer = checkLayer
|
|
||||||
// self.containerButton.layer.addSublayer(checkLayer)
|
|
||||||
// checkLayer.frame = CGRect(origin: CGPoint(x: -checkSize, y: floor((height - verticalInset * 2.0 - checkSize) / 2.0)), size: CGSize(width: checkSize, height: checkSize))
|
|
||||||
// checkLayer.setSelected(isSelected, animated: false)
|
|
||||||
// checkLayer.setNeedsDisplay()
|
|
||||||
// }
|
|
||||||
// transition.setFrame(layer: checkLayer, frame: CGRect(origin: CGPoint(x: floor((54.0 - checkSize) * 0.5), y: floor((height - verticalInset * 2.0 - checkSize) / 2.0)), size: CGSize(width: checkSize, height: checkSize)))
|
|
||||||
// } else {
|
|
||||||
// if let checkLayer = self.checkLayer {
|
|
||||||
// self.checkLayer = nil
|
|
||||||
// transition.setPosition(layer: checkLayer, position: CGPoint(x: -checkLayer.bounds.width * 0.5, y: checkLayer.position.y), completion: { [weak checkLayer] _ in
|
|
||||||
// checkLayer?.removeFromSuperlayer()
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let avatarSize: CGFloat = 40.0
|
|
||||||
//
|
|
||||||
// let avatarFrame = CGRect(origin: CGPoint(x: avatarLeftInset, y: floor((height - verticalInset * 2.0 - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize))
|
|
||||||
// if self.avatarNode.bounds.isEmpty {
|
|
||||||
// self.avatarNode.frame = avatarFrame
|
|
||||||
// } else {
|
|
||||||
// transition.setFrame(layer: self.avatarNode.layer, frame: avatarFrame)
|
|
||||||
// }
|
|
||||||
// if let peer = component.peer {
|
|
||||||
// let clipStyle: AvatarNodeClipStyle
|
|
||||||
// if case let .channel(channel) = peer, channel.flags.contains(.isForum) {
|
|
||||||
// clipStyle = .roundedRect
|
|
||||||
// } else {
|
|
||||||
// clipStyle = .round
|
|
||||||
// }
|
|
||||||
// self.avatarNode.setPeer(context: component.context, theme: component.theme, peer: peer, clipStyle: clipStyle, synchronousLoad: synchronousLoad, displayDimensions: CGSize(width: avatarSize, height: avatarSize))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let labelData: (String, Bool)
|
|
||||||
//
|
|
||||||
// if let presence = component.presence {
|
|
||||||
// let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
|
|
||||||
// labelData = stringAndActivityForUserPresence(strings: component.strings, dateTimeFormat: PresentationDateTimeFormat(), presence: presence, relativeTo: Int32(timestamp))
|
|
||||||
// } else if let subtitle = component.subtitle {
|
|
||||||
// labelData = (subtitle, false)
|
|
||||||
// } else if case .legacyGroup = component.peer {
|
|
||||||
// labelData = (component.strings.Group_Status, false)
|
|
||||||
// } else if case let .channel(channel) = component.peer {
|
|
||||||
// if case .group = channel.info {
|
|
||||||
// labelData = (component.strings.Group_Status, false)
|
|
||||||
// } else {
|
|
||||||
// labelData = (component.strings.Channel_Status, false)
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// labelData = (component.strings.Group_Status, false)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let labelSize = self.label.update(
|
|
||||||
// transition: .immediate,
|
|
||||||
// component: AnyComponent(MultilineTextComponent(
|
|
||||||
// text: .plain(NSAttributedString(string: labelData.0, font: Font.regular(15.0), textColor: labelData.1 ? component.theme.list.itemAccentColor : component.theme.list.itemSecondaryTextColor))
|
|
||||||
// )),
|
|
||||||
// environment: {},
|
|
||||||
// containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0)
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// let previousTitleFrame = self.title.view?.frame
|
|
||||||
// var previousTitleContents: UIView?
|
|
||||||
// if hasSelectionUpdated && !"".isEmpty {
|
|
||||||
// previousTitleContents = self.title.view?.snapshotView(afterScreenUpdates: false)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let titleSize = self.title.update(
|
|
||||||
// transition: .immediate,
|
|
||||||
// component: AnyComponent(MultilineTextComponent(
|
|
||||||
// text: .plain(NSAttributedString(string: component.title, font: Font.semibold(17.0), textColor: component.theme.list.itemPrimaryTextColor))
|
|
||||||
// )),
|
|
||||||
// environment: {},
|
|
||||||
// containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0)
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// let titleSpacing: CGFloat = 1.0
|
|
||||||
// let centralContentHeight: CGFloat = titleSize.height + labelSize.height + titleSpacing
|
|
||||||
//
|
|
||||||
// let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - verticalInset * 2.0 - centralContentHeight) / 2.0)), size: titleSize)
|
|
||||||
// if let titleView = self.title.view {
|
|
||||||
// if titleView.superview == nil {
|
|
||||||
// titleView.isUserInteractionEnabled = false
|
|
||||||
// self.containerButton.addSubview(titleView)
|
|
||||||
// }
|
|
||||||
// titleView.frame = titleFrame
|
|
||||||
// if let previousTitleFrame, previousTitleFrame.origin.x != titleFrame.origin.x {
|
|
||||||
// transition.animatePosition(view: titleView, from: CGPoint(x: previousTitleFrame.origin.x - titleFrame.origin.x, y: 0.0), to: CGPoint(), additive: true)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if let previousTitleFrame, let previousTitleContents, previousTitleFrame.size != titleSize {
|
|
||||||
// previousTitleContents.frame = CGRect(origin: previousTitleFrame.origin, size: previousTitleFrame.size)
|
|
||||||
// self.addSubview(previousTitleContents)
|
|
||||||
//
|
|
||||||
// transition.setFrame(view: previousTitleContents, frame: CGRect(origin: titleFrame.origin, size: previousTitleFrame.size))
|
|
||||||
// transition.setAlpha(view: previousTitleContents, alpha: 0.0, completion: { [weak previousTitleContents] _ in
|
|
||||||
// previousTitleContents?.removeFromSuperview()
|
|
||||||
// })
|
|
||||||
// transition.animateAlpha(view: titleView, from: 0.0, to: 1.0)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if let labelView = self.label.view {
|
|
||||||
// if labelView.superview == nil {
|
|
||||||
// labelView.isUserInteractionEnabled = false
|
|
||||||
// self.containerButton.addSubview(labelView)
|
|
||||||
// }
|
|
||||||
// transition.setFrame(view: labelView, frame: CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.maxY + titleSpacing), size: labelSize))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if themeUpdated {
|
|
||||||
// self.separatorLayer.backgroundColor = component.theme.list.itemPlainSeparatorColor.cgColor
|
|
||||||
// }
|
|
||||||
// transition.setFrame(layer: self.separatorLayer, frame: CGRect(origin: CGPoint(x: leftInset, y: height), size: CGSize(width: availableSize.width - leftInset, height: UIScreenPixel)))
|
|
||||||
// self.separatorLayer.isHidden = !component.hasNext
|
|
||||||
//
|
|
||||||
// let containerFrame = CGRect(origin: CGPoint(x: contextInset, y: verticalInset), size: CGSize(width: availableSize.width - contextInset * 2.0, height: height - verticalInset * 2.0))
|
|
||||||
// transition.setFrame(view: self.containerButton, frame: containerFrame)
|
|
||||||
//
|
|
||||||
// return CGSize(width: availableSize.width, height: height)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func makeView() -> View {
|
|
||||||
// return View(frame: CGRect())
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
|
||||||
// return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@ -7,16 +7,16 @@ import MultilineTextComponent
|
|||||||
|
|
||||||
final class SectionHeaderComponent: Component {
|
final class SectionHeaderComponent: Component {
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let sideInset: CGFloat
|
let style: ShareWithPeersScreenComponent.Style
|
||||||
let title: String
|
let title: String
|
||||||
|
|
||||||
init(
|
init(
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
sideInset: CGFloat,
|
style: ShareWithPeersScreenComponent.Style,
|
||||||
title: String
|
title: String
|
||||||
) {
|
) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.sideInset = sideInset
|
self.style = style
|
||||||
self.title = title
|
self.title = title
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ final class SectionHeaderComponent: Component {
|
|||||||
if lhs.theme !== rhs.theme {
|
if lhs.theme !== rhs.theme {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.sideInset != rhs.sideInset {
|
if lhs.style != rhs.style {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.title != rhs.title {
|
if lhs.title != rhs.title {
|
||||||
@ -59,13 +59,19 @@ final class SectionHeaderComponent: Component {
|
|||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
let height: CGFloat = 28.0
|
let height: CGFloat = 28.0
|
||||||
let leftInset: CGFloat = component.sideInset
|
let leftInset: CGFloat = 16.0
|
||||||
let rightInset: CGFloat = component.sideInset
|
let rightInset: CGFloat = 0.0
|
||||||
|
|
||||||
let previousTitleFrame = self.title.view?.frame
|
let previousTitleFrame = self.title.view?.frame
|
||||||
|
|
||||||
if themeUpdated {
|
if themeUpdated {
|
||||||
|
switch component.style {
|
||||||
|
case .plain:
|
||||||
|
self.backgroundView.isHidden = false
|
||||||
self.backgroundView.updateColor(color: component.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
|
self.backgroundView.updateColor(color: component.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
|
||||||
|
case .blocks:
|
||||||
|
self.backgroundView.isHidden = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let titleSize = self.title.update(
|
let titleSize = self.title.update(
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import AvatarNode
|
|||||||
import LocalizedPeerData
|
import LocalizedPeerData
|
||||||
import PeerListItemComponent
|
import PeerListItemComponent
|
||||||
import LottieComponent
|
import LottieComponent
|
||||||
|
import TooltipUI
|
||||||
|
|
||||||
final class ShareWithPeersScreenComponent: Component {
|
final class ShareWithPeersScreenComponent: Component {
|
||||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||||
@ -27,30 +28,33 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let stateContext: ShareWithPeersScreen.StateContext
|
let stateContext: ShareWithPeersScreen.StateContext
|
||||||
let initialPrivacy: EngineStoryPrivacy
|
let initialPrivacy: EngineStoryPrivacy
|
||||||
let timeout: Int
|
let screenshot: Bool
|
||||||
|
let pin: Bool
|
||||||
let categoryItems: [CategoryItem]
|
let categoryItems: [CategoryItem]
|
||||||
let completion: (EngineStoryPrivacy) -> Void
|
let optionItems: [OptionItem]
|
||||||
let editCategory: (EngineStoryPrivacy) -> Void
|
let completion: (EngineStoryPrivacy, Bool, Bool) -> Void
|
||||||
let secondaryAction: () -> Void
|
let editCategory: (EngineStoryPrivacy, Bool, Bool) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
stateContext: ShareWithPeersScreen.StateContext,
|
stateContext: ShareWithPeersScreen.StateContext,
|
||||||
initialPrivacy: EngineStoryPrivacy,
|
initialPrivacy: EngineStoryPrivacy,
|
||||||
timeout: Int,
|
screenshot: Bool,
|
||||||
|
pin: Bool,
|
||||||
categoryItems: [CategoryItem],
|
categoryItems: [CategoryItem],
|
||||||
completion: @escaping (EngineStoryPrivacy) -> Void,
|
optionItems: [OptionItem],
|
||||||
editCategory: @escaping (EngineStoryPrivacy) -> Void,
|
completion: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void,
|
||||||
secondaryAction: @escaping () -> Void
|
editCategory: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.stateContext = stateContext
|
self.stateContext = stateContext
|
||||||
self.initialPrivacy = initialPrivacy
|
self.initialPrivacy = initialPrivacy
|
||||||
self.timeout = timeout
|
self.screenshot = screenshot
|
||||||
|
self.pin = pin
|
||||||
self.categoryItems = categoryItems
|
self.categoryItems = categoryItems
|
||||||
|
self.optionItems = optionItems
|
||||||
self.completion = completion
|
self.completion = completion
|
||||||
self.editCategory = editCategory
|
self.editCategory = editCategory
|
||||||
self.secondaryAction = secondaryAction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: ShareWithPeersScreenComponent, rhs: ShareWithPeersScreenComponent) -> Bool {
|
static func ==(lhs: ShareWithPeersScreenComponent, rhs: ShareWithPeersScreenComponent) -> Bool {
|
||||||
@ -63,16 +67,26 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
if lhs.initialPrivacy != rhs.initialPrivacy {
|
if lhs.initialPrivacy != rhs.initialPrivacy {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.timeout != rhs.timeout {
|
if lhs.screenshot != rhs.screenshot {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.pin != rhs.pin {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.categoryItems != rhs.categoryItems {
|
if lhs.categoryItems != rhs.categoryItems {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.optionItems != rhs.optionItems {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Style {
|
||||||
|
case plain
|
||||||
|
case blocks
|
||||||
|
}
|
||||||
|
|
||||||
private struct ItemLayout: Equatable {
|
private struct ItemLayout: Equatable {
|
||||||
struct Section: Equatable {
|
struct Section: Equatable {
|
||||||
var id: Int
|
var id: Int
|
||||||
@ -97,6 +111,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var style: ShareWithPeersScreenComponent.Style
|
||||||
var containerSize: CGSize
|
var containerSize: CGSize
|
||||||
var containerInset: CGFloat
|
var containerInset: CGFloat
|
||||||
var bottomInset: CGFloat
|
var bottomInset: CGFloat
|
||||||
@ -107,7 +122,8 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
|
|
||||||
var contentHeight: CGFloat
|
var contentHeight: CGFloat
|
||||||
|
|
||||||
init(containerSize: CGSize, containerInset: CGFloat, bottomInset: CGFloat, topInset: CGFloat, sideInset: CGFloat, navigationHeight: CGFloat, sections: [Section]) {
|
init(style: ShareWithPeersScreenComponent.Style, containerSize: CGSize, containerInset: CGFloat, bottomInset: CGFloat, topInset: CGFloat, sideInset: CGFloat, navigationHeight: CGFloat, sections: [Section]) {
|
||||||
|
self.style = style
|
||||||
self.containerSize = containerSize
|
self.containerSize = containerSize
|
||||||
self.containerInset = containerInset
|
self.containerInset = containerInset
|
||||||
self.bottomInset = bottomInset
|
self.bottomInset = bottomInset
|
||||||
@ -155,6 +171,13 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
case violet
|
case violet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CategoryId: Int, Hashable {
|
||||||
|
case everyone = 0
|
||||||
|
case contacts = 1
|
||||||
|
case closeFriends = 2
|
||||||
|
case selectedContacts = 3
|
||||||
|
}
|
||||||
|
|
||||||
final class CategoryItem: Equatable {
|
final class CategoryItem: Equatable {
|
||||||
let id: CategoryId
|
let id: CategoryId
|
||||||
let title: String
|
let title: String
|
||||||
@ -204,11 +227,29 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CategoryId: Int, Hashable {
|
enum OptionId: Int, Hashable {
|
||||||
case everyone = 0
|
case screenshot = 0
|
||||||
case contacts = 1
|
case pin = 1
|
||||||
case closeFriends = 2
|
}
|
||||||
case selectedContacts = 3
|
|
||||||
|
final class OptionItem: Equatable {
|
||||||
|
let id: OptionId
|
||||||
|
let title: String
|
||||||
|
|
||||||
|
init(
|
||||||
|
id: OptionId,
|
||||||
|
title: String
|
||||||
|
) {
|
||||||
|
self.id = id
|
||||||
|
self.title = title
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: OptionItem, rhs: OptionItem) -> Bool {
|
||||||
|
if lhs === rhs {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class View: UIView, UIScrollViewDelegate {
|
final class View: UIView, UIScrollViewDelegate {
|
||||||
@ -239,17 +280,20 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
|
|
||||||
private let categoryTemplateItem = ComponentView<Empty>()
|
private let categoryTemplateItem = ComponentView<Empty>()
|
||||||
private let peerTemplateItem = ComponentView<Empty>()
|
private let peerTemplateItem = ComponentView<Empty>()
|
||||||
|
private let optionTemplateItem = ComponentView<Empty>()
|
||||||
|
|
||||||
private let itemContainerView: UIView
|
private let itemContainerView: UIView
|
||||||
private var visibleSectionHeaders: [Int: ComponentView<Empty>] = [:]
|
private var visibleSectionHeaders: [Int: ComponentView<Empty>] = [:]
|
||||||
private var visibleItems: [AnyHashable: ComponentView<Empty>] = [:]
|
private var visibleItems: [AnyHashable: ComponentView<Empty>] = [:]
|
||||||
|
private var visibleSectionBackgrounds: [Int: UIView] = [:]
|
||||||
|
private var visibleSectionFooters: [Int: ComponentView<Empty>] = [:]
|
||||||
|
|
||||||
private var ignoreScrolling: Bool = false
|
private var ignoreScrolling: Bool = false
|
||||||
private var isDismissed: Bool = false
|
private var isDismissed: Bool = false
|
||||||
|
|
||||||
private var selectedPeers: [EnginePeer.Id] = []
|
private var selectedPeers: [EnginePeer.Id] = []
|
||||||
private var selectedCategories = Set<CategoryId>()
|
private var selectedCategories = Set<CategoryId>()
|
||||||
private var selectedPeersByCategory: [CategoryId: [EnginePeer.Id]] = [:]
|
private var selectedOptions = Set<OptionId>()
|
||||||
|
|
||||||
private var component: ShareWithPeersScreenComponent?
|
private var component: ShareWithPeersScreenComponent?
|
||||||
private weak var state: EmptyComponentState?
|
private weak var state: EmptyComponentState?
|
||||||
@ -389,8 +433,67 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func presentOptionsTooltip(optionId: OptionId) {
|
||||||
|
guard let component = self.component, let controller = self.environment?.controller() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let animationName: String
|
||||||
|
let text: String
|
||||||
|
|
||||||
|
switch optionId {
|
||||||
|
case .screenshot:
|
||||||
|
if self.selectedOptions.contains(.screenshot) {
|
||||||
|
if self.selectedCategories.contains(.everyone) {
|
||||||
|
animationName = "anim_savemedia"
|
||||||
|
text = "Downloading, sharing and taking screenshots will be enabled for this story."
|
||||||
|
} else {
|
||||||
|
animationName = "anim_savemedia"
|
||||||
|
text = "Downloading and taking screenshots will be enabled for this story."
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if self.selectedCategories.contains(.everyone) {
|
||||||
|
animationName = "premium_unlock"
|
||||||
|
text = "Downloading, sharing and taking screenshots will be disabled for this story."
|
||||||
|
} else {
|
||||||
|
animationName = "premium_unlock"
|
||||||
|
text = "Downloading and taking screenshots will be disabled for this story."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .pin:
|
||||||
|
if self.selectedOptions.contains(.pin) {
|
||||||
|
animationName = "anim_profileadd"
|
||||||
|
text = "Users allowed to view your story will see it on your page event after it expires."
|
||||||
|
} else {
|
||||||
|
animationName = "anim_autoremove_on"
|
||||||
|
text = "The story will disappear after it expires."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let tooltipScreen = TooltipScreen(
|
||||||
|
context: component.context,
|
||||||
|
account: component.context.account,
|
||||||
|
sharedContext: component.context.sharedContext,
|
||||||
|
text: .markdown(text: text),
|
||||||
|
style: .wide,
|
||||||
|
icon: .animation(name: animationName, delay: 0.0, tintColor: .white),
|
||||||
|
location: .top,
|
||||||
|
displayDuration: .custom(4.0),
|
||||||
|
shouldDismissOnTouch: { point in
|
||||||
|
return .ignore
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
controller.window?.forEachController({ controller in
|
||||||
|
if let controller = controller as? TooltipScreen {
|
||||||
|
controller.dismiss(inPlace: true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
controller.present(tooltipScreen, in: .window(.root))
|
||||||
|
}
|
||||||
|
|
||||||
private func updateScrolling(transition: Transition) {
|
private func updateScrolling(transition: Transition) {
|
||||||
guard let component = self.component, let environment = self.environment, let controller = environment.controller(), let itemLayout = self.itemLayout else {
|
guard let component = self.component, let environment = self.environment, let itemLayout = self.itemLayout else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let stateValue = self.effectiveStateValue else {
|
guard let stateValue = self.effectiveStateValue else {
|
||||||
@ -412,9 +515,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
var topOffsetFraction = topOffset / topOffsetDistance
|
var topOffsetFraction = topOffset / topOffsetDistance
|
||||||
topOffsetFraction = max(0.0, min(1.0, topOffsetFraction))
|
topOffsetFraction = max(0.0, min(1.0, topOffsetFraction))
|
||||||
|
|
||||||
let transitionFactor: CGFloat = 1.0 - topOffsetFraction
|
//let transitionFactor: CGFloat = 1.0 - topOffsetFraction
|
||||||
let _ = transitionFactor
|
|
||||||
let _ = controller
|
|
||||||
//controller.updateModalStyleOverlayTransitionFactor(transitionFactor, transition: transition.containedViewLayoutTransition)
|
//controller.updateModalStyleOverlayTransitionFactor(transitionFactor, transition: transition.containedViewLayoutTransition)
|
||||||
|
|
||||||
var visibleBounds = self.scrollView.bounds
|
var visibleBounds = self.scrollView.bounds
|
||||||
@ -427,14 +528,42 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
|
|
||||||
var validIds: [AnyHashable] = []
|
var validIds: [AnyHashable] = []
|
||||||
var validSectionHeaders: [AnyHashable] = []
|
var validSectionHeaders: [AnyHashable] = []
|
||||||
|
var validSectionBackgrounds: [AnyHashable] = []
|
||||||
var sectionOffset: CGFloat = itemLayout.navigationHeight
|
var sectionOffset: CGFloat = itemLayout.navigationHeight
|
||||||
for sectionIndex in 0 ..< itemLayout.sections.count {
|
for sectionIndex in 0 ..< itemLayout.sections.count {
|
||||||
let section = itemLayout.sections[sectionIndex]
|
let section = itemLayout.sections[sectionIndex]
|
||||||
|
|
||||||
var minSectionHeader: UIView?
|
if case .blocks = itemLayout.style {
|
||||||
|
let sectionBackgroundFrame = CGRect(origin: CGPoint(x: itemLayout.sideInset, y: sectionOffset + section.insets.top), size: CGSize(width: itemLayout.containerSize.width, height: section.totalHeight - section.insets.top))
|
||||||
|
|
||||||
|
if visibleFrame.intersects(sectionBackgroundFrame) {
|
||||||
|
validSectionBackgrounds.append(section.id)
|
||||||
|
|
||||||
|
var sectionBackground: UIView
|
||||||
|
var sectionBackgroundTransition = transition
|
||||||
|
if let current = self.visibleSectionBackgrounds[section.id] {
|
||||||
|
sectionBackground = current
|
||||||
|
} else {
|
||||||
|
if !transition.animation.isImmediate {
|
||||||
|
sectionBackgroundTransition = .immediate
|
||||||
|
}
|
||||||
|
sectionBackground = UIView()
|
||||||
|
sectionBackground.backgroundColor = environment.theme.list.itemModalBlocksBackgroundColor
|
||||||
|
sectionBackground.layer.cornerRadius = 10.0
|
||||||
|
self.visibleSectionBackgrounds[section.id] = sectionBackground
|
||||||
|
}
|
||||||
|
|
||||||
|
if sectionBackground.superview == nil {
|
||||||
|
sectionBackground.isUserInteractionEnabled = false
|
||||||
|
self.itemContainerView.addSubview(sectionBackground)
|
||||||
|
}
|
||||||
|
sectionBackgroundTransition.setFrame(view: sectionBackground, frame: sectionBackgroundFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var minSectionHeader: UIView?
|
||||||
do {
|
do {
|
||||||
var sectionHeaderFrame = CGRect(origin: CGPoint(x: visibleFrame.minX, y: itemLayout.containerInset + sectionOffset - self.scrollView.bounds.minY + itemLayout.topInset), size: CGSize(width: itemLayout.containerSize.width, height: section.insets.top))
|
var sectionHeaderFrame = CGRect(origin: CGPoint(x: itemLayout.sideInset, y: itemLayout.containerInset + sectionOffset - self.scrollView.bounds.minY + itemLayout.topInset), size: CGSize(width: itemLayout.containerSize.width, height: section.insets.top))
|
||||||
|
|
||||||
let sectionHeaderMinY = topOffset + itemLayout.containerInset + itemLayout.navigationHeight
|
let sectionHeaderMinY = topOffset + itemLayout.containerInset + itemLayout.navigationHeight
|
||||||
let sectionHeaderMaxY = itemLayout.containerInset + sectionOffset - self.scrollView.bounds.minY + itemLayout.topInset + section.totalHeight - 28.0
|
let sectionHeaderMaxY = itemLayout.containerInset + sectionOffset - self.scrollView.bounds.minY + itemLayout.topInset + section.totalHeight - 28.0
|
||||||
@ -459,19 +588,17 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
let sectionTitle: String
|
let sectionTitle: String
|
||||||
if section.id == 0 {
|
if section.id == 0 {
|
||||||
sectionTitle = "WHO CAN VIEW"
|
sectionTitle = "WHO CAN VIEW"
|
||||||
} else {
|
} else if section.id == 1 {
|
||||||
if case .chats = component.stateContext.subject {
|
|
||||||
sectionTitle = "CHATS"
|
|
||||||
} else {
|
|
||||||
sectionTitle = "CONTACTS"
|
sectionTitle = "CONTACTS"
|
||||||
}
|
} else {
|
||||||
|
sectionTitle = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = sectionHeader.update(
|
let _ = sectionHeader.update(
|
||||||
transition: sectionHeaderTransition,
|
transition: sectionHeaderTransition,
|
||||||
component: AnyComponent(SectionHeaderComponent(
|
component: AnyComponent(SectionHeaderComponent(
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
sideInset: 16.0,
|
style: itemLayout.style,
|
||||||
title: sectionTitle
|
title: sectionTitle
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
@ -492,7 +619,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
|
|
||||||
if section.id == 0 {
|
if section.id == 0 {
|
||||||
for i in 0 ..< component.categoryItems.count {
|
for i in 0 ..< component.categoryItems.count {
|
||||||
let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: sectionOffset + section.insets.top + CGFloat(i) * section.itemHeight), size: CGSize(width: itemLayout.containerSize.width, height: section.itemHeight))
|
let itemFrame = CGRect(origin: CGPoint(x: itemLayout.sideInset, y: sectionOffset + section.insets.top + CGFloat(i) * section.itemHeight), size: CGSize(width: itemLayout.containerSize.width, height: section.itemHeight))
|
||||||
if !visibleBounds.intersects(itemFrame) {
|
if !visibleBounds.intersects(itemFrame) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -519,7 +646,6 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
component: AnyComponent(CategoryListItemComponent(
|
component: AnyComponent(CategoryListItemComponent(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
sideInset: itemLayout.sideInset,
|
|
||||||
title: item.title,
|
title: item.title,
|
||||||
color: item.iconColor,
|
color: item.iconColor,
|
||||||
iconName: item.icon,
|
iconName: item.icon,
|
||||||
@ -527,7 +653,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
selectionState: .editing(isSelected: self.selectedCategories.contains(item.id), isTinted: false),
|
selectionState: .editing(isSelected: self.selectedCategories.contains(item.id), isTinted: false),
|
||||||
hasNext: i != component.categoryItems.count - 1,
|
hasNext: i != component.categoryItems.count - 1,
|
||||||
action: { [weak self] in
|
action: { [weak self] in
|
||||||
guard let self else {
|
guard let self, let environment = self.environment, let controller = environment.controller() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if self.selectedCategories.contains(categoryId) {
|
if self.selectedCategories.contains(categoryId) {
|
||||||
@ -538,7 +664,11 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
self.selectedCategories.insert(categoryId)
|
self.selectedCategories.insert(categoryId)
|
||||||
|
|
||||||
if self.selectedPeers.isEmpty && categoryId == .selectedContacts {
|
if self.selectedPeers.isEmpty && categoryId == .selectedContacts {
|
||||||
component.editCategory(EngineStoryPrivacy(base: .nobody, additionallyIncludePeers: []))
|
component.editCategory(
|
||||||
|
EngineStoryPrivacy(base: .nobody, additionallyIncludePeers: []),
|
||||||
|
self.selectedOptions.contains(.screenshot),
|
||||||
|
self.selectedOptions.contains(.pin)
|
||||||
|
)
|
||||||
controller.dismiss()
|
controller.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -560,7 +690,11 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
base = .nobody
|
base = .nobody
|
||||||
}
|
}
|
||||||
if let base {
|
if let base {
|
||||||
component.editCategory(EngineStoryPrivacy(base: base, additionallyIncludePeers: self.selectedPeers))
|
component.editCategory(
|
||||||
|
EngineStoryPrivacy(base: base, additionallyIncludePeers: self.selectedPeers),
|
||||||
|
self.selectedOptions.contains(.screenshot),
|
||||||
|
self.selectedOptions.contains(.pin)
|
||||||
|
)
|
||||||
controller.dismiss()
|
controller.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -581,7 +715,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
} else if section.id == 1 {
|
} else if section.id == 1 {
|
||||||
for i in 0 ..< stateValue.peers.count {
|
for i in 0 ..< stateValue.peers.count {
|
||||||
let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: sectionOffset + section.insets.top + CGFloat(i) * section.itemHeight), size: CGSize(width: itemLayout.containerSize.width, height: section.itemHeight))
|
let itemFrame = CGRect(origin: CGPoint(x: itemLayout.sideInset, y: sectionOffset + section.insets.top + CGFloat(i) * section.itemHeight), size: CGSize(width: itemLayout.containerSize.width, height: section.itemHeight))
|
||||||
if !visibleBounds.intersects(itemFrame) {
|
if !visibleBounds.intersects(itemFrame) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -647,6 +781,95 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
itemTransition.setFrame(view: itemView, frame: itemFrame)
|
itemTransition.setFrame(view: itemView, frame: itemFrame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if section.id == 2 {
|
||||||
|
for i in 0 ..< component.optionItems.count {
|
||||||
|
let itemFrame = CGRect(origin: CGPoint(x: itemLayout.sideInset, y: sectionOffset + section.insets.top + CGFloat(i) * section.itemHeight), size: CGSize(width: itemLayout.containerSize.width, height: section.itemHeight))
|
||||||
|
if !visibleBounds.intersects(itemFrame) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = component.optionItems[i]
|
||||||
|
let optionId = item.id
|
||||||
|
let itemId = AnyHashable(item.id)
|
||||||
|
validIds.append(itemId)
|
||||||
|
|
||||||
|
var itemTransition = transition
|
||||||
|
let visibleItem: ComponentView<Empty>
|
||||||
|
if let current = self.visibleItems[itemId] {
|
||||||
|
visibleItem = current
|
||||||
|
} else {
|
||||||
|
visibleItem = ComponentView()
|
||||||
|
if !transition.animation.isImmediate {
|
||||||
|
itemTransition = .immediate
|
||||||
|
}
|
||||||
|
self.visibleItems[itemId] = visibleItem
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = visibleItem.update(
|
||||||
|
transition: itemTransition,
|
||||||
|
component: AnyComponent(OptionListItemComponent(
|
||||||
|
theme: environment.theme,
|
||||||
|
title: item.title,
|
||||||
|
hasNext: i != component.optionItems.count - 1,
|
||||||
|
selected: self.selectedOptions.contains(item.id),
|
||||||
|
selectionChanged: { [weak self] selected in
|
||||||
|
if let self {
|
||||||
|
if selected {
|
||||||
|
self.selectedOptions.insert(optionId)
|
||||||
|
} else {
|
||||||
|
self.selectedOptions.remove(optionId)
|
||||||
|
}
|
||||||
|
let transition = Transition(animation: .curve(duration: 0.35, curve: .spring))
|
||||||
|
self.state?.updated(transition: transition)
|
||||||
|
|
||||||
|
self.presentOptionsTooltip(optionId: optionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: itemFrame.size
|
||||||
|
)
|
||||||
|
if let itemView = visibleItem.view {
|
||||||
|
if itemView.superview == nil {
|
||||||
|
if let minSectionHeader {
|
||||||
|
self.itemContainerView.insertSubview(itemView, belowSubview: minSectionHeader)
|
||||||
|
} else {
|
||||||
|
self.itemContainerView.addSubview(itemView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
itemTransition.setFrame(view: itemView, frame: itemFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let sectionFooter: ComponentView<Empty>
|
||||||
|
var sectionFooterTransition = transition
|
||||||
|
if let current = self.visibleSectionFooters[section.id] {
|
||||||
|
sectionFooter = current
|
||||||
|
} else {
|
||||||
|
if !transition.animation.isImmediate {
|
||||||
|
sectionFooterTransition = .immediate
|
||||||
|
}
|
||||||
|
sectionFooter = ComponentView()
|
||||||
|
self.visibleSectionFooters[section.id] = sectionFooter
|
||||||
|
}
|
||||||
|
|
||||||
|
let footerSize = sectionFooter.update(
|
||||||
|
transition: sectionFooterTransition,
|
||||||
|
component: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(string: "Keep this story on your page even after it expires in \(24) hours. Privacy settings will apply.", font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor)),
|
||||||
|
maximumNumberOfLines: 0,
|
||||||
|
lineSpacing: 0.2
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: itemLayout.containerSize.width - 16.0 * 2.0, height: itemLayout.contentHeight)
|
||||||
|
)
|
||||||
|
let footerFrame = CGRect(origin: CGPoint(x: itemLayout.sideInset + 16.0, y: sectionOffset + section.totalHeight + 7.0), size: footerSize)
|
||||||
|
if let footerView = sectionFooter.view {
|
||||||
|
if footerView.superview == nil {
|
||||||
|
self.itemContainerView.addSubview(footerView)
|
||||||
|
}
|
||||||
|
sectionFooterTransition.setFrame(view: footerView, frame: footerFrame)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sectionOffset += section.totalHeight
|
sectionOffset += section.totalHeight
|
||||||
@ -678,6 +901,17 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
self.visibleSectionHeaders.removeValue(forKey: id)
|
self.visibleSectionHeaders.removeValue(forKey: id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var removeSectionBackgroundIds: [Int] = []
|
||||||
|
for (id, item) in self.visibleSectionBackgrounds {
|
||||||
|
if !validSectionBackgrounds.contains(id) {
|
||||||
|
removeSectionBackgroundIds.append(id)
|
||||||
|
item.removeFromSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for id in removeSectionBackgroundIds {
|
||||||
|
self.visibleSectionBackgrounds.removeValue(forKey: id)
|
||||||
|
}
|
||||||
|
|
||||||
let fadeTransition = Transition.easeInOut(duration: 0.25)
|
let fadeTransition = Transition.easeInOut(duration: 0.25)
|
||||||
if let searchStateContext = self.searchStateContext, case let .search(query) = searchStateContext.subject, let value = searchStateContext.stateValue, value.peers.isEmpty {
|
if let searchStateContext = self.searchStateContext, case let .search(query) = searchStateContext.subject, let value = searchStateContext.stateValue, value.peers.isEmpty {
|
||||||
let sideInset: CGFloat = 44.0
|
let sideInset: CGFloat = 44.0
|
||||||
@ -830,7 +1064,14 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
|
|
||||||
let resetScrolling = self.scrollView.bounds.width != availableSize.width
|
let resetScrolling = self.scrollView.bounds.width != availableSize.width
|
||||||
|
|
||||||
let sideInset: CGFloat = 0.0
|
var sideInset: CGFloat = 0.0
|
||||||
|
if case .stories = component.stateContext.subject {
|
||||||
|
sideInset = 16.0
|
||||||
|
self.scrollView.bounces = false
|
||||||
|
} else {
|
||||||
|
self.scrollView.bounces = true
|
||||||
|
}
|
||||||
|
|
||||||
let containerWidth: CGFloat
|
let containerWidth: CGFloat
|
||||||
if case .regular = environment.metrics.widthClass {
|
if case .regular = environment.metrics.widthClass {
|
||||||
containerWidth = 390.0
|
containerWidth = 390.0
|
||||||
@ -851,6 +1092,13 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
self.selectedCategories.insert(.selectedContacts)
|
self.selectedCategories.insert(.selectedContacts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if component.screenshot {
|
||||||
|
self.selectedOptions.insert(.screenshot)
|
||||||
|
}
|
||||||
|
if component.pin {
|
||||||
|
self.selectedOptions.insert(.pin)
|
||||||
|
}
|
||||||
|
|
||||||
var applyState = false
|
var applyState = false
|
||||||
self.defaultStateValue = component.stateContext.stateValue
|
self.defaultStateValue = component.stateContext.stateValue
|
||||||
self.selectedPeers = Array(component.stateContext.initialPeerIds)
|
self.selectedPeers = Array(component.stateContext.initialPeerIds)
|
||||||
@ -880,23 +1128,41 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
self.backgroundView.image = generateImage(CGSize(width: 20.0, height: 20.0), rotatedContext: { size, context in
|
self.backgroundView.image = generateImage(CGSize(width: 20.0, height: 20.0), rotatedContext: { size, context in
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
if case .stories = component.stateContext.subject {
|
||||||
|
context.setFillColor(environment.theme.list.modalBlocksBackgroundColor.cgColor)
|
||||||
|
} else {
|
||||||
context.setFillColor(environment.theme.list.plainBackgroundColor.cgColor)
|
context.setFillColor(environment.theme.list.plainBackgroundColor.cgColor)
|
||||||
|
}
|
||||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
|
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
|
||||||
context.fill(CGRect(origin: CGPoint(x: 0.0, y: size.height * 0.5), size: CGSize(width: size.width, height: size.height * 0.5)))
|
context.fill(CGRect(origin: CGPoint(x: 0.0, y: size.height * 0.5), size: CGSize(width: size.width, height: size.height * 0.5)))
|
||||||
})?.stretchableImage(withLeftCapWidth: 10, topCapHeight: 19)
|
})?.stretchableImage(withLeftCapWidth: 10, topCapHeight: 19)
|
||||||
|
|
||||||
|
if case .stories = component.stateContext.subject {
|
||||||
|
self.navigationBackgroundView.updateColor(color: environment.theme.list.modalBlocksBackgroundColor, transition: .immediate)
|
||||||
|
self.navigationSeparatorLayer.backgroundColor = UIColor.clear.cgColor
|
||||||
|
self.bottomBackgroundView.updateColor(color: environment.theme.list.modalBlocksBackgroundColor, transition: .immediate)
|
||||||
|
self.bottomSeparatorLayer.backgroundColor = UIColor.clear.cgColor
|
||||||
|
} else {
|
||||||
self.navigationBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
|
self.navigationBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
|
||||||
self.navigationSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
|
self.navigationSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
|
||||||
self.textFieldSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
|
|
||||||
|
|
||||||
self.bottomBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
|
self.bottomBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
|
||||||
self.bottomSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
|
self.bottomSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.textFieldSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
|
||||||
|
}
|
||||||
|
|
||||||
|
let itemLayoutStyle: ShareWithPeersScreenComponent.Style
|
||||||
|
let itemsContainerWidth: CGFloat
|
||||||
let navigationTextFieldSize: CGSize
|
let navigationTextFieldSize: CGSize
|
||||||
if case .stories = component.stateContext.subject {
|
if case .stories = component.stateContext.subject {
|
||||||
|
itemLayoutStyle = .blocks
|
||||||
|
itemsContainerWidth = containerWidth - sideInset * 2.0
|
||||||
navigationTextFieldSize = .zero
|
navigationTextFieldSize = .zero
|
||||||
} else {
|
} else {
|
||||||
|
itemLayoutStyle = .plain
|
||||||
|
itemsContainerWidth = containerWidth
|
||||||
|
|
||||||
var tokens: [TokenListTextField.Token] = []
|
var tokens: [TokenListTextField.Token] = []
|
||||||
for peerId in self.selectedPeers {
|
for peerId in self.selectedPeers {
|
||||||
guard let stateValue = self.defaultStateValue, let peer = stateValue.peers.first(where: { $0.id == peerId }) else {
|
guard let stateValue = self.defaultStateValue, let peer = stateValue.peers.first(where: { $0.id == peerId }) else {
|
||||||
@ -974,12 +1240,12 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
|
|
||||||
transition.setFrame(view: self.dimView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
transition.setFrame(view: self.dimView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||||
|
|
||||||
|
|
||||||
let categoryItemSize = self.categoryTemplateItem.update(
|
let categoryItemSize = self.categoryTemplateItem.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(CategoryListItemComponent(
|
component: AnyComponent(CategoryListItemComponent(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
sideInset: sideInset,
|
|
||||||
title: "Title",
|
title: "Title",
|
||||||
color: .blue,
|
color: .blue,
|
||||||
iconName: nil,
|
iconName: nil,
|
||||||
@ -990,7 +1256,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
secondaryAction: {}
|
secondaryAction: {}
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: containerWidth, height: 1000.0)
|
containerSize: CGSize(width: itemsContainerWidth, height: 1000.0)
|
||||||
)
|
)
|
||||||
let peerItemSize = self.peerTemplateItem.update(
|
let peerItemSize = self.peerTemplateItem.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
@ -1011,7 +1277,19 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: containerWidth, height: 1000.0)
|
containerSize: CGSize(width: itemsContainerWidth, height: 1000.0)
|
||||||
|
)
|
||||||
|
let optionItemSize = self.optionTemplateItem.update(
|
||||||
|
transition: transition,
|
||||||
|
component: AnyComponent(OptionListItemComponent(
|
||||||
|
theme: environment.theme,
|
||||||
|
title: "Title",
|
||||||
|
hasNext: true,
|
||||||
|
selected: false,
|
||||||
|
selectionChanged: { _ in }
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: itemsContainerWidth, height: 1000.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
var sections: [ItemLayout.Section] = []
|
var sections: [ItemLayout.Section] = []
|
||||||
@ -1023,6 +1301,12 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
itemHeight: categoryItemSize.height,
|
itemHeight: categoryItemSize.height,
|
||||||
itemCount: component.categoryItems.count
|
itemCount: component.categoryItems.count
|
||||||
))
|
))
|
||||||
|
sections.append(ItemLayout.Section(
|
||||||
|
id: 2,
|
||||||
|
insets: UIEdgeInsets(top: 28.0, left: 0.0, bottom: 0.0, right: 0.0),
|
||||||
|
itemHeight: optionItemSize.height,
|
||||||
|
itemCount: component.optionItems.count
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
sections.append(ItemLayout.Section(
|
sections.append(ItemLayout.Section(
|
||||||
id: 1,
|
id: 1,
|
||||||
@ -1065,9 +1349,13 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
var actionButtonTitle = "Save Settings"
|
var actionButtonTitle = "Save Settings"
|
||||||
let title: String
|
let title: String
|
||||||
switch component.stateContext.subject {
|
switch component.stateContext.subject {
|
||||||
case .stories:
|
case let .stories(editing):
|
||||||
|
if editing {
|
||||||
|
title = "Edit Story"
|
||||||
|
} else {
|
||||||
title = "Share Story"
|
title = "Share Story"
|
||||||
actionButtonTitle = "Post Story"
|
actionButtonTitle = "Post Story"
|
||||||
|
}
|
||||||
case .chats:
|
case .chats:
|
||||||
title = "Send as a Message"
|
title = "Send as a Message"
|
||||||
case let .contacts(category):
|
case let .contacts(category):
|
||||||
@ -1110,15 +1398,25 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
navigationHeight += navigationTextFieldFrame.height
|
navigationHeight += navigationTextFieldFrame.height
|
||||||
|
|
||||||
|
if case .stories = component.stateContext.subject {
|
||||||
|
navigationHeight += 16.0
|
||||||
|
}
|
||||||
|
|
||||||
let topInset: CGFloat
|
let topInset: CGFloat
|
||||||
if environment.inputHeight != 0.0 || !self.navigationTextFieldState.text.isEmpty {
|
if environment.inputHeight != 0.0 || !self.navigationTextFieldState.text.isEmpty {
|
||||||
topInset = 0.0
|
topInset = 0.0
|
||||||
} else {
|
} else {
|
||||||
if case .stories = component.stateContext.subject {
|
let inset: CGFloat
|
||||||
topInset = max(0.0, availableSize.height - containerInset - 427.0)
|
if case let .stories(editing) = component.stateContext.subject {
|
||||||
|
if editing {
|
||||||
|
inset = 430.0
|
||||||
} else {
|
} else {
|
||||||
topInset = max(0.0, availableSize.height - containerInset - 600.0)
|
inset = 605.0
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
inset = 600.0
|
||||||
|
}
|
||||||
|
topInset = max(0.0, availableSize.height - containerInset - inset)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.navigationBackgroundView.update(size: CGSize(width: containerWidth, height: navigationHeight), cornerRadius: 10.0, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], transition: transition.containedViewLayoutTransition)
|
self.navigationBackgroundView.update(size: CGSize(width: containerWidth, height: navigationHeight), cornerRadius: 10.0, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], transition: transition.containedViewLayoutTransition)
|
||||||
@ -1164,10 +1462,14 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
base = .nobody
|
base = .nobody
|
||||||
}
|
}
|
||||||
|
|
||||||
component.completion(EngineStoryPrivacy(
|
component.completion(
|
||||||
|
EngineStoryPrivacy(
|
||||||
base: base,
|
base: base,
|
||||||
additionallyIncludePeers: self.selectedPeers
|
additionallyIncludePeers: self.selectedPeers
|
||||||
))
|
),
|
||||||
|
self.selectedOptions.contains(.screenshot),
|
||||||
|
self.selectedOptions.contains(.pin)
|
||||||
|
)
|
||||||
|
|
||||||
controller.dismiss()
|
controller.dismiss()
|
||||||
}
|
}
|
||||||
@ -1194,12 +1496,12 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
self.bottomBackgroundView.update(size: self.bottomBackgroundView.bounds.size, transition: transition.containedViewLayoutTransition)
|
self.bottomBackgroundView.update(size: self.bottomBackgroundView.bounds.size, transition: transition.containedViewLayoutTransition)
|
||||||
transition.setFrame(layer: self.bottomSeparatorLayer, frame: CGRect(origin: CGPoint(x: containerSideInset + sideInset, y: availableSize.height - bottomPanelHeight - 8.0 - UIScreenPixel), size: CGSize(width: containerWidth, height: UIScreenPixel)))
|
transition.setFrame(layer: self.bottomSeparatorLayer, frame: CGRect(origin: CGPoint(x: containerSideInset + sideInset, y: availableSize.height - bottomPanelHeight - 8.0 - UIScreenPixel), size: CGSize(width: containerWidth, height: UIScreenPixel)))
|
||||||
|
|
||||||
let itemContainerSize = CGSize(width: containerWidth, height: availableSize.height)
|
let itemContainerSize = CGSize(width: itemsContainerWidth, height: availableSize.height)
|
||||||
let itemLayout = ItemLayout(containerSize: itemContainerSize, containerInset: containerInset, bottomInset: bottomPanelHeight, topInset: topInset, sideInset: sideInset, navigationHeight: navigationHeight, sections: sections)
|
let itemLayout = ItemLayout(style: itemLayoutStyle, containerSize: itemContainerSize, containerInset: containerInset, bottomInset: bottomPanelHeight, topInset: topInset, sideInset: sideInset, navigationHeight: navigationHeight, sections: sections)
|
||||||
let previousItemLayout = self.itemLayout
|
let previousItemLayout = self.itemLayout
|
||||||
self.itemLayout = itemLayout
|
self.itemLayout = itemLayout
|
||||||
|
|
||||||
contentTransition.setFrame(view: self.itemContainerView, frame: CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: containerWidth, height: itemLayout.contentHeight)))
|
contentTransition.setFrame(view: self.itemContainerView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: containerWidth, height: itemLayout.contentHeight)))
|
||||||
|
|
||||||
let scrollContentHeight = max(topInset + itemLayout.contentHeight + containerInset, availableSize.height - containerInset)
|
let scrollContentHeight = max(topInset + itemLayout.contentHeight + containerInset, availableSize.height - containerInset)
|
||||||
|
|
||||||
@ -1208,7 +1510,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
transition.setPosition(view: self.backgroundView, position: CGPoint(x: availableSize.width / 2.0, y: availableSize.height / 2.0))
|
transition.setPosition(view: self.backgroundView, position: CGPoint(x: availableSize.width / 2.0, y: availableSize.height / 2.0))
|
||||||
transition.setBounds(view: self.backgroundView, bounds: CGRect(origin: CGPoint(x: containerSideInset, y: 0.0), size: CGSize(width: containerWidth, height: availableSize.height)))
|
transition.setBounds(view: self.backgroundView, bounds: CGRect(origin: CGPoint(x: containerSideInset, y: 0.0), size: CGSize(width: containerWidth, height: availableSize.height)))
|
||||||
|
|
||||||
let scrollClippingFrame = CGRect(origin: CGPoint(x: sideInset, y: containerInset + 10.0), size: CGSize(width: availableSize.width, height: availableSize.height - 10.0))
|
let scrollClippingFrame = CGRect(origin: CGPoint(x: 0.0, y: containerInset + 10.0), size: CGSize(width: availableSize.width, height: availableSize.height - 10.0))
|
||||||
transition.setPosition(view: self.scrollContentClippingView, position: scrollClippingFrame.center)
|
transition.setPosition(view: self.scrollContentClippingView, position: scrollClippingFrame.center)
|
||||||
transition.setBounds(view: self.scrollContentClippingView, bounds: CGRect(origin: CGPoint(x: scrollClippingFrame.minX, y: scrollClippingFrame.minY), size: scrollClippingFrame.size))
|
transition.setBounds(view: self.scrollContentClippingView, bounds: CGRect(origin: CGPoint(x: scrollClippingFrame.minX, y: scrollClippingFrame.minY), size: scrollClippingFrame.size))
|
||||||
|
|
||||||
@ -1265,7 +1567,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
|||||||
|
|
||||||
public final class StateContext {
|
public final class StateContext {
|
||||||
public enum Subject: Equatable {
|
public enum Subject: Equatable {
|
||||||
case stories
|
case stories(editing: Bool)
|
||||||
case chats
|
case chats
|
||||||
case contacts(EngineStoryPrivacy.Base)
|
case contacts(EngineStoryPrivacy.Base)
|
||||||
case search(String)
|
case search(String)
|
||||||
@ -1430,11 +1732,20 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
|||||||
|
|
||||||
public var dismissed: () -> Void = {}
|
public var dismissed: () -> Void = {}
|
||||||
|
|
||||||
public init(context: AccountContext, initialPrivacy: EngineStoryPrivacy, timeout: Int, stateContext: StateContext, completion: @escaping (EngineStoryPrivacy) -> Void, editCategory: @escaping (EngineStoryPrivacy) -> Void, secondaryAction: @escaping () -> Void = {}) {
|
public init(
|
||||||
|
context: AccountContext,
|
||||||
|
initialPrivacy: EngineStoryPrivacy,
|
||||||
|
allowScreenshots: Bool = true,
|
||||||
|
pin: Bool = false,
|
||||||
|
stateContext: StateContext,
|
||||||
|
completion: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void,
|
||||||
|
editCategory: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void
|
||||||
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
var categoryItems: [ShareWithPeersScreenComponent.CategoryItem] = []
|
var categoryItems: [ShareWithPeersScreenComponent.CategoryItem] = []
|
||||||
if case .stories = stateContext.subject {
|
var optionItems: [ShareWithPeersScreenComponent.OptionItem] = []
|
||||||
|
if case let .stories(editing) = stateContext.subject {
|
||||||
categoryItems.append(ShareWithPeersScreenComponent.CategoryItem(
|
categoryItems.append(ShareWithPeersScreenComponent.CategoryItem(
|
||||||
id: .everyone,
|
id: .everyone,
|
||||||
title: "Everyone",
|
title: "Everyone",
|
||||||
@ -1482,17 +1793,30 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
|||||||
iconColor: .violet,
|
iconColor: .violet,
|
||||||
actionTitle: selectedContactsSubtitle
|
actionTitle: selectedContactsSubtitle
|
||||||
))
|
))
|
||||||
|
|
||||||
|
if !editing {
|
||||||
|
optionItems.append(ShareWithPeersScreenComponent.OptionItem(
|
||||||
|
id: .screenshot,
|
||||||
|
title: "Allow Screenshots"
|
||||||
|
))
|
||||||
|
|
||||||
|
optionItems.append(ShareWithPeersScreenComponent.OptionItem(
|
||||||
|
id: .pin,
|
||||||
|
title: "Keep on My Page"
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.init(context: context, component: ShareWithPeersScreenComponent(
|
super.init(context: context, component: ShareWithPeersScreenComponent(
|
||||||
context: context,
|
context: context,
|
||||||
stateContext: stateContext,
|
stateContext: stateContext,
|
||||||
initialPrivacy: initialPrivacy,
|
initialPrivacy: initialPrivacy,
|
||||||
timeout: timeout,
|
screenshot: allowScreenshots,
|
||||||
|
pin: pin,
|
||||||
categoryItems: categoryItems,
|
categoryItems: categoryItems,
|
||||||
|
optionItems: optionItems,
|
||||||
completion: completion,
|
completion: completion,
|
||||||
editCategory: editCategory,
|
editCategory: editCategory
|
||||||
secondaryAction: secondaryAction
|
|
||||||
), navigationBarAppearance: .none, theme: .dark)
|
), navigationBarAppearance: .none, theme: .dark)
|
||||||
|
|
||||||
self.statusBar.statusBarStyle = .Ignore
|
self.statusBar.statusBarStyle = .Ignore
|
||||||
|
|||||||
@ -2587,7 +2587,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let stateContext = ShareWithPeersScreen.StateContext(context: context, subject: .stories, initialPeerIds: Set(privacy.additionallyIncludePeers))
|
let stateContext = ShareWithPeersScreen.StateContext(context: context, subject: .stories(editing: true), initialPeerIds: Set(privacy.additionallyIncludePeers))
|
||||||
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
|
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -2595,9 +2595,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
let controller = ShareWithPeersScreen(
|
let controller = ShareWithPeersScreen(
|
||||||
context: context,
|
context: context,
|
||||||
initialPrivacy: privacy,
|
initialPrivacy: privacy,
|
||||||
timeout: 86400,
|
|
||||||
stateContext: stateContext,
|
stateContext: stateContext,
|
||||||
completion: { [weak self] privacy in
|
completion: { [weak self] privacy, _, _ in
|
||||||
guard let self, let component = self.component else {
|
guard let self, let component = self.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2606,7 +2605,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
self.privacyController = nil
|
self.privacyController = nil
|
||||||
self.updateIsProgressPaused()
|
self.updateIsProgressPaused()
|
||||||
},
|
},
|
||||||
editCategory: { [weak self] privacy in
|
editCategory: { [weak self] privacy, _, _ in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2644,9 +2643,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
let controller = ShareWithPeersScreen(
|
let controller = ShareWithPeersScreen(
|
||||||
context: context,
|
context: context,
|
||||||
initialPrivacy: privacy,
|
initialPrivacy: privacy,
|
||||||
timeout: 86400,
|
|
||||||
stateContext: stateContext,
|
stateContext: stateContext,
|
||||||
completion: { result in
|
completion: { result, _, _ in
|
||||||
if case .closeFriends = privacy.base {
|
if case .closeFriends = privacy.base {
|
||||||
let _ = context.engine.privacy.updateCloseFriends(peerIds: result.additionallyIncludePeers).start()
|
let _ = context.engine.privacy.updateCloseFriends(peerIds: result.additionallyIncludePeers).start()
|
||||||
completion(EngineStoryPrivacy(base: .closeFriends, additionallyIncludePeers: []))
|
completion(EngineStoryPrivacy(base: .closeFriends, additionallyIncludePeers: []))
|
||||||
@ -2654,7 +2652,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
completion(result)
|
completion(result)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
editCategory: { _ in }
|
editCategory: { _, _, _ in }
|
||||||
)
|
)
|
||||||
controller.dismissed = { [weak self] in
|
controller.dismissed = { [weak self] in
|
||||||
if let self {
|
if let self {
|
||||||
|
|||||||
23
submodules/TelegramUI/Components/SwitchComponent/BUILD
Normal file
23
submodules/TelegramUI/Components/SwitchComponent/BUILD
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "SwitchComponent",
|
||||||
|
module_name = "SwitchComponent",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.swift",
|
||||||
|
]),
|
||||||
|
copts = [
|
||||||
|
"-warnings-as-errors",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||||
|
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||||
|
"//submodules/Display:Display",
|
||||||
|
"//submodules/ContextUI:ContextUI",
|
||||||
|
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||||
|
"//submodules/ComponentFlow:ComponentFlow",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import ComponentFlow
|
||||||
|
import TelegramPresentationData
|
||||||
|
|
||||||
|
public final class SwitchComponent: Component {
|
||||||
|
public typealias EnvironmentType = Empty
|
||||||
|
|
||||||
|
let tintColor: UIColor?
|
||||||
|
let value: Bool
|
||||||
|
let valueUpdated: (Bool) -> Void
|
||||||
|
|
||||||
|
public init(
|
||||||
|
tintColor: UIColor? = nil,
|
||||||
|
value: Bool,
|
||||||
|
valueUpdated: @escaping (Bool) -> Void
|
||||||
|
) {
|
||||||
|
self.tintColor = tintColor
|
||||||
|
self.value = value
|
||||||
|
self.valueUpdated = valueUpdated
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: SwitchComponent, rhs: SwitchComponent) -> Bool {
|
||||||
|
if lhs.tintColor != rhs.tintColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.value != rhs.value {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class View: UIView {
|
||||||
|
private let switchView: UISwitch
|
||||||
|
|
||||||
|
private var component: SwitchComponent?
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
self.switchView = UISwitch()
|
||||||
|
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.addSubview(self.switchView)
|
||||||
|
|
||||||
|
self.switchView.addTarget(self, action: #selector(self.valueChanged(_:)), for: .valueChanged)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func valueChanged(_ sender: Any) {
|
||||||
|
self.component?.valueUpdated(self.switchView.isOn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(component: SwitchComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
|
||||||
|
self.component = component
|
||||||
|
|
||||||
|
self.switchView.tintColor = component.tintColor
|
||||||
|
self.switchView.setOn(component.value, animated: !transition.animation.isImmediate)
|
||||||
|
|
||||||
|
self.switchView.sizeToFit()
|
||||||
|
self.switchView.frame = CGRect(origin: .zero, size: self.switchView.frame.size)
|
||||||
|
|
||||||
|
return self.switchView.frame.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeView() -> View {
|
||||||
|
return View(frame: CGRect())
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
|
||||||
|
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
||||||
15
submodules/TelegramUI/Images.xcassets/Media Editor/Next.imageset/Contents.json
vendored
Normal file
15
submodules/TelegramUI/Images.xcassets/Media Editor/Next.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "arrow_left.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@ -10,35 +10,35 @@ stream
|
|||||||
/DeviceRGB CS
|
/DeviceRGB CS
|
||||||
/DeviceRGB cs
|
/DeviceRGB cs
|
||||||
q
|
q
|
||||||
1.000000 0.000000 -0.000000 1.000000 4.335022 4.334961 cm
|
1.000000 0.000000 -0.000000 1.000000 4.170021 4.169979 cm
|
||||||
1.000000 1.000000 1.000000 scn
|
1.000000 1.000000 1.000000 scn
|
||||||
1.330000 10.665017 m
|
1.660000 10.830000 m
|
||||||
1.330000 15.820595 5.509422 20.000017 10.665000 20.000017 c
|
1.660000 15.894451 5.765549 20.000000 10.830000 20.000000 c
|
||||||
15.820578 20.000017 20.000000 15.820595 20.000000 10.665017 c
|
15.894451 20.000000 20.000000 15.894451 20.000000 10.830000 c
|
||||||
20.000000 7.511573 18.436378 4.723331 16.042154 3.033316 c
|
20.000000 7.821688 18.551388 5.151716 16.313671 3.479595 c
|
||||||
14.314164 10.809276 l
|
14.640235 11.010053 l
|
||||||
14.246551 11.113539 13.976685 11.330017 13.665000 11.330017 c
|
14.555845 11.389809 14.219020 11.660000 13.830000 11.660000 c
|
||||||
13.443334 11.330017 l
|
13.553333 11.660000 l
|
||||||
11.613684 16.818966 l
|
11.778684 16.983950 l
|
||||||
11.309785 17.730663 10.020216 17.730665 9.716317 16.818970 c
|
11.474785 17.895645 10.185216 17.895649 9.881317 16.983952 c
|
||||||
7.886667 11.330017 l
|
8.106667 11.660000 l
|
||||||
7.665000 11.330017 l
|
7.830000 11.660000 l
|
||||||
7.353315 11.330017 7.083449 11.113539 7.015836 10.809276 c
|
7.440980 11.660000 7.104155 11.389809 7.019765 11.010053 c
|
||||||
5.287845 3.033316 l
|
5.346330 3.479595 l
|
||||||
2.893622 4.723331 1.330000 7.511574 1.330000 10.665017 c
|
3.108612 5.151716 1.660000 7.821688 1.660000 10.830000 c
|
||||||
h
|
h
|
||||||
13.131556 10.000017 m
|
13.164198 10.000000 m
|
||||||
14.839726 2.313250 l
|
14.815517 2.569059 l
|
||||||
13.583508 1.684090 12.165603 1.330017 10.665000 1.330017 c
|
13.610337 1.986547 12.258304 1.660000 10.830000 1.660000 c
|
||||||
9.164397 1.330017 7.746492 1.684090 6.490273 2.313250 c
|
9.401696 1.660000 8.049662 1.986547 6.844482 2.569059 c
|
||||||
8.198444 10.000017 l
|
8.495803 10.000000 l
|
||||||
13.131556 10.000017 l
|
13.164198 10.000000 l
|
||||||
h
|
h
|
||||||
10.665000 21.330017 m
|
10.830000 21.660000 m
|
||||||
4.774883 21.330017 0.000000 16.555134 0.000000 10.665017 c
|
4.848756 21.660000 0.000000 16.811243 0.000000 10.830000 c
|
||||||
0.000000 4.774900 4.774883 0.000015 10.665000 0.000015 c
|
0.000000 4.848755 4.848756 0.000000 10.830000 0.000000 c
|
||||||
16.555117 0.000015 21.330002 4.774900 21.330002 10.665017 c
|
16.811245 0.000000 21.660000 4.848755 21.660000 10.830000 c
|
||||||
21.330002 16.555134 16.555117 21.330017 10.665000 21.330017 c
|
21.660000 16.811243 16.811245 21.660000 10.830000 21.660000 c
|
||||||
h
|
h
|
||||||
f*
|
f*
|
||||||
n
|
n
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "ic_editor_tools.pdf",
|
"filename" : "tools_30.pdf",
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
101
submodules/TelegramUI/Images.xcassets/Media Editor/Tools.imageset/tools_30.pdf
vendored
Normal file
101
submodules/TelegramUI/Images.xcassets/Media Editor/Tools.imageset/tools_30.pdf
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 4.170021 4.169979 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
5.830000 21.660000 m
|
||||||
|
6.288396 21.660000 6.660000 21.288397 6.660000 20.830000 c
|
||||||
|
6.660000 17.660000 l
|
||||||
|
20.830000 17.660000 l
|
||||||
|
21.288397 17.660000 21.660000 17.288397 21.660000 16.830000 c
|
||||||
|
21.660000 16.371603 21.288397 16.000000 20.830000 16.000000 c
|
||||||
|
6.660000 16.000000 l
|
||||||
|
6.660000 12.830000 l
|
||||||
|
6.660000 12.371604 6.288396 12.000000 5.830000 12.000000 c
|
||||||
|
5.371603 12.000000 5.000000 12.371604 5.000000 12.830000 c
|
||||||
|
5.000000 16.000000 l
|
||||||
|
0.830000 16.000000 l
|
||||||
|
0.371603 16.000000 0.000000 16.371603 0.000000 16.830000 c
|
||||||
|
0.000000 17.288397 0.371603 17.660000 0.830000 17.660000 c
|
||||||
|
5.000000 17.660000 l
|
||||||
|
5.000000 20.830000 l
|
||||||
|
5.000000 21.288397 5.371603 21.660000 5.830000 21.660000 c
|
||||||
|
h
|
||||||
|
20.830000 3.999998 m
|
||||||
|
21.288397 3.999998 21.660000 4.371601 21.660000 4.829998 c
|
||||||
|
21.660000 5.288395 21.288397 5.659998 20.830000 5.659998 c
|
||||||
|
16.660000 5.659998 l
|
||||||
|
16.660000 8.830000 l
|
||||||
|
16.660000 9.288396 16.288397 9.660000 15.830000 9.660000 c
|
||||||
|
15.371604 9.660000 15.000000 9.288396 15.000000 8.830000 c
|
||||||
|
15.000000 5.659998 l
|
||||||
|
0.830000 5.659998 l
|
||||||
|
0.371603 5.659998 0.000000 5.288395 0.000000 4.829998 c
|
||||||
|
0.000000 4.371601 0.371603 3.999998 0.830000 3.999998 c
|
||||||
|
15.000000 3.999998 l
|
||||||
|
15.000000 0.829998 l
|
||||||
|
15.000000 0.371603 15.371604 0.000000 15.830000 0.000000 c
|
||||||
|
16.288397 0.000000 16.660000 0.371603 16.660000 0.829998 c
|
||||||
|
16.660000 3.999998 l
|
||||||
|
20.830000 3.999998 l
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
1452
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Pages 5 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000001542 00000 n
|
||||||
|
0000001565 00000 n
|
||||||
|
0000001738 00000 n
|
||||||
|
0000001812 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
1871
|
||||||
|
%%EOF
|
||||||
BIN
submodules/TelegramUI/Resources/Animations/anim_profileadd.tgs
Normal file
BIN
submodules/TelegramUI/Resources/Animations/anim_profileadd.tgs
Normal file
Binary file not shown.
@ -14502,7 +14502,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .entities(text: solution.text, entities: solution.entities), icon: .animation(name: "anim_infotip", delay: 0.2), location: .top, shouldDismissOnTouch: { point in
|
let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .entities(text: solution.text, entities: solution.entities), icon: .animation(name: "anim_infotip", delay: 0.2, tintColor: nil), location: .top, shouldDismissOnTouch: { point in
|
||||||
return .ignore
|
return .ignore
|
||||||
}, openActiveTextItem: { [weak self] item, action in
|
}, openActiveTextItem: { [weak self] item, action in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -14595,7 +14595,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .entities(text: psaText, entities: psaEntities), icon: .animation(name: "anim_infotip", delay: 0.2), location: .top, displayDuration: .custom(10.0), shouldDismissOnTouch: { point in
|
let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .entities(text: psaText, entities: psaEntities), icon: .animation(name: "anim_infotip", delay: 0.2, tintColor: nil), location: .top, displayDuration: .custom(10.0), shouldDismissOnTouch: { point in
|
||||||
return .ignore
|
return .ignore
|
||||||
}, openActiveTextItem: { [weak self] item, action in
|
}, openActiveTextItem: { [weak self] item, action in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -14709,7 +14709,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .entities(text: psaText, entities: psaEntities), icon: .animation(name: "anim_infotip", delay: 0.2), location: .top, displayDuration: .custom(10.0), shouldDismissOnTouch: { point in
|
let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .entities(text: psaText, entities: psaEntities), icon: .animation(name: "anim_infotip", delay: 0.2, tintColor: nil), location: .top, displayDuration: .custom(10.0), shouldDismissOnTouch: { point in
|
||||||
return .ignore
|
return .ignore
|
||||||
}, openActiveTextItem: { [weak self] item, action in
|
}, openActiveTextItem: { [weak self] item, action in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
|
|||||||
@ -24,10 +24,8 @@ import LegacyMediaPickerUI
|
|||||||
import LegacyCamera
|
import LegacyCamera
|
||||||
import AvatarNode
|
import AvatarNode
|
||||||
import LocalMediaResources
|
import LocalMediaResources
|
||||||
import ShareWithPeersScreen
|
|
||||||
import ImageCompression
|
import ImageCompression
|
||||||
import TextFormat
|
import TextFormat
|
||||||
import UndoUI
|
|
||||||
|
|
||||||
private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceholderNode {
|
private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceholderNode {
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
@ -369,7 +367,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||||||
case let .image(image, dimensions):
|
case let .image(image, dimensions):
|
||||||
if let imageData = compressImageToJPEG(image, quality: 0.7) {
|
if let imageData = compressImageToJPEG(image, quality: 0.7) {
|
||||||
let entities = generateChatInputTextEntities(caption)
|
let entities = generateChatInputTextEntities(caption)
|
||||||
self.context.engine.messages.uploadStory(media: .image(dimensions: dimensions, data: imageData), text: caption.string, entities: entities, pin: privacy.archive, privacy: privacy.privacy, isForwardingDisabled: false, period: privacy.timeout, randomId: randomId)
|
self.context.engine.messages.uploadStory(media: .image(dimensions: dimensions, data: imageData), text: caption.string, entities: entities, pin: privacy.pin, privacy: privacy.privacy, isForwardingDisabled: privacy.isForwardingDisabled, period: privacy.timeout, randomId: randomId)
|
||||||
Queue.mainQueue().justDispatch {
|
Queue.mainQueue().justDispatch {
|
||||||
commit({})
|
commit({})
|
||||||
}
|
}
|
||||||
@ -392,7 +390,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||||||
}
|
}
|
||||||
let imageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6) }
|
let imageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6) }
|
||||||
let entities = generateChatInputTextEntities(caption)
|
let entities = generateChatInputTextEntities(caption)
|
||||||
self.context.engine.messages.uploadStory(media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameImageData: imageData), text: caption.string, entities: entities, pin: privacy.archive, privacy: privacy.privacy, isForwardingDisabled: false, period: privacy.timeout, randomId: randomId)
|
self.context.engine.messages.uploadStory(media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameImageData: imageData), text: caption.string, entities: entities, pin: privacy.pin, privacy: privacy.privacy, isForwardingDisabled: privacy.isForwardingDisabled, period: privacy.timeout, randomId: randomId)
|
||||||
Queue.mainQueue().justDispatch {
|
Queue.mainQueue().justDispatch {
|
||||||
commit({})
|
commit({})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -132,7 +132,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
private var arrowGradientNode: ASDisplayNode?
|
private var arrowGradientNode: ASDisplayNode?
|
||||||
private let arrowNode: ASImageNode
|
private let arrowNode: ASImageNode
|
||||||
private let arrowContainer: ASDisplayNode
|
private let arrowContainer: ASDisplayNode
|
||||||
private let animatedStickerNode: AnimatedStickerNode
|
private let animatedStickerNode: DefaultAnimatedStickerNodeImpl
|
||||||
private var downArrowsNode: DownArrowsIconNode?
|
private var downArrowsNode: DownArrowsIconNode?
|
||||||
private var avatarNode: AvatarNode?
|
private var avatarNode: AvatarNode?
|
||||||
private var avatarStoryIndicator: ComponentView<Empty>?
|
private var avatarStoryIndicator: ComponentView<Empty>?
|
||||||
@ -374,9 +374,10 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
switch icon {
|
switch icon {
|
||||||
case .none:
|
case .none:
|
||||||
break
|
break
|
||||||
case let .animation(animationName, _):
|
case let .animation(animationName, _, animationTintColor):
|
||||||
self.animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(name: animationName), width: Int(70 * UIScreenScale), height: Int(70 * UIScreenScale), playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
self.animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(name: animationName), width: Int(70 * UIScreenScale), height: Int(70 * UIScreenScale), playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||||
self.animatedStickerNode.automaticallyLoadFirstFrame = true
|
self.animatedStickerNode.automaticallyLoadFirstFrame = true
|
||||||
|
self.animatedStickerNode.dynamicColor = animationTintColor
|
||||||
case .downArrows:
|
case .downArrows:
|
||||||
self.downArrowsNode = DownArrowsIconNode()
|
self.downArrowsNode = DownArrowsIconNode()
|
||||||
case let .peer(peer, _):
|
case let .peer(peer, _):
|
||||||
@ -510,21 +511,25 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
let contentInset: CGFloat = 11.0
|
let contentInset: CGFloat = 11.0
|
||||||
let contentVerticalInset: CGFloat = 8.0
|
let contentVerticalInset: CGFloat = 8.0
|
||||||
let animationSize: CGSize
|
let animationSize: CGSize
|
||||||
let animationInset: CGFloat
|
var animationInset: CGFloat = 0.0
|
||||||
let animationSpacing: CGFloat
|
var animationSpacing: CGFloat = 0.0
|
||||||
|
var animationOffset: CGFloat = 0.0
|
||||||
|
|
||||||
switch self.icon {
|
switch self.icon {
|
||||||
case .none:
|
case .none:
|
||||||
animationSize = CGSize()
|
animationSize = CGSize()
|
||||||
animationInset = 0.0
|
|
||||||
animationSpacing = 0.0
|
|
||||||
case .downArrows:
|
case .downArrows:
|
||||||
animationSize = CGSize(width: 24.0, height: 32.0)
|
animationSize = CGSize(width: 24.0, height: 32.0)
|
||||||
animationInset = (40.0 - animationSize.width) / 2.0
|
animationInset = (40.0 - animationSize.width) / 2.0
|
||||||
animationSpacing = 8.0
|
case let .animation(animationName, _, _):
|
||||||
case let .animation(animationName, _):
|
if animationName == "premium_unlock" {
|
||||||
|
animationSize = CGSize(width: 34.0, height: 34.0)
|
||||||
|
} else {
|
||||||
animationSize = CGSize(width: 32.0, height: 32.0)
|
animationSize = CGSize(width: 32.0, height: 32.0)
|
||||||
if animationName == "ChatListFoldersTooltip" {
|
}
|
||||||
|
if animationName == "anim_autoremove_on" {
|
||||||
|
animationOffset = -3.0
|
||||||
|
} else if animationName == "ChatListFoldersTooltip" {
|
||||||
animationInset = (70.0 - animationSize.width) / 2.0
|
animationInset = (70.0 - animationSize.width) / 2.0
|
||||||
} else {
|
} else {
|
||||||
animationInset = 0.0
|
animationInset = 0.0
|
||||||
@ -555,12 +560,14 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
var backgroundHeight: CGFloat
|
var backgroundHeight: CGFloat
|
||||||
switch self.tooltipStyle {
|
switch self.tooltipStyle {
|
||||||
case .default, .gradient, .customBlur:
|
case .default, .gradient, .customBlur, .wide:
|
||||||
backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0
|
backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0
|
||||||
case .light:
|
case .light:
|
||||||
backgroundHeight = max(28.0, max(animationSize.height, textSize.height) + 4.0 * 2.0)
|
backgroundHeight = max(28.0, max(animationSize.height, textSize.height) + 4.0 * 2.0)
|
||||||
}
|
}
|
||||||
if self.actionButtonNode != nil {
|
if case .wide = self.tooltipStyle {
|
||||||
|
backgroundHeight += 4.0
|
||||||
|
} else if self.actionButtonNode != nil {
|
||||||
backgroundHeight += 4.0
|
backgroundHeight += 4.0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -668,7 +675,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
transition.updateFrame(node: actionButtonNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.width - actionSize.width - 16.0, y: floor((backgroundHeight - actionSize.height) / 2.0)), size: actionSize))
|
transition.updateFrame(node: actionButtonNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.width - actionSize.width - 16.0, y: floor((backgroundHeight - actionSize.height) / 2.0)), size: actionSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
let animationFrame = CGRect(origin: CGPoint(x: contentInset - animationInset, y: floorToScreenPixels((backgroundHeight - animationSize.height - animationInset * 2.0) / 2.0)), size: CGSize(width: animationSize.width + animationInset * 2.0, height: animationSize.height + animationInset * 2.0))
|
let animationFrame = CGRect(origin: CGPoint(x: contentInset - animationInset, y: floorToScreenPixels((backgroundHeight - animationSize.height - animationInset * 2.0) / 2.0) + animationOffset), size: CGSize(width: animationSize.width + animationInset * 2.0, height: animationSize.height + animationInset * 2.0))
|
||||||
transition.updateFrame(node: self.animatedStickerNode, frame: animationFrame)
|
transition.updateFrame(node: self.animatedStickerNode, frame: animationFrame)
|
||||||
self.animatedStickerNode.updateLayout(size: CGSize(width: animationSize.width + animationInset * 2.0, height: animationSize.height + animationInset * 2.0))
|
self.animatedStickerNode.updateLayout(size: CGSize(width: animationSize.width + animationInset * 2.0, height: animationSize.height + animationInset * 2.0))
|
||||||
|
|
||||||
@ -802,7 +809,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
let animationDelay: Double
|
let animationDelay: Double
|
||||||
switch self.icon {
|
switch self.icon {
|
||||||
case let .animation(_, delay):
|
case let .animation(_, delay, _):
|
||||||
animationDelay = delay
|
animationDelay = delay
|
||||||
case .none, .downArrows:
|
case .none, .downArrows:
|
||||||
animationDelay = 0.0
|
animationDelay = 0.0
|
||||||
@ -815,14 +822,14 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateOut(completion: @escaping () -> Void) {
|
func animateOut(inPlace: Bool, completion: @escaping () -> Void) {
|
||||||
switch self.location {
|
switch self.location {
|
||||||
case .top, .bottom:
|
case .top, .bottom:
|
||||||
self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { _ in
|
self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { _ in
|
||||||
completion()
|
completion()
|
||||||
})
|
})
|
||||||
self.containerNode.layer.animateScale(from: 1.0, to: 0.96, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
self.containerNode.layer.animateScale(from: 1.0, to: 0.96, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||||
if let _ = self.validLayout, case .top = self.location {
|
if let _ = self.validLayout, case .top = self.location, !inPlace {
|
||||||
let offset: CGFloat
|
let offset: CGFloat
|
||||||
if case .top = self.location {
|
if case .top = self.location {
|
||||||
offset = -13.0 - self.backgroundContainerNode.frame.height
|
offset = -13.0 - self.backgroundContainerNode.frame.height
|
||||||
@ -884,7 +891,7 @@ public final class TooltipScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum Icon {
|
public enum Icon {
|
||||||
case animation(name: String, delay: Double)
|
case animation(name: String, delay: Double, tintColor: UIColor?)
|
||||||
case peer(peer: EnginePeer, isStory: Bool)
|
case peer(peer: EnginePeer, isStory: Bool)
|
||||||
case downArrows
|
case downArrows
|
||||||
}
|
}
|
||||||
@ -918,6 +925,7 @@ public final class TooltipScreen: ViewController {
|
|||||||
case light
|
case light
|
||||||
case customBlur(UIColor)
|
case customBlur(UIColor)
|
||||||
case gradient(UIColor, UIColor)
|
case gradient(UIColor, UIColor)
|
||||||
|
case wide
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Alignment {
|
public enum Alignment {
|
||||||
@ -1073,13 +1081,13 @@ public final class TooltipScreen: ViewController {
|
|||||||
self.controllerNode.addRelativeScrollingOffset(value, transition: transition)
|
self.controllerNode.addRelativeScrollingOffset(value, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
public func dismiss(inPlace: Bool, completion: (() -> Void)? = nil) {
|
||||||
if self.isDismissed {
|
if self.isDismissed {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.isDismissed = true
|
self.isDismissed = true
|
||||||
self.willBecomeDismissed?(self)
|
self.willBecomeDismissed?(self)
|
||||||
self.controllerNode.animateOut(completion: { [weak self] in
|
self.controllerNode.animateOut(inPlace: inPlace, completion: { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1088,4 +1096,8 @@ public final class TooltipScreen: ViewController {
|
|||||||
becameDismissed?(strongSelf)
|
becameDismissed?(strongSelf)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||||
|
self.dismiss(inPlace: false, completion: completion)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user