Various improvements

This commit is contained in:
Ilya Laktyushin 2023-06-29 06:39:59 +02:00
parent 1387bb5416
commit 7b0bbd0cce
30 changed files with 1105 additions and 654 deletions

View File

@ -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)
}

View File

@ -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?()
}
}
}

View File

@ -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) {

View File

@ -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):

View File

@ -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())
})
}

View File

@ -32,6 +32,7 @@ swift_library(
"//submodules/Components/PagerComponent:PagerComponent",
"//submodules/PremiumUI",
"//submodules/ProgressNavigationButtonNode",
"//submodules/TelegramUI/Components/SwitchComponent",
],
visibility = [
"//visibility:public",

View File

@ -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

View File

@ -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);
}
}

View File

@ -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)
}
}

View File

@ -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)
),

View File

@ -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

View File

@ -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,

View File

@ -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",

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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)
// }
//}

View File

@ -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(

View File

@ -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

View File

@ -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 {

View 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",
],
)

View File

@ -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)
}
}

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "arrow_left.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View File

@ -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

View File

@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "ic_editor_tools.pdf",
"filename" : "tools_30.pdf",
"idiom" : "universal"
}
],

View 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

View File

@ -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 {

View File

@ -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({})
}

View File

@ -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)
}
}