mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
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())
|
||||
|
||||
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 {
|
||||
return .dismiss(consume: false)
|
||||
}
|
||||
|
@ -128,14 +128,21 @@ public final class LottieAnimationComponent: Component {
|
||||
}
|
||||
|
||||
public func playOnce() {
|
||||
guard let animationView = self.animationView else {
|
||||
guard let animationView = self.animationView, let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
animationView.stop()
|
||||
animationView.loopMode = .playOnce
|
||||
animationView.play { [weak self] _ in
|
||||
self?.currentCompletion?()
|
||||
|
||||
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
|
||||
self?.currentCompletion?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -485,6 +485,7 @@ private let color6Tag = GenericComponentViewTag()
|
||||
private let color7Tag = GenericComponentViewTag()
|
||||
private let color8Tag = GenericComponentViewTag()
|
||||
private let colorTags = [color1Tag, color2Tag, color3Tag, color4Tag, color5Tag, color6Tag, color7Tag, color8Tag]
|
||||
private let cancelButtonTag = GenericComponentViewTag()
|
||||
private let doneButtonTag = GenericComponentViewTag()
|
||||
|
||||
private final class DrawingScreenComponent: CombinedComponent {
|
||||
@ -2074,42 +2075,46 @@ private final class DrawingScreenComponent: CombinedComponent {
|
||||
animatingOut = true
|
||||
}
|
||||
|
||||
let backButton = backButton.update(
|
||||
component: Button(
|
||||
content: AnyComponent(
|
||||
LottieAnimationComponent(
|
||||
animation: LottieAnimationComponent.AnimationItem(
|
||||
name: "media_backToCancel",
|
||||
mode: .animating(loop: false),
|
||||
range: animatingOut || component.isAvatar ? (0.5, 1.0) : (0.0, 0.5)
|
||||
),
|
||||
colors: ["__allcolors__": .white],
|
||||
size: CGSize(width: 33.0, height: 33.0)
|
||||
)
|
||||
),
|
||||
action: { [weak state] in
|
||||
if let state = state {
|
||||
dismissEyedropper.invoke(Void())
|
||||
state.saveToolState()
|
||||
dismiss.invoke(Void())
|
||||
if animatingOut && component.sourceHint == .storyEditor {
|
||||
|
||||
} else {
|
||||
let backButton = backButton.update(
|
||||
component: Button(
|
||||
content: AnyComponent(
|
||||
LottieAnimationComponent(
|
||||
animation: LottieAnimationComponent.AnimationItem(
|
||||
name: "media_backToCancel",
|
||||
mode: .animating(loop: false),
|
||||
range: animatingOut || component.isAvatar ? (0.5, 1.0) : (0.0, 0.5)
|
||||
),
|
||||
colors: ["__allcolors__": .white],
|
||||
size: CGSize(width: 33.0, height: 33.0)
|
||||
)
|
||||
),
|
||||
action: { [weak state] in
|
||||
if let state = state {
|
||||
dismissEyedropper.invoke(Void())
|
||||
state.saveToolState()
|
||||
dismiss.invoke(Void())
|
||||
}
|
||||
}
|
||||
).minSize(CGSize(width: 44.0, height: 44.0)).tagged(cancelButtonTag),
|
||||
availableSize: CGSize(width: 33.0, height: 33.0),
|
||||
transition: .immediate
|
||||
)
|
||||
var backButtonPosition = CGPoint(x: environment.safeInsets.left + backButton.size.width / 2.0 + 3.0, y: context.availableSize.height - environment.safeInsets.bottom - backButton.size.height / 2.0 - 2.0 - UIScreenPixel)
|
||||
if component.sourceHint == .storyEditor {
|
||||
backButtonPosition.x = backButtonPosition.x + 2.0
|
||||
if case .regular = environment.metrics.widthClass {
|
||||
backButtonPosition.x += 20.0
|
||||
}
|
||||
).minSize(CGSize(width: 44.0, height: 44.0)),
|
||||
availableSize: CGSize(width: 33.0, height: 33.0),
|
||||
transition: .immediate
|
||||
)
|
||||
var backButtonPosition = CGPoint(x: environment.safeInsets.left + backButton.size.width / 2.0 + 3.0, y: context.availableSize.height - environment.safeInsets.bottom - backButton.size.height / 2.0 - 2.0 - UIScreenPixel)
|
||||
if component.sourceHint == .storyEditor {
|
||||
backButtonPosition.x = backButtonPosition.x + 2.0
|
||||
if case .regular = environment.metrics.widthClass {
|
||||
backButtonPosition.x += 20.0
|
||||
backButtonPosition.y = floorToScreenPixels(context.availableSize.height - previewBottomInset + 3.0 + backButton.size.height / 2.0)
|
||||
}
|
||||
backButtonPosition.y = floorToScreenPixels(context.availableSize.height - previewBottomInset + 3.0 + backButton.size.height / 2.0)
|
||||
context.add(backButton
|
||||
.position(backButtonPosition)
|
||||
.opacity(controlsAreVisible ? 1.0 : 0.0)
|
||||
)
|
||||
}
|
||||
context.add(backButton
|
||||
.position(backButtonPosition)
|
||||
.opacity(controlsAreVisible ? 1.0 : 0.0)
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
case let .addException(title):
|
||||
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()
|
||||
})
|
||||
case let .exceptionItem(_, peer, label):
|
||||
|
@ -1958,10 +1958,6 @@ public class CameraScreen: ViewController {
|
||||
self.navigationPresentation = .flatModal
|
||||
|
||||
self.requestAudioSession()
|
||||
|
||||
if #available(iOS 13.0, *) {
|
||||
try? AVAudioSession.sharedInstance().setAllowHapticsAndSystemSoundsDuringRecording(true)
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder: NSCoder) {
|
||||
@ -1982,7 +1978,11 @@ public class CameraScreen: ViewController {
|
||||
}
|
||||
|
||||
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())
|
||||
})
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ swift_library(
|
||||
"//submodules/Components/PagerComponent:PagerComponent",
|
||||
"//submodules/PremiumUI",
|
||||
"//submodules/ProgressNavigationButtonNode",
|
||||
"//submodules/TelegramUI/Components/SwitchComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -16,72 +16,7 @@ import EmojiStatusComponent
|
||||
import PremiumUI
|
||||
import ProgressNavigationButtonNode
|
||||
import Postbox
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
import SwitchComponent
|
||||
|
||||
private final class TitleFieldComponent: Component {
|
||||
typealias EnvironmentType = Empty
|
||||
|
@ -38,5 +38,10 @@ fragment half4 dualFragmentShader(RasterizerData in [[stage_in]],
|
||||
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));
|
||||
|
||||
return mix(half4(color, 0.0), half4(color, 1.0 * alpha), distance);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -9,25 +9,27 @@ import AccountContext
|
||||
|
||||
public struct MediaEditorResultPrivacy: Codable, Equatable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case privacy
|
||||
case peers
|
||||
case timeout
|
||||
case disableForwarding
|
||||
case archive
|
||||
}
|
||||
|
||||
public let privacy: EngineStoryPrivacy
|
||||
public let timeout: Int
|
||||
public let archive: Bool
|
||||
public let isForwardingDisabled: Bool
|
||||
public let pin: Bool
|
||||
|
||||
public init(
|
||||
privacy: EngineStoryPrivacy,
|
||||
timeout: Int,
|
||||
archive: Bool
|
||||
isForwardingDisabled: Bool,
|
||||
pin: Bool
|
||||
) {
|
||||
self.privacy = privacy
|
||||
self.timeout = timeout
|
||||
self.archive = archive
|
||||
self.isForwardingDisabled = isForwardingDisabled
|
||||
self.pin = pin
|
||||
}
|
||||
|
||||
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.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 {
|
||||
@ -43,7 +46,8 @@ public struct MediaEditorResultPrivacy: Codable, Equatable {
|
||||
|
||||
try container.encode(self.privacy, forKey: .privacy)
|
||||
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(
|
||||
width: size.width / containerSize.width,
|
||||
height: size.height / containerSize.height
|
||||
width: size.width / containerSize.width * 2.0,
|
||||
height: size.height / containerSize.height * 2.0
|
||||
)
|
||||
let relativeOffset = CGPoint(
|
||||
x: position.x / containerSize.width,
|
||||
@ -358,24 +358,45 @@ private func verticesData(
|
||||
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 [
|
||||
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,
|
||||
localPos: simd_float2(0.0, 0.0)
|
||||
),
|
||||
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,
|
||||
localPos: simd_float2(1.0, 0.0)
|
||||
),
|
||||
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,
|
||||
localPos: simd_float2(0.0, 1.0)
|
||||
),
|
||||
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,
|
||||
localPos: simd_float2(1.0, 1.0)
|
||||
),
|
||||
|
@ -562,6 +562,10 @@ final class MediaEditorScreenComponent: Component {
|
||||
view.alpha = 1.0
|
||||
}
|
||||
|
||||
if let buttonView = self.cancelButton.view as? Button.View, let view = buttonView.content as? LottieAnimationComponent.View {
|
||||
view.playOnce()
|
||||
}
|
||||
|
||||
let buttons = [
|
||||
self.drawButton,
|
||||
self.textButton,
|
||||
@ -660,8 +664,8 @@ final class MediaEditorScreenComponent: Component {
|
||||
LottieAnimationComponent(
|
||||
animation: LottieAnimationComponent.AnimationItem(
|
||||
name: "media_backToCancel",
|
||||
mode: .still(position: .begin),
|
||||
range: nil
|
||||
mode: .still(position: .end),
|
||||
range: (0.5, 1.0)
|
||||
),
|
||||
colors: ["__allcolors__": .white],
|
||||
size: CGSize(width: 33.0, height: 33.0)
|
||||
@ -693,10 +697,10 @@ final class MediaEditorScreenComponent: Component {
|
||||
let doneButtonSize = self.doneButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(Image(
|
||||
image: state.image(.done),
|
||||
size: CGSize(width: 33.0, height: 33.0)
|
||||
)),
|
||||
content: AnyComponent(DoneButtonComponent(
|
||||
backgroundColor: UIColor(rgb: 0x007aff),
|
||||
icon: UIImage(bundleImageName: "Media Editor/Next")!,
|
||||
title: "NEXT")),
|
||||
action: {
|
||||
guard let controller = environment.controller() as? MediaEditorScreen else {
|
||||
return
|
||||
@ -707,7 +711,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 44.0, height: 44.0)
|
||||
containerSize: CGSize(width: availableSize.width, height: 44.0)
|
||||
)
|
||||
let doneButtonFrame = CGRect(
|
||||
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
|
||||
buttonsLeftOffset = floorToScreenPixels((availableSize.width - buttonsAvailableWidth) / 2.0)
|
||||
} else {
|
||||
buttonsAvailableWidth = availableSize.width
|
||||
buttonsLeftOffset = 0.0
|
||||
buttonsAvailableWidth = floor(availableSize.width - cancelButtonSize.width * 0.66 - (doneButtonSize.width - cancelButtonSize.width * 0.33) - buttonSideInset * 2.0)
|
||||
buttonsLeftOffset = floorToScreenPixels(buttonSideInset + cancelButtonSize.width * 0.66)
|
||||
}
|
||||
|
||||
let drawButtonSize = self.drawButton.update(
|
||||
@ -747,7 +751,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
containerSize: CGSize(width: 40.0, height: 40.0)
|
||||
)
|
||||
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
|
||||
)
|
||||
if let drawButtonView = self.drawButton.view {
|
||||
@ -776,7 +780,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
containerSize: CGSize(width: 40.0, height: 40.0)
|
||||
)
|
||||
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
|
||||
)
|
||||
if let textButtonView = self.textButton.view {
|
||||
@ -805,7 +809,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
containerSize: CGSize(width: 40.0, height: 40.0)
|
||||
)
|
||||
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
|
||||
)
|
||||
if let stickerButtonView = self.stickerButton.view {
|
||||
@ -834,7 +838,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
containerSize: CGSize(width: 40.0, height: 40.0)
|
||||
)
|
||||
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
|
||||
)
|
||||
if let toolsButtonView = self.toolsButton.view {
|
||||
@ -923,9 +927,6 @@ final class MediaEditorScreenComponent: Component {
|
||||
default:
|
||||
timeoutValue = "24"
|
||||
}
|
||||
if component.privacy.archive {
|
||||
timeoutValue = "∞"
|
||||
}
|
||||
timeoutSelected = false
|
||||
|
||||
var inputPanelAvailableWidth = previewSize.width
|
||||
@ -1530,7 +1531,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -1846,8 +1852,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if case let .asset(asset) = subject, asset.mediaType == .video {
|
||||
}
|
||||
//#if DEBUG
|
||||
// if case let .asset(asset) = subject, asset.mediaType == .video {
|
||||
// let videoEntity = DrawingStickerEntity(content: .dualVideoReference)
|
||||
// videoEntity.referenceDrawingSize = storyDimensions
|
||||
// videoEntity.scale = 1.49
|
||||
@ -1863,8 +1870,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//#endif
|
||||
}
|
||||
|
||||
self.gradientColorsDisposable = mediaEditor.gradientColors.start(next: { [weak self] colors in
|
||||
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 text: String
|
||||
if controller.state.privacy.archive {
|
||||
if controller.state.privacy.pin {
|
||||
text = "Story will be kept on your page."
|
||||
} else {
|
||||
text = "Story will disappear in 24 hours."
|
||||
@ -3040,7 +3047,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
|
||||
if isEditing {
|
||||
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 {
|
||||
let _ = combineLatest(
|
||||
@ -3050,7 +3062,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
).start(next: { [weak self] state, peer in
|
||||
if let self, var privacy = state?.privacy {
|
||||
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
|
||||
}
|
||||
@ -3080,36 +3092,41 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
|
||||
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
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let initialPrivacy = privacy.privacy
|
||||
let timeout = privacy.timeout
|
||||
let archive = privacy.archive
|
||||
self.push(
|
||||
ShareWithPeersScreen(
|
||||
context: self.context,
|
||||
initialPrivacy: initialPrivacy,
|
||||
timeout: timeout,
|
||||
allowScreenshots: !privacy.isForwardingDisabled,
|
||||
pin: privacy.pin,
|
||||
stateContext: stateContext,
|
||||
completion: { [weak self] privacy in
|
||||
completion: { [weak self] privacy, allowScreenshots, pin in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.state.privacy = MediaEditorResultPrivacy(privacy: privacy, timeout: timeout, archive: archive)
|
||||
self.state.privacy = MediaEditorResultPrivacy(privacy: privacy, timeout: timeout, isForwardingDisabled: !allowScreenshots, pin: pin)
|
||||
completion()
|
||||
},
|
||||
editCategory: { [weak self] privacy in
|
||||
editCategory: { [weak self] privacy, allowScreenshots, pin in
|
||||
guard let self else {
|
||||
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 {
|
||||
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.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
guard let self else {
|
||||
@ -3128,9 +3145,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
ShareWithPeersScreen(
|
||||
context: self.context,
|
||||
initialPrivacy: privacy,
|
||||
timeout: 0,
|
||||
allowScreenshots: !isForwardingDisabled,
|
||||
pin: pin,
|
||||
stateContext: stateContext,
|
||||
completion: { [weak self] result in
|
||||
completion: { [weak self] result, isForwardingDisabled, pin in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@ -3141,7 +3159,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
completion(result)
|
||||
}
|
||||
},
|
||||
editCategory: { _ in }
|
||||
editCategory: { _, _, _ in }
|
||||
)
|
||||
)
|
||||
})
|
||||
@ -3152,16 +3170,16 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
|
||||
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 {
|
||||
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 currentValue = self.state.privacy.timeout
|
||||
let currentArchived = self.state.privacy.archive
|
||||
let currentArchived = self.state.privacy.pin
|
||||
let emptyAction: ((ContextMenuActionItem.Action) -> Void)? = nil
|
||||
|
||||
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)
|
||||
|
||||
if hasPremium {
|
||||
updateTimeout(3600 * 6, false)
|
||||
updateTimeout(3600 * 6)
|
||||
} else {
|
||||
self?.presentTimeoutPremiumSuggestion(3600 * 6)
|
||||
}
|
||||
@ -3191,7 +3209,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
a(.default)
|
||||
|
||||
if hasPremium {
|
||||
updateTimeout(3600 * 12, false)
|
||||
updateTimeout(3600 * 12)
|
||||
} else {
|
||||
self?.presentTimeoutPremiumSuggestion(3600 * 12)
|
||||
}
|
||||
@ -3201,7 +3219,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
}, action: { _, a in
|
||||
a(.default)
|
||||
|
||||
updateTimeout(86400, false)
|
||||
updateTimeout(86400)
|
||||
})))
|
||||
items.append(.action(ContextMenuActionItem(text: "48 Hours", icon: { theme in
|
||||
if !hasPremium {
|
||||
@ -3213,7 +3231,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
a(.default)
|
||||
|
||||
if hasPremium {
|
||||
updateTimeout(86400 * 2, false)
|
||||
updateTimeout(86400 * 2)
|
||||
} else {
|
||||
self?.presentTimeoutPremiumSuggestion(86400 * 2)
|
||||
}
|
||||
@ -3873,7 +3891,7 @@ final class PrivacyButtonComponent: CombinedComponent {
|
||||
|
||||
let text = text.update(
|
||||
component: Text(
|
||||
text: "\(context.component.text)",
|
||||
text: context.component.text,
|
||||
font: Font.medium(14.0),
|
||||
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 let controller: ViewController
|
||||
private let sourceView: UIView
|
||||
|
@ -284,6 +284,8 @@ private final class MediaToolsScreenComponent: Component {
|
||||
func animateOutToEditor(completion: @escaping () -> Void) {
|
||||
self.animatingOut = true
|
||||
|
||||
self.cancelButton.view?.isHidden = true
|
||||
|
||||
let buttons = [
|
||||
self.adjustmentsButton,
|
||||
self.tintButton,
|
||||
|
@ -35,6 +35,8 @@ swift_library(
|
||||
"//submodules/LocalizedPeerData",
|
||||
"//submodules/TelegramUI/Components/Stories/PeerListItemComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
"//submodules/TelegramUI/Components/SwitchComponent",
|
||||
"//submodules/TooltipUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -20,7 +20,6 @@ final class CategoryListItemComponent: Component {
|
||||
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let sideInset: CGFloat
|
||||
let title: String
|
||||
let color: ShareWithPeersScreenComponent.CategoryColor
|
||||
let iconName: String?
|
||||
@ -33,7 +32,6 @@ final class CategoryListItemComponent: Component {
|
||||
init(
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
sideInset: CGFloat,
|
||||
title: String,
|
||||
color: ShareWithPeersScreenComponent.CategoryColor,
|
||||
iconName: String?,
|
||||
@ -45,7 +43,6 @@ final class CategoryListItemComponent: Component {
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.sideInset = sideInset
|
||||
self.title = title
|
||||
self.color = color
|
||||
self.iconName = iconName
|
||||
@ -63,9 +60,6 @@ final class CategoryListItemComponent: Component {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.sideInset != rhs.sideInset {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
@ -179,11 +173,11 @@ final class CategoryListItemComponent: Component {
|
||||
|
||||
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
|
||||
let height: CGFloat = 56.0
|
||||
let verticalInset: CGFloat = 0.0
|
||||
var leftInset: CGFloat = 62.0
|
||||
let rightInset: CGFloat = contextInset * 2.0 + 8.0
|
||||
var avatarLeftInset: CGFloat = 10.0
|
||||
|
||||
if case let .editing(isSelected, isTinted) = component.selectionState {
|
||||
leftInset += 44.0
|
||||
@ -310,7 +304,7 @@ final class CategoryListItemComponent: Component {
|
||||
labelArrowView.isUserInteractionEnabled = false
|
||||
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 {
|
||||
|
@ -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 {
|
||||
let theme: PresentationTheme
|
||||
let sideInset: CGFloat
|
||||
let style: ShareWithPeersScreenComponent.Style
|
||||
let title: String
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
sideInset: CGFloat,
|
||||
style: ShareWithPeersScreenComponent.Style,
|
||||
title: String
|
||||
) {
|
||||
self.theme = theme
|
||||
self.sideInset = sideInset
|
||||
self.style = style
|
||||
self.title = title
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ final class SectionHeaderComponent: Component {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.sideInset != rhs.sideInset {
|
||||
if lhs.style != rhs.style {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
@ -59,13 +59,19 @@ final class SectionHeaderComponent: Component {
|
||||
self.state = state
|
||||
|
||||
let height: CGFloat = 28.0
|
||||
let leftInset: CGFloat = component.sideInset
|
||||
let rightInset: CGFloat = component.sideInset
|
||||
let leftInset: CGFloat = 16.0
|
||||
let rightInset: CGFloat = 0.0
|
||||
|
||||
let previousTitleFrame = self.title.view?.frame
|
||||
|
||||
if themeUpdated {
|
||||
self.backgroundView.updateColor(color: component.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
|
||||
switch component.style {
|
||||
case .plain:
|
||||
self.backgroundView.isHidden = false
|
||||
self.backgroundView.updateColor(color: component.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
|
||||
case .blocks:
|
||||
self.backgroundView.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
let titleSize = self.title.update(
|
||||
|
@ -20,6 +20,7 @@ import AvatarNode
|
||||
import LocalizedPeerData
|
||||
import PeerListItemComponent
|
||||
import LottieComponent
|
||||
import TooltipUI
|
||||
|
||||
final class ShareWithPeersScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
@ -27,30 +28,33 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
let context: AccountContext
|
||||
let stateContext: ShareWithPeersScreen.StateContext
|
||||
let initialPrivacy: EngineStoryPrivacy
|
||||
let timeout: Int
|
||||
let screenshot: Bool
|
||||
let pin: Bool
|
||||
let categoryItems: [CategoryItem]
|
||||
let completion: (EngineStoryPrivacy) -> Void
|
||||
let editCategory: (EngineStoryPrivacy) -> Void
|
||||
let secondaryAction: () -> Void
|
||||
let optionItems: [OptionItem]
|
||||
let completion: (EngineStoryPrivacy, Bool, Bool) -> Void
|
||||
let editCategory: (EngineStoryPrivacy, Bool, Bool) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
stateContext: ShareWithPeersScreen.StateContext,
|
||||
initialPrivacy: EngineStoryPrivacy,
|
||||
timeout: Int,
|
||||
screenshot: Bool,
|
||||
pin: Bool,
|
||||
categoryItems: [CategoryItem],
|
||||
completion: @escaping (EngineStoryPrivacy) -> Void,
|
||||
editCategory: @escaping (EngineStoryPrivacy) -> Void,
|
||||
secondaryAction: @escaping () -> Void
|
||||
optionItems: [OptionItem],
|
||||
completion: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void,
|
||||
editCategory: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.stateContext = stateContext
|
||||
self.initialPrivacy = initialPrivacy
|
||||
self.timeout = timeout
|
||||
self.screenshot = screenshot
|
||||
self.pin = pin
|
||||
self.categoryItems = categoryItems
|
||||
self.optionItems = optionItems
|
||||
self.completion = completion
|
||||
self.editCategory = editCategory
|
||||
self.secondaryAction = secondaryAction
|
||||
}
|
||||
|
||||
static func ==(lhs: ShareWithPeersScreenComponent, rhs: ShareWithPeersScreenComponent) -> Bool {
|
||||
@ -63,16 +67,26 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
if lhs.initialPrivacy != rhs.initialPrivacy {
|
||||
return false
|
||||
}
|
||||
if lhs.timeout != rhs.timeout {
|
||||
if lhs.screenshot != rhs.screenshot {
|
||||
return false
|
||||
}
|
||||
if lhs.pin != rhs.pin {
|
||||
return false
|
||||
}
|
||||
if lhs.categoryItems != rhs.categoryItems {
|
||||
return false
|
||||
}
|
||||
|
||||
if lhs.optionItems != rhs.optionItems {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
enum Style {
|
||||
case plain
|
||||
case blocks
|
||||
}
|
||||
|
||||
private struct ItemLayout: Equatable {
|
||||
struct Section: Equatable {
|
||||
var id: Int
|
||||
@ -97,6 +111,7 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
var style: ShareWithPeersScreenComponent.Style
|
||||
var containerSize: CGSize
|
||||
var containerInset: CGFloat
|
||||
var bottomInset: CGFloat
|
||||
@ -107,7 +122,8 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
|
||||
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.containerInset = containerInset
|
||||
self.bottomInset = bottomInset
|
||||
@ -155,6 +171,13 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
case violet
|
||||
}
|
||||
|
||||
enum CategoryId: Int, Hashable {
|
||||
case everyone = 0
|
||||
case contacts = 1
|
||||
case closeFriends = 2
|
||||
case selectedContacts = 3
|
||||
}
|
||||
|
||||
final class CategoryItem: Equatable {
|
||||
let id: CategoryId
|
||||
let title: String
|
||||
@ -204,13 +227,31 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
enum CategoryId: Int, Hashable {
|
||||
case everyone = 0
|
||||
case contacts = 1
|
||||
case closeFriends = 2
|
||||
case selectedContacts = 3
|
||||
enum OptionId: Int, Hashable {
|
||||
case screenshot = 0
|
||||
case pin = 1
|
||||
}
|
||||
|
||||
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 {
|
||||
private let dimView: UIView
|
||||
private let backgroundView: UIImageView
|
||||
@ -239,17 +280,20 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
|
||||
private let categoryTemplateItem = ComponentView<Empty>()
|
||||
private let peerTemplateItem = ComponentView<Empty>()
|
||||
private let optionTemplateItem = ComponentView<Empty>()
|
||||
|
||||
private let itemContainerView: UIView
|
||||
private var visibleSectionHeaders: [Int: 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 isDismissed: Bool = false
|
||||
|
||||
private var selectedPeers: [EnginePeer.Id] = []
|
||||
private var selectedCategories = Set<CategoryId>()
|
||||
private var selectedPeersByCategory: [CategoryId: [EnginePeer.Id]] = [:]
|
||||
private var selectedOptions = Set<OptionId>()
|
||||
|
||||
private var component: ShareWithPeersScreenComponent?
|
||||
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) {
|
||||
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
|
||||
}
|
||||
guard let stateValue = self.effectiveStateValue else {
|
||||
@ -412,9 +515,7 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
var topOffsetFraction = topOffset / topOffsetDistance
|
||||
topOffsetFraction = max(0.0, min(1.0, topOffsetFraction))
|
||||
|
||||
let transitionFactor: CGFloat = 1.0 - topOffsetFraction
|
||||
let _ = transitionFactor
|
||||
let _ = controller
|
||||
//let transitionFactor: CGFloat = 1.0 - topOffsetFraction
|
||||
//controller.updateModalStyleOverlayTransitionFactor(transitionFactor, transition: transition.containedViewLayoutTransition)
|
||||
|
||||
var visibleBounds = self.scrollView.bounds
|
||||
@ -427,14 +528,42 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
|
||||
var validIds: [AnyHashable] = []
|
||||
var validSectionHeaders: [AnyHashable] = []
|
||||
var validSectionBackgrounds: [AnyHashable] = []
|
||||
var sectionOffset: CGFloat = itemLayout.navigationHeight
|
||||
for sectionIndex in 0 ..< itemLayout.sections.count {
|
||||
let section = itemLayout.sections[sectionIndex]
|
||||
|
||||
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 {
|
||||
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 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
|
||||
if section.id == 0 {
|
||||
sectionTitle = "WHO CAN VIEW"
|
||||
} else if section.id == 1 {
|
||||
sectionTitle = "CONTACTS"
|
||||
} else {
|
||||
if case .chats = component.stateContext.subject {
|
||||
sectionTitle = "CHATS"
|
||||
} else {
|
||||
sectionTitle = "CONTACTS"
|
||||
}
|
||||
sectionTitle = ""
|
||||
}
|
||||
|
||||
let _ = sectionHeader.update(
|
||||
transition: sectionHeaderTransition,
|
||||
component: AnyComponent(SectionHeaderComponent(
|
||||
theme: environment.theme,
|
||||
sideInset: 16.0,
|
||||
style: itemLayout.style,
|
||||
title: sectionTitle
|
||||
)),
|
||||
environment: {},
|
||||
@ -492,7 +619,7 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
|
||||
if section.id == 0 {
|
||||
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) {
|
||||
continue
|
||||
}
|
||||
@ -519,7 +646,6 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
component: AnyComponent(CategoryListItemComponent(
|
||||
context: component.context,
|
||||
theme: environment.theme,
|
||||
sideInset: itemLayout.sideInset,
|
||||
title: item.title,
|
||||
color: item.iconColor,
|
||||
iconName: item.icon,
|
||||
@ -527,7 +653,7 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
selectionState: .editing(isSelected: self.selectedCategories.contains(item.id), isTinted: false),
|
||||
hasNext: i != component.categoryItems.count - 1,
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
guard let self, let environment = self.environment, let controller = environment.controller() else {
|
||||
return
|
||||
}
|
||||
if self.selectedCategories.contains(categoryId) {
|
||||
@ -538,7 +664,11 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
self.selectedCategories.insert(categoryId)
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -560,7 +690,11 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
base = .nobody
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -581,7 +715,7 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
}
|
||||
} else if section.id == 1 {
|
||||
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) {
|
||||
continue
|
||||
}
|
||||
@ -647,6 +781,95 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
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
|
||||
@ -678,6 +901,17 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
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)
|
||||
if let searchStateContext = self.searchStateContext, case let .search(query) = searchStateContext.subject, let value = searchStateContext.stateValue, value.peers.isEmpty {
|
||||
let sideInset: CGFloat = 44.0
|
||||
@ -830,7 +1064,14 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
|
||||
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
|
||||
if case .regular = environment.metrics.widthClass {
|
||||
containerWidth = 390.0
|
||||
@ -851,6 +1092,13 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
self.selectedCategories.insert(.selectedContacts)
|
||||
}
|
||||
|
||||
if component.screenshot {
|
||||
self.selectedOptions.insert(.screenshot)
|
||||
}
|
||||
if component.pin {
|
||||
self.selectedOptions.insert(.pin)
|
||||
}
|
||||
|
||||
var applyState = false
|
||||
self.defaultStateValue = component.stateContext.stateValue
|
||||
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
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setFillColor(environment.theme.list.plainBackgroundColor.cgColor)
|
||||
if case .stories = component.stateContext.subject {
|
||||
context.setFillColor(environment.theme.list.modalBlocksBackgroundColor.cgColor)
|
||||
} else {
|
||||
context.setFillColor(environment.theme.list.plainBackgroundColor.cgColor)
|
||||
}
|
||||
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)))
|
||||
})?.stretchableImage(withLeftCapWidth: 10, topCapHeight: 19)
|
||||
|
||||
self.navigationBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
|
||||
self.navigationSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
|
||||
self.textFieldSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
|
||||
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.navigationSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
|
||||
self.bottomBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
|
||||
self.bottomSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
|
||||
}
|
||||
|
||||
self.bottomBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
|
||||
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
|
||||
if case .stories = component.stateContext.subject {
|
||||
itemLayoutStyle = .blocks
|
||||
itemsContainerWidth = containerWidth - sideInset * 2.0
|
||||
navigationTextFieldSize = .zero
|
||||
} else {
|
||||
itemLayoutStyle = .plain
|
||||
itemsContainerWidth = containerWidth
|
||||
|
||||
var tokens: [TokenListTextField.Token] = []
|
||||
for peerId in self.selectedPeers {
|
||||
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))
|
||||
|
||||
|
||||
let categoryItemSize = self.categoryTemplateItem.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(CategoryListItemComponent(
|
||||
context: component.context,
|
||||
theme: environment.theme,
|
||||
sideInset: sideInset,
|
||||
title: "Title",
|
||||
color: .blue,
|
||||
iconName: nil,
|
||||
@ -990,7 +1256,7 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
secondaryAction: {}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: containerWidth, height: 1000.0)
|
||||
containerSize: CGSize(width: itemsContainerWidth, height: 1000.0)
|
||||
)
|
||||
let peerItemSize = self.peerTemplateItem.update(
|
||||
transition: transition,
|
||||
@ -1011,7 +1277,19 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
}
|
||||
)),
|
||||
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] = []
|
||||
@ -1023,6 +1301,12 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
itemHeight: categoryItemSize.height,
|
||||
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 {
|
||||
sections.append(ItemLayout.Section(
|
||||
id: 1,
|
||||
@ -1065,9 +1349,13 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
var actionButtonTitle = "Save Settings"
|
||||
let title: String
|
||||
switch component.stateContext.subject {
|
||||
case .stories:
|
||||
title = "Share Story"
|
||||
actionButtonTitle = "Post Story"
|
||||
case let .stories(editing):
|
||||
if editing {
|
||||
title = "Edit Story"
|
||||
} else {
|
||||
title = "Share Story"
|
||||
actionButtonTitle = "Post Story"
|
||||
}
|
||||
case .chats:
|
||||
title = "Send as a Message"
|
||||
case let .contacts(category):
|
||||
@ -1110,15 +1398,25 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
}
|
||||
navigationHeight += navigationTextFieldFrame.height
|
||||
|
||||
if case .stories = component.stateContext.subject {
|
||||
navigationHeight += 16.0
|
||||
}
|
||||
|
||||
let topInset: CGFloat
|
||||
if environment.inputHeight != 0.0 || !self.navigationTextFieldState.text.isEmpty {
|
||||
topInset = 0.0
|
||||
} else {
|
||||
if case .stories = component.stateContext.subject {
|
||||
topInset = max(0.0, availableSize.height - containerInset - 427.0)
|
||||
let inset: CGFloat
|
||||
if case let .stories(editing) = component.stateContext.subject {
|
||||
if editing {
|
||||
inset = 430.0
|
||||
} else {
|
||||
inset = 605.0
|
||||
}
|
||||
} else {
|
||||
topInset = max(0.0, availableSize.height - containerInset - 600.0)
|
||||
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)
|
||||
@ -1164,10 +1462,14 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
base = .nobody
|
||||
}
|
||||
|
||||
component.completion(EngineStoryPrivacy(
|
||||
base: base,
|
||||
additionallyIncludePeers: self.selectedPeers
|
||||
))
|
||||
component.completion(
|
||||
EngineStoryPrivacy(
|
||||
base: base,
|
||||
additionallyIncludePeers: self.selectedPeers
|
||||
),
|
||||
self.selectedOptions.contains(.screenshot),
|
||||
self.selectedOptions.contains(.pin)
|
||||
)
|
||||
|
||||
controller.dismiss()
|
||||
}
|
||||
@ -1194,12 +1496,12 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
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)))
|
||||
|
||||
let itemContainerSize = CGSize(width: containerWidth, height: availableSize.height)
|
||||
let itemLayout = ItemLayout(containerSize: itemContainerSize, containerInset: containerInset, bottomInset: bottomPanelHeight, topInset: topInset, sideInset: sideInset, navigationHeight: navigationHeight, sections: sections)
|
||||
let itemContainerSize = CGSize(width: itemsContainerWidth, height: availableSize.height)
|
||||
let itemLayout = ItemLayout(style: itemLayoutStyle, containerSize: itemContainerSize, containerInset: containerInset, bottomInset: bottomPanelHeight, topInset: topInset, sideInset: sideInset, navigationHeight: navigationHeight, sections: sections)
|
||||
let previousItemLayout = self.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)
|
||||
|
||||
@ -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.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.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 enum Subject: Equatable {
|
||||
case stories
|
||||
case stories(editing: Bool)
|
||||
case chats
|
||||
case contacts(EngineStoryPrivacy.Base)
|
||||
case search(String)
|
||||
@ -1430,11 +1732,20 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
||||
|
||||
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
|
||||
|
||||
var categoryItems: [ShareWithPeersScreenComponent.CategoryItem] = []
|
||||
if case .stories = stateContext.subject {
|
||||
var optionItems: [ShareWithPeersScreenComponent.OptionItem] = []
|
||||
if case let .stories(editing) = stateContext.subject {
|
||||
categoryItems.append(ShareWithPeersScreenComponent.CategoryItem(
|
||||
id: .everyone,
|
||||
title: "Everyone",
|
||||
@ -1482,17 +1793,30 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
||||
iconColor: .violet,
|
||||
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(
|
||||
context: context,
|
||||
stateContext: stateContext,
|
||||
initialPrivacy: initialPrivacy,
|
||||
timeout: timeout,
|
||||
screenshot: allowScreenshots,
|
||||
pin: pin,
|
||||
categoryItems: categoryItems,
|
||||
optionItems: optionItems,
|
||||
completion: completion,
|
||||
editCategory: editCategory,
|
||||
secondaryAction: secondaryAction
|
||||
editCategory: editCategory
|
||||
), navigationBarAppearance: .none, theme: .dark)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
|
@ -2587,7 +2587,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
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
|
||||
guard let self else {
|
||||
return
|
||||
@ -2595,9 +2595,8 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
let controller = ShareWithPeersScreen(
|
||||
context: context,
|
||||
initialPrivacy: privacy,
|
||||
timeout: 86400,
|
||||
stateContext: stateContext,
|
||||
completion: { [weak self] privacy in
|
||||
completion: { [weak self] privacy, _, _ in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
@ -2606,7 +2605,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
self.privacyController = nil
|
||||
self.updateIsProgressPaused()
|
||||
},
|
||||
editCategory: { [weak self] privacy in
|
||||
editCategory: { [weak self] privacy, _, _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@ -2644,9 +2643,8 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
let controller = ShareWithPeersScreen(
|
||||
context: context,
|
||||
initialPrivacy: privacy,
|
||||
timeout: 86400,
|
||||
stateContext: stateContext,
|
||||
completion: { result in
|
||||
completion: { result, _, _ in
|
||||
if case .closeFriends = privacy.base {
|
||||
let _ = context.engine.privacy.updateCloseFriends(peerIds: result.additionallyIncludePeers).start()
|
||||
completion(EngineStoryPrivacy(base: .closeFriends, additionallyIncludePeers: []))
|
||||
@ -2654,7 +2652,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
completion(result)
|
||||
}
|
||||
},
|
||||
editCategory: { _ in }
|
||||
editCategory: { _, _, _ in }
|
||||
)
|
||||
controller.dismissed = { [weak self] in
|
||||
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
|
||||
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.330000 10.665017 m
|
||||
1.330000 15.820595 5.509422 20.000017 10.665000 20.000017 c
|
||||
15.820578 20.000017 20.000000 15.820595 20.000000 10.665017 c
|
||||
20.000000 7.511573 18.436378 4.723331 16.042154 3.033316 c
|
||||
14.314164 10.809276 l
|
||||
14.246551 11.113539 13.976685 11.330017 13.665000 11.330017 c
|
||||
13.443334 11.330017 l
|
||||
11.613684 16.818966 l
|
||||
11.309785 17.730663 10.020216 17.730665 9.716317 16.818970 c
|
||||
7.886667 11.330017 l
|
||||
7.665000 11.330017 l
|
||||
7.353315 11.330017 7.083449 11.113539 7.015836 10.809276 c
|
||||
5.287845 3.033316 l
|
||||
2.893622 4.723331 1.330000 7.511574 1.330000 10.665017 c
|
||||
1.660000 10.830000 m
|
||||
1.660000 15.894451 5.765549 20.000000 10.830000 20.000000 c
|
||||
15.894451 20.000000 20.000000 15.894451 20.000000 10.830000 c
|
||||
20.000000 7.821688 18.551388 5.151716 16.313671 3.479595 c
|
||||
14.640235 11.010053 l
|
||||
14.555845 11.389809 14.219020 11.660000 13.830000 11.660000 c
|
||||
13.553333 11.660000 l
|
||||
11.778684 16.983950 l
|
||||
11.474785 17.895645 10.185216 17.895649 9.881317 16.983952 c
|
||||
8.106667 11.660000 l
|
||||
7.830000 11.660000 l
|
||||
7.440980 11.660000 7.104155 11.389809 7.019765 11.010053 c
|
||||
5.346330 3.479595 l
|
||||
3.108612 5.151716 1.660000 7.821688 1.660000 10.830000 c
|
||||
h
|
||||
13.131556 10.000017 m
|
||||
14.839726 2.313250 l
|
||||
13.583508 1.684090 12.165603 1.330017 10.665000 1.330017 c
|
||||
9.164397 1.330017 7.746492 1.684090 6.490273 2.313250 c
|
||||
8.198444 10.000017 l
|
||||
13.131556 10.000017 l
|
||||
13.164198 10.000000 m
|
||||
14.815517 2.569059 l
|
||||
13.610337 1.986547 12.258304 1.660000 10.830000 1.660000 c
|
||||
9.401696 1.660000 8.049662 1.986547 6.844482 2.569059 c
|
||||
8.495803 10.000000 l
|
||||
13.164198 10.000000 l
|
||||
h
|
||||
10.665000 21.330017 m
|
||||
4.774883 21.330017 0.000000 16.555134 0.000000 10.665017 c
|
||||
0.000000 4.774900 4.774883 0.000015 10.665000 0.000015 c
|
||||
16.555117 0.000015 21.330002 4.774900 21.330002 10.665017 c
|
||||
21.330002 16.555134 16.555117 21.330017 10.665000 21.330017 c
|
||||
10.830000 21.660000 m
|
||||
4.848756 21.660000 0.000000 16.811243 0.000000 10.830000 c
|
||||
0.000000 4.848755 4.848756 0.000000 10.830000 0.000000 c
|
||||
16.811245 0.000000 21.660000 4.848755 21.660000 10.830000 c
|
||||
21.660000 16.811243 16.811245 21.660000 10.830000 21.660000 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_editor_tools.pdf",
|
||||
"filename" : "tools_30.pdf",
|
||||
"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
|
||||
}
|
||||
|
||||
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
|
||||
}, openActiveTextItem: { [weak self] item, action in
|
||||
guard let strongSelf = self else {
|
||||
@ -14595,7 +14595,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
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
|
||||
}, openActiveTextItem: { [weak self] item, action in
|
||||
guard let strongSelf = self else {
|
||||
@ -14709,7 +14709,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
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
|
||||
}, openActiveTextItem: { [weak self] item, action in
|
||||
guard let strongSelf = self else {
|
||||
|
@ -24,10 +24,8 @@ import LegacyMediaPickerUI
|
||||
import LegacyCamera
|
||||
import AvatarNode
|
||||
import LocalMediaResources
|
||||
import ShareWithPeersScreen
|
||||
import ImageCompression
|
||||
import TextFormat
|
||||
import UndoUI
|
||||
|
||||
private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceholderNode {
|
||||
private var presentationData: PresentationData
|
||||
@ -369,7 +367,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
case let .image(image, dimensions):
|
||||
if let imageData = compressImageToJPEG(image, quality: 0.7) {
|
||||
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 {
|
||||
commit({})
|
||||
}
|
||||
@ -392,7 +390,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
}
|
||||
let imageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6) }
|
||||
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 {
|
||||
commit({})
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
private var arrowGradientNode: ASDisplayNode?
|
||||
private let arrowNode: ASImageNode
|
||||
private let arrowContainer: ASDisplayNode
|
||||
private let animatedStickerNode: AnimatedStickerNode
|
||||
private let animatedStickerNode: DefaultAnimatedStickerNodeImpl
|
||||
private var downArrowsNode: DownArrowsIconNode?
|
||||
private var avatarNode: AvatarNode?
|
||||
private var avatarStoryIndicator: ComponentView<Empty>?
|
||||
@ -374,9 +374,10 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
switch icon {
|
||||
case .none:
|
||||
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.automaticallyLoadFirstFrame = true
|
||||
self.animatedStickerNode.dynamicColor = animationTintColor
|
||||
case .downArrows:
|
||||
self.downArrowsNode = DownArrowsIconNode()
|
||||
case let .peer(peer, _):
|
||||
@ -510,21 +511,25 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
let contentInset: CGFloat = 11.0
|
||||
let contentVerticalInset: CGFloat = 8.0
|
||||
let animationSize: CGSize
|
||||
let animationInset: CGFloat
|
||||
let animationSpacing: CGFloat
|
||||
var animationInset: CGFloat = 0.0
|
||||
var animationSpacing: CGFloat = 0.0
|
||||
var animationOffset: CGFloat = 0.0
|
||||
|
||||
switch self.icon {
|
||||
case .none:
|
||||
animationSize = CGSize()
|
||||
animationInset = 0.0
|
||||
animationSpacing = 0.0
|
||||
case .downArrows:
|
||||
animationSize = CGSize(width: 24.0, height: 32.0)
|
||||
animationInset = (40.0 - animationSize.width) / 2.0
|
||||
animationSpacing = 8.0
|
||||
case let .animation(animationName, _):
|
||||
animationSize = CGSize(width: 32.0, height: 32.0)
|
||||
if animationName == "ChatListFoldersTooltip" {
|
||||
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)
|
||||
}
|
||||
if animationName == "anim_autoremove_on" {
|
||||
animationOffset = -3.0
|
||||
} else if animationName == "ChatListFoldersTooltip" {
|
||||
animationInset = (70.0 - animationSize.width) / 2.0
|
||||
} else {
|
||||
animationInset = 0.0
|
||||
@ -555,12 +560,14 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
|
||||
var backgroundHeight: CGFloat
|
||||
switch self.tooltipStyle {
|
||||
case .default, .gradient, .customBlur:
|
||||
case .default, .gradient, .customBlur, .wide:
|
||||
backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0
|
||||
case .light:
|
||||
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
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
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)
|
||||
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
|
||||
switch self.icon {
|
||||
case let .animation(_, delay):
|
||||
case let .animation(_, delay, _):
|
||||
animationDelay = delay
|
||||
case .none, .downArrows:
|
||||
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 {
|
||||
case .top, .bottom:
|
||||
self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
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
|
||||
if case .top = self.location {
|
||||
offset = -13.0 - self.backgroundContainerNode.frame.height
|
||||
@ -884,7 +891,7 @@ public final class TooltipScreen: ViewController {
|
||||
}
|
||||
|
||||
public enum Icon {
|
||||
case animation(name: String, delay: Double)
|
||||
case animation(name: String, delay: Double, tintColor: UIColor?)
|
||||
case peer(peer: EnginePeer, isStory: Bool)
|
||||
case downArrows
|
||||
}
|
||||
@ -918,6 +925,7 @@ public final class TooltipScreen: ViewController {
|
||||
case light
|
||||
case customBlur(UIColor)
|
||||
case gradient(UIColor, UIColor)
|
||||
case wide
|
||||
}
|
||||
|
||||
public enum Alignment {
|
||||
@ -1073,13 +1081,13 @@ public final class TooltipScreen: ViewController {
|
||||
self.controllerNode.addRelativeScrollingOffset(value, transition: transition)
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
public func dismiss(inPlace: Bool, completion: (() -> Void)? = nil) {
|
||||
if self.isDismissed {
|
||||
return
|
||||
}
|
||||
self.isDismissed = true
|
||||
self.willBecomeDismissed?(self)
|
||||
self.controllerNode.animateOut(completion: { [weak self] in
|
||||
self.controllerNode.animateOut(inPlace: inPlace, completion: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -1088,4 +1096,8 @@ public final class TooltipScreen: ViewController {
|
||||
becameDismissed?(strongSelf)
|
||||
})
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.dismiss(inPlace: false, completion: completion)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user