Various improvements

This commit is contained in:
Ilya Laktyushin 2023-09-01 00:02:49 +04:00
parent 24b671a53c
commit 4b5ccefbc8
6 changed files with 198 additions and 68 deletions

View File

@ -9878,3 +9878,17 @@ Sorry for the inconvenience.";
"SecretImage.ViewOnce.Title" = "Disappearing Photo"; "SecretImage.ViewOnce.Title" = "Disappearing Photo";
"SecretVideo.ViewOnce.Title" = "Disappearing Video"; "SecretVideo.ViewOnce.Title" = "Disappearing Video";
"MediaPicker.Timer.Description" = "Choose how long the media will be kept after opening.";
"MediaPicker.Timer.ViewOnce" = "View Once";
"MediaPicker.Timer.Seconds_1" = "%d Second";
"MediaPicker.Timer.Seconds_any" = "%d Seconds";
"MediaPicker.Timer.DoNotDelete" = "Do Not Delete";
"MediaPicker.Timer.Photo.ViewOnceTooltip" = "Photo set to view once.";
"MediaPicker.Timer.Photo.TimerTooltip" = "Photo will be deleted in\n%@ seconds after opening.";
"MediaPicker.Timer.Photo.KeepTooltip" = "Photo will be kept in chat.";
"MediaPicker.Timer.Video.ViewOnceTooltip" = "Video set to view once.";
"MediaPicker.Timer.Video.TimerTooltip" = "Video will be deleted in\n%@ seconds after opening.";
"MediaPicker.Timer.Video.KeepTooltip" = "Video will be kept in chat.";

View File

@ -392,11 +392,15 @@ public final class DrawingStickerEntityView: DrawingEntityView {
} }
override func onSelection() { override func onSelection() {
self.presentReactionSelection() if self.isReaction {
self.presentReactionSelection()
}
} }
func onDeselection() { func onDeselection() {
let _ = self.dismissReactionSelection() if self.isReaction {
let _ = self.dismissReactionSelection()
}
} }
private weak var reactionContextNode: ReactionContextNode? private weak var reactionContextNode: ReactionContextNode?

View File

