Various improvements

This commit is contained in:
Ilya Laktyushin
2025-09-17 20:58:42 +04:00
parent 0f648157fa
commit cb523192d5
73 changed files with 3460 additions and 428 deletions

View File

@@ -22,6 +22,7 @@ import ContextReferenceButtonComponent
import ForwardInfoPanelComponent
import MultilineTextComponent
import PlainButtonComponent
import GlassBackgroundComponent
private var sharedIsReduceTransparencyEnabled = UIAccessibility.isReduceTransparencyEnabled
@@ -48,6 +49,7 @@ public final class MessageInputPanelComponent: Component {
case story
case editor
case media
case glass
}
public enum InputMode: Hashable {
@@ -141,7 +143,7 @@ public final class MessageInputPanelComponent: Component {
}
public final class ExternalState {
public fileprivate(set) var isEditing: Bool = false
public var isEditing: Bool = false
public fileprivate(set) var hasText: Bool = false
public fileprivate(set) var isKeyboardHidden: Bool = false
@@ -154,6 +156,18 @@ public final class MessageInputPanelComponent: Component {
}
}
public final class SendActionTransition {
public let textSnapshotView: UIView
public let globalFrame: CGRect
public let cornerRadius: CGFloat
init(textSnapshotView: UIView, globalFrame: CGRect, cornerRadius: CGFloat) {
self.textSnapshotView = textSnapshotView
self.globalFrame = globalFrame
self.cornerRadius = cornerRadius
}
}
public let externalState: ExternalState
public let context: AccountContext
public let theme: PresentationTheme
@@ -170,7 +184,7 @@ public final class MessageInputPanelComponent: Component {
public let areVoiceMessagesAvailable: Bool
public let presentController: (ViewController) -> Void
public let presentInGlobalOverlay: (ViewController) -> Void
public let sendMessageAction: () -> Void
public let sendMessageAction: (SendActionTransition?) -> Void
public let sendMessageOptionsAction: ((UIView, ContextGesture?) -> Void)?
public let sendStickerAction: (TelegramMediaFile) -> Void
public let setMediaRecordingActive: ((Bool, Bool, Bool, UIView?) -> Void)?
@@ -229,7 +243,7 @@ public final class MessageInputPanelComponent: Component {
areVoiceMessagesAvailable: Bool,
presentController: @escaping (ViewController) -> Void,
presentInGlobalOverlay: @escaping (ViewController) -> Void,
sendMessageAction: @escaping () -> Void,
sendMessageAction: @escaping (SendActionTransition?) -> Void,
sendMessageOptionsAction: ((UIView, ContextGesture?) -> Void)?,
sendStickerAction: @escaping (TelegramMediaFile) -> Void,
setMediaRecordingActive: ((Bool, Bool, Bool, UIView?) -> Void)?,
@@ -464,6 +478,7 @@ public final class MessageInputPanelComponent: Component {
public final class View: UIView {
private let fieldBackgroundView: BlurredBackgroundView
private let fieldBackgroundTint: UIView
private var fieldGlassBackgroundView: GlassBackgroundView?
private let gradientView: UIImageView
private let bottomGradientView: UIView
@@ -773,12 +788,18 @@ public final class MessageInputPanelComponent: Component {
insets.right = 41.0
}
var textFieldSideInset = 9.0
if case .media = component.style {
let textFieldSideInset: CGFloat
switch component.style {
case .media, .glass:
textFieldSideInset = 8.0
default:
textFieldSideInset = 9.0
}
let mediaInsets = UIEdgeInsets(top: insets.top, left: textFieldSideInset, bottom: insets.bottom, right: 41.0)
var mediaInsets = UIEdgeInsets(top: insets.top, left: textFieldSideInset, bottom: insets.bottom, right: 41.0)
if case .glass = component.style {
mediaInsets.right = 54.0
}
let baseFieldHeight: CGFloat = 40.0
@@ -1025,6 +1046,8 @@ public final class MessageInputPanelComponent: Component {
var fieldBackgroundFrame: CGRect
if hasMediaRecording {
fieldBackgroundFrame = CGRect(origin: CGPoint(x: mediaInsets.left, y: insets.top), size: CGSize(width: availableSize.width - mediaInsets.left - mediaInsets.right, height: fieldFrame.height))
} else if case .glass = component.style {
fieldBackgroundFrame = CGRect(origin: CGPoint(x: mediaInsets.left, y: insets.top), size: CGSize(width: availableSize.width - mediaInsets.left - mediaInsets.right, height: fieldFrame.height))
} else if isEditing || component.style == .editor || component.style == .media {
fieldBackgroundFrame = fieldFrame
} else {
@@ -1042,6 +1065,25 @@ public final class MessageInputPanelComponent: Component {
//transition.setFrame(view: self.vibrancyEffectView, frame: CGRect(origin: CGPoint(), size: fieldBackgroundFrame.size))
switch component.style {
case .glass:
if self.fieldGlassBackgroundView == nil {
let fieldGlassBackgroundView = GlassBackgroundView(frame: fieldBackgroundFrame)
self.insertSubview(fieldGlassBackgroundView, aboveSubview: self.fieldBackgroundView)
self.fieldGlassBackgroundView = fieldGlassBackgroundView
self.fieldBackgroundView.isHidden = true
self.fieldBackgroundTint.isHidden = true
}
if let fieldGlassBackgroundView = self.fieldGlassBackgroundView {
let backgroundColor = UIColor(rgb: 0x1b1d22)
fieldGlassBackgroundView.update(size: fieldBackgroundFrame.size, cornerRadius: baseFieldHeight * 0.5, isDark: true, tintColor: backgroundColor, transition: transition)
transition.setFrame(view: fieldGlassBackgroundView, frame: fieldBackgroundFrame)
}
default:
break
}
transition.setFrame(view: self.fieldBackgroundView, frame: fieldBackgroundFrame)
self.fieldBackgroundView.update(size: fieldBackgroundFrame.size, cornerRadius: headerHeight > 0.0 ? 18.0 : baseFieldHeight * 0.5, transition: transition.containedViewLayoutTransition)
transition.setFrame(view: self.fieldBackgroundTint, frame: fieldBackgroundFrame)
@@ -1063,7 +1105,7 @@ public final class MessageInputPanelComponent: Component {
transition.setAlpha(view: self.bottomGradientView, alpha: component.displayGradient ? 1.0 : 0.0)
let placeholderOriginX: CGFloat
if isEditing || component.style == .story {
if isEditing || component.style == .story || component.style == .glass {
placeholderOriginX = 16.0
} else {
placeholderOriginX = floorToScreenPixels(fieldBackgroundFrame.minX + (fieldBackgroundFrame.width - placeholderSize.width) / 2.0)
@@ -1456,6 +1498,7 @@ public final class MessageInputPanelComponent: Component {
}
}
var inputActionButtonAvailableSize = CGSize(width: 33.0, height: 33.0)
var inputActionButtonAlpha = 1.0
let inputActionButtonMode: MessageInputActionButtonComponent.Mode
if case .editor = component.style {
@@ -1469,12 +1512,19 @@ public final class MessageInputPanelComponent: Component {
if !isEditing {
inputActionButtonAlpha = 0.0
}
} else if case .glass = component.style {
inputActionButtonAvailableSize = CGSize(width: 40.0, height: 40.0)
if self.textFieldExternalState.hasText {
inputActionButtonMode = .send
} else {
inputActionButtonMode = .close
}
} else {
if hasMediaEditing {
inputActionButtonMode = .send
} else {
if self.textFieldExternalState.hasText {
if let sendPaidMessageStars = component.sendPaidMessageStars, "".isEmpty {
if let sendPaidMessageStars = component.sendPaidMessageStars, !"".isEmpty {
inputActionButtonMode = .stars(sendPaidMessageStars.value)
} else {
inputActionButtonMode = .send
@@ -1494,6 +1544,7 @@ public final class MessageInputPanelComponent: Component {
transition: transition,
component: AnyComponent(MessageInputActionButtonComponent(
mode: inputActionButtonMode,
style: component.style == .glass ? .glass : .legacy,
storyId: component.storyItem?.id,
action: { [weak self] mode, action, sendAction in
guard let self, let component = self.component else {
@@ -1503,19 +1554,29 @@ public final class MessageInputPanelComponent: Component {
switch mode {
case .none:
break
case .close:
component.sendMessageAction(nil)
case .send, .stars:
if case .up = action {
if component.recordedAudioPreview != nil {
component.sendMessageAction()
component.sendMessageAction(nil)
} else if component.hasRecordedVideoPreview {
component.sendMessageAction()
component.sendMessageAction(nil)
} else if case let .text(string) = self.getSendMessageInput(), string.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
} else {
if let maxLength = component.maxLength, self.textFieldExternalState.textLength > maxLength {
self.animateError()
component.presentTextLengthLimitTooltip?()
} else {
component.sendMessageAction()
var sendActionTransition: MessageInputPanelComponent.SendActionTransition?
if let snapshotView = self.textClippingView.snapshotView(afterScreenUpdates: false), let backgroundView = self.fieldGlassBackgroundView {
sendActionTransition = MessageInputPanelComponent.SendActionTransition(
textSnapshotView: snapshotView,
globalFrame: backgroundView.convert(backgroundView.bounds, to: nil),
cornerRadius: baseFieldHeight * 0.5
)
}
component.sendMessageAction(sendActionTransition)
}
}
}
@@ -1525,7 +1586,7 @@ public final class MessageInputPanelComponent: Component {
self.animateError()
component.presentTextLengthLimitTooltip?()
} else {
component.sendMessageAction()
component.sendMessageAction(nil)
}
}
case .voiceInput, .videoInput:
@@ -1587,7 +1648,7 @@ public final class MessageInputPanelComponent: Component {
hasShadow: component.style == .editor
)),
environment: {},
containerSize: CGSize(width: 33.0, height: 33.0)
containerSize: inputActionButtonAvailableSize
)
let hasLikeAction: Bool
@@ -1617,8 +1678,13 @@ public final class MessageInputPanelComponent: Component {
inputActionButtonOriginX -= 46.0
}
} else {
if component.setMediaRecordingActive != nil || isEditing {
inputActionButtonOriginX = fieldBackgroundFrame.maxX + floorToScreenPixels((41.0 - inputActionButtonSize.width) * 0.5)
if component.setMediaRecordingActive != nil || isEditing || component.style == .glass {
switch component.style {
case .glass:
inputActionButtonOriginX = fieldBackgroundFrame.maxX + 6.0
default:
inputActionButtonOriginX = fieldBackgroundFrame.maxX + floorToScreenPixels((41.0 - inputActionButtonSize.width) * 0.5)
}
} else {
inputActionButtonOriginX = size.width
}