@ -880,6 +880,10 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
displayCaption = !self.textNode.isHidden displayCaption = !self.textNode.isHidden
} }
if metrics.isTablet {
self.fullscreenButton.isHidden = true
}
var textFrame = CGRect() var textFrame = CGRect()
var visibleTextHeight: CGFloat = 0.0 var visibleTextHeight: CGFloat = 0.0
if !self.textNode.isHidden { if !self.textNode.isHidden {

View File

@ -81,6 +81,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView {
} }
public func setTimeout(_ timeout: Int32) { public func setTimeout(_ timeout: Int32) {
self.dismissTimeoutTooltip()
var timeout: Int32? = timeout var timeout: Int32? = timeout
if timeout == 0 { if timeout == 0 {
timeout = nil timeout = nil
@ -250,12 +251,12 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView {
let currentValue = self.currentTimeout let currentValue = self.currentTimeout
let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkPresentationTheme) let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkPresentationTheme)
let title = "Choose how long the media will be kept after opening." let title = presentationData.strings.MediaPicker_Timer_Description
let emptyAction: ((ContextMenuActionItem.Action) -> Void)? = nil let emptyAction: ((ContextMenuActionItem.Action) -> Void)? = nil
items.append(.action(ContextMenuActionItem(text: title, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: emptyAction))) items.append(.action(ContextMenuActionItem(text: title, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: emptyAction)))
items.append(.action(ContextMenuActionItem(text: "View Once", icon: { theme in items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPicker_Timer_ViewOnce, icon: { theme in
return currentValue == viewOnceTimeout ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil return currentValue == viewOnceTimeout ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, a in }, action: { _, a in
a(.default) a(.default)
@ -263,31 +264,19 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView {
updateTimeout(viewOnceTimeout) updateTimeout(viewOnceTimeout)
}))) })))
items.append(.action(ContextMenuActionItem(text: "3 Seconds", icon: { theme in let values: [Int32] = [3, 10, 30]
return currentValue == 3 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, a in
a(.default)
updateTimeout(3)
})))
items.append(.action(ContextMenuActionItem(text: "10 Seconds", icon: { theme in for value in values {
return currentValue == 10 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPicker_Timer_Seconds(value), icon: { theme in
}, action: { _, a in return currentValue == value ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
a(.default) }, action: { _, a in
a(.default)
updateTimeout(value)
})))
}
updateTimeout(10) items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPicker_Timer_DoNotDelete, icon: { theme in
})))
items.append(.action(ContextMenuActionItem(text: "30 Seconds", icon: { theme in
return currentValue == 30 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, a in
a(.default)
updateTimeout(30)
})))
items.append(.action(ContextMenuActionItem(text: "Do Not Delete", icon: { theme in
return currentValue == nil ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil return currentValue == nil ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, a in }, action: { _, a in
a(.default) a(.default)
@ -300,29 +289,36 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView {
} }
private weak var tooltipController: TooltipScreen? private weak var tooltipController: TooltipScreen?
private func presentTimeoutTooltip(sourceView: UIView, timeout: Int32?) {
guard let superview = self.view.superview?.superview else { private func dismissTimeoutTooltip() {
return
}
if let tooltipController = self.tooltipController { if let tooltipController = self.tooltipController {
self.tooltipController = nil self.tooltipController = nil
tooltipController.dismiss() tooltipController.dismiss()
} }
}
private func presentTimeoutTooltip(sourceView: UIView, timeout: Int32?) {
guard let superview = self.view.superview?.superview else {
return
}
self.dismissTimeoutTooltip()
let parentFrame = superview.convert(superview.bounds, to: nil) let parentFrame = superview.convert(superview.bounds, to: nil)
let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0) let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0)
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 2.0), size: CGSize()) let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 2.0), size: CGSize())
let isVideo = !"".isEmpty
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let text: String let text: String
let iconName: String let iconName: String
if timeout == viewOnceTimeout { if timeout == viewOnceTimeout {
text = "Photo set to view once." text = isVideo ? presentationData.strings.MediaPicker_Timer_Video_ViewOnceTooltip : presentationData.strings.MediaPicker_Timer_Photo_ViewOnceTooltip
iconName = "anim_autoremove_on" iconName = "anim_autoremove_on"
} else if let timeout { } else if let timeout {
text = "Photo will be deleted in \(timeout) seconds after opening." text = isVideo ? presentationData.strings.MediaPicker_Timer_Video_TimerTooltip("\(timeout)").string : presentationData.strings.MediaPicker_Timer_Photo_TimerTooltip("\(timeout)").string
iconName = "anim_autoremove_on" iconName = "anim_autoremove_on"
} else { } else {
text = "Photo will be kept in chat." text = isVideo ? presentationData.strings.MediaPicker_Timer_Video_KeepTooltip : presentationData.strings.MediaPicker_Timer_Photo_KeepTooltip
iconName = "anim_autoremove_off" iconName = "anim_autoremove_off"
} }
@ -330,7 +326,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView {
account: self.context.account, account: self.context.account,
sharedContext: self.context.sharedContext, sharedContext: self.context.sharedContext,
text: .plain(text: text), text: .plain(text: text),
balancedTextLayout: true, balancedTextLayout: false,
style: .customBlur(UIColor(rgb: 0x18181a), 0.0), style: .customBlur(UIColor(rgb: 0x18181a), 0.0),
arrowStyle: .small, arrowStyle: .small,
icon: .animation(name: iconName, delay: 0.1, tintColor: nil), icon: .animation(name: iconName, delay: 0.1, tintColor: nil),

View File

@ -1456,42 +1456,17 @@ public final class MessageInputPanelComponent: Component {
let accentColor = component.theme.chat.inputPanel.panelControlAccentColor let accentColor = component.theme.chat.inputPanel.panelControlAccentColor
if let timeoutAction = component.timeoutAction, let timeoutValue = component.timeoutValue { if let timeoutAction = component.timeoutAction, let timeoutValue = component.timeoutValue {
func generateIcon(value: String, selected: Bool) -> UIImage? {
let image = UIImage(bundleImageName: "Media Editor/Timeout")!
let valueString = NSAttributedString(string: value, font: Font.with(size: value.count == 1 ? 12.0 : 10.0, design: .round, weight: .semibold), textColor: .white, paragraphAlignment: .center)
return generateImage(image.size, contextGenerator: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds)
if selected {
context.setFillColor(accentColor.cgColor)
context.fillEllipse(in: CGRect(origin: .zero, size: size))
} else {
if let cgImage = image.cgImage {
context.draw(cgImage, in: CGRect(origin: .zero, size: size))
}
}
var offset: CGPoint = CGPoint(x: 0.0, y: -3.0 - UIScreenPixel)
if value == "" {
offset.x += UIScreenPixel
offset.y += 1.0 - UIScreenPixel
}
let valuePath = CGMutablePath()
valuePath.addRect(bounds.offsetBy(dx: offset.x, dy: offset.y))
let valueFramesetter = CTFramesetterCreateWithAttributedString(valueString as CFAttributedString)
let valyeFrame = CTFramesetterCreateFrame(valueFramesetter, CFRangeMake(0, valueString.length), valuePath, nil)
CTFrameDraw(valyeFrame, context)
})
}
let icon = generateIcon(value: timeoutValue, selected: component.timeoutSelected)
let timeoutButtonSize = self.timeoutButton.update( let timeoutButtonSize = self.timeoutButton.update(
transition: transition, transition: transition,
component: AnyComponent(Button( component: AnyComponent(Button(
content: AnyComponent(Image(image: icon, size: CGSize(width: 20.0, height: 20.0))), content: AnyComponent(
TimeoutContentComponent(
color: .white,
accentColor: accentColor,
isSelected: component.timeoutSelected,
value: timeoutValue
)
),
action: { [weak self] in action: { [weak self] in
guard let self, let timeoutButtonView = self.timeoutButton.view else { guard let self, let timeoutButtonView = self.timeoutButton.view else {
return return

View File

@ -0,0 +1,137 @@
import Foundation
import UIKit
import Display
import ComponentFlow
public final class TimeoutContentComponent: Component {
public let color: UIColor
public let accentColor: UIColor
public let isSelected: Bool
public let value: String
public init(
color: UIColor,
accentColor: UIColor,
isSelected: Bool,
value: String
) {
self.color = color
self.accentColor = accentColor
self.isSelected = isSelected
self.value = value
}
public static func ==(lhs: TimeoutContentComponent, rhs: TimeoutContentComponent) -> Bool {
if lhs.color != rhs.color {
return false
}
if lhs.accentColor != rhs.accentColor {
return false
}
if lhs.isSelected != rhs.isSelected {
return false
}
if lhs.value != rhs.value {
return false
}
return true
}
public final class View: UIView {
private var component: TimeoutContentComponent?
private weak var state: EmptyComponentState?
private let background: UIImageView
private let foreground: UIImageView
private let text = ComponentView<Empty>()
override init(frame: CGRect) {
self.background = UIImageView(image: UIImage(bundleImageName: "Media Editor/Timeout"))
self.foreground = UIImageView()
super.init(frame: frame)
self.addSubview(self.background)
self.addSubview(self.foreground)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(component: TimeoutContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
let previousComponent = self.component
self.component = component
self.state = state
let size = CGSize(width: 20.0, height: 20.0)
if previousComponent?.accentColor != component.accentColor {
self.foreground.image = generateFilledCircleImage(diameter: size.width, color: component.accentColor)
}
var updated = false
if let previousComponent {
if previousComponent.isSelected != component.isSelected {
updated = true
}
if previousComponent.value != component.value {
if let textView = self.text.view, let snapshotView = textView.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = textView.frame
self.addSubview(snapshotView)
snapshotView.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: 3.0), duration: 0.2, removeOnCompletion: false, additive: true)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
snapshotView.removeFromSuperview()
})
textView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
textView.layer.animatePosition(from: CGPoint(x: 0.0, y: -3.0), to: .zero, duration: 0.2, additive: true)
}
}
}
let fontSize: CGFloat
let textOffset: CGFloat
if component.value.count == 1 {
fontSize = 12.0
textOffset = UIScreenPixel
} else {
fontSize = 10.0
textOffset = -UIScreenPixel
}
let font = Font.with(size: fontSize, design: .round, weight: .semibold)
let textSize = self.text.update(
transition: .immediate,
component: AnyComponent(Text(text: component.value, font: font, color: .white)),
environment: {},
containerSize: CGSize(width: 100.0, height: 100.0)
)
if let textView = self.text.view {
if textView.superview == nil {
self.addSubview(textView)
}
let textFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0) + UIScreenPixel, y: floorToScreenPixels((size.height - textSize.height) / 2.0) + textOffset), size: textSize)
transition.setPosition(view: textView, position: textFrame.center)
textView.bounds = CGRect(origin: CGPoint(), size: textFrame.size)
}
self.background.frame = CGRect(origin: .zero, size: size)
self.foreground.bounds = CGRect(origin: .zero, size: size)
self.foreground.center = CGPoint(x: size.width / 2.0, y: size.height / 2.0)
let foregroundTransition: Transition = updated ? .easeInOut(duration: 0.2) : transition
foregroundTransition.setScale(view: self.foreground, scale: component.isSelected ? 1.0 : 0.001)
return size
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public 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)
}
}