Various improvements

This commit is contained in:
Ilya Laktyushin
2025-09-19 22:48:59 +04:00
parent 80a7f9fcd2
commit b65a684431
15 changed files with 476 additions and 198 deletions

View File

@@ -59,7 +59,7 @@ class ChatSlowmodeItem: ListViewItem, ItemListItem {
}
}
private let allowedValues: [Int32] = [0, 10, 30, 60, 300, 900, 3600]
private let allowedValues: [Int32] = [0, 5, 10, 30, 60, 300, 900, 3600]
class ChatSlowmodeItemNode: ListViewItemNode {
private let backgroundNode: ASDisplayNode

View File

@@ -60,7 +60,7 @@ private enum UsernameSetupEntry: ItemListNodeEntry {
case publicLinkInfo(PresentationTheme, String)
case additionalLinkHeader(PresentationTheme, String)
case additionalLink(PresentationTheme, TelegramPeerUsername, Int32)
case additionalLink(PresentationTheme, TelegramPeerUsername, Int32, Bool)
case additionalLinkInfo(PresentationTheme, String)
var section: ItemListSectionId {
@@ -84,7 +84,7 @@ private enum UsernameSetupEntry: ItemListNodeEntry {
return .index(3)
case .additionalLinkHeader:
return .index(4)
case let .additionalLink(_, username, _):
case let .additionalLink(_, username, _, _):
return .username(username.username)
case .additionalLinkInfo:
return .index(5)
@@ -123,8 +123,8 @@ private enum UsernameSetupEntry: ItemListNodeEntry {
} else {
return false
}
case let .additionalLink(lhsTheme, lhsAddressName, lhsIndex):
if case let .additionalLink(rhsTheme, rhsAddressName, rhsIndex) = rhs, lhsTheme === rhsTheme, lhsAddressName == rhsAddressName, lhsIndex == rhsIndex {
case let .additionalLink(lhsTheme, lhsAddressName, lhsIndex, lhsCanToggleIsActive):
if case let .additionalLink(rhsTheme, rhsAddressName, rhsIndex, rhsCanToggleIsActive) = rhs, lhsTheme === rhsTheme, lhsAddressName == rhsAddressName, lhsIndex == rhsIndex, lhsCanToggleIsActive == rhsCanToggleIsActive {
return true
} else {
return false
@@ -175,9 +175,9 @@ private enum UsernameSetupEntry: ItemListNodeEntry {
default:
return true
}
case let .additionalLink(_, _, lhsIndex):
case let .additionalLink(_, _, lhsIndex, _):
switch rhs {
case let .additionalLink(_, _, rhsIndex):
case let .additionalLink(_, _, rhsIndex, _):
return lhsIndex < rhsIndex
case .publicLinkHeader, .editablePublicLink, .publicLinkStatus, .publicLinkInfo, .additionalLinkHeader:
return false
@@ -232,9 +232,9 @@ private enum UsernameSetupEntry: ItemListNodeEntry {
}, sectionId: self.section)
case let .additionalLinkHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .additionalLink(_, link, _):
case let .additionalLink(_, link, _, canToggleIsActive):
return AdditionalLinkItem(presentationData: presentationData, username: link, sectionId: self.section, style: .blocks, tapAction: {
if !link.flags.contains(.isEditable) {
if canToggleIsActive {
if link.isActive {
arguments.deactivateLink(link.username)
} else {
@@ -348,8 +348,12 @@ private func usernameSetupControllerEntries(presentationData: PresentationData,
entries.append(.publicLinkStatus(presentationData.theme, currentUsername, status, statusText, currentUsername))
}
var isBot = false
let otherUsernames = peer.usernames.filter { !$0.flags.contains(.isEditable) }
if case .bot = mode {
isBot = true
var infoText = presentationData.strings.Username_BotLinkHint
if otherUsernames.isEmpty {
infoText = presentationData.strings.Username_BotLinkHintExtended
@@ -384,7 +388,11 @@ private func usernameSetupControllerEntries(presentationData: PresentationData,
}
var i: Int32 = 0
for username in usernames {
entries.append(.additionalLink(presentationData.theme, username, i))
var canToggleIsActive = false
if !username.flags.contains(.isEditable) || isBot {
canToggleIsActive = true
}
entries.append(.additionalLink(presentationData.theme, username, i, canToggleIsActive))
i += 1
}
@@ -613,7 +621,7 @@ public func usernameSetupController(context: AccountContext, mode: UsernameSetup
controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [UsernameSetupEntry]) -> Signal<Bool, NoError> in
let fromEntry = entries[fromIndex]
guard case let .additionalLink(_, fromUsername, _) = fromEntry else {
guard case let .additionalLink(_, fromUsername, _, _) = fromEntry else {
return .single(false)
}
var referenceId: String?
@@ -626,7 +634,7 @@ public func usernameSetupController(context: AccountContext, mode: UsernameSetup
var i = 0
for entry in entries {
switch entry {
case let .additionalLink(_, link, _):
case let .additionalLink(_, link, _, _):
currentUsernames.append(link.username)
if !link.isActive && maxIndex == nil {
maxIndex = max(0, i - 1)
@@ -639,7 +647,7 @@ public func usernameSetupController(context: AccountContext, mode: UsernameSetup
if toIndex < entries.count {
switch entries[toIndex] {
case let .additionalLink(_, toUsername, _):
case let .additionalLink(_, toUsername, _, _):
if toUsername.isActive {
referenceId = toUsername.username
} else {
@@ -716,7 +724,7 @@ public func usernameSetupController(context: AccountContext, mode: UsernameSetup
var currentUsernames: [TelegramPeerUsername] = []
for entry in entries {
switch entry {
case let .additionalLink(_, username, _):
case let .additionalLink(_, username, _, _):
currentUsernames.append(username)
default:
break

View File

@@ -142,10 +142,11 @@ final class VideoChatActionButtonComponent: Component {
let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.2)
let genericBackgroundColor = UIColor(rgb: 0x1b1d22)
let genericBackgroundColor = UIColor(rgb: 0x2d2f38)
let titleText: String
let backgroundColor: UIColor
var tintColorKind: GlassBackgroundView.TintColor.Kind = .panel
let iconDiameter: CGFloat
var isEnabled: Bool = true
switch component.content {
@@ -208,6 +209,7 @@ final class VideoChatActionButtonComponent: Component {
case .leave:
titleText = component.strings.VoiceChat_Leave
backgroundColor = UIColor(rgb: 0x330d0b)
tintColorKind = .custom
iconDiameter = 22.0
}
@@ -282,7 +284,7 @@ final class VideoChatActionButtonComponent: Component {
}
}
self.background.update(size: size, cornerRadius: size.width * 0.5, isDark: true, tintColor: .init(kind: .custom, color: backgroundColor), transition: tintTransition)
self.background.update(size: size, cornerRadius: size.width * 0.5, isDark: true, tintColor: .init(kind: tintColorKind, color: backgroundColor), transition: tintTransition)
transition.setFrame(view: self.background, frame: CGRect(origin: CGPoint(), size: size))
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) * 0.5), y: size.height + 8.0), size: titleSize)

View File

@@ -34,7 +34,7 @@ private func blurredAvatarImage(_ dataImage: UIImage) -> UIImage? {
}
private let activityBorderImage: UIImage = {
return generateStretchableFilledCircleImage(diameter: 20.0, color: nil, strokeColor: .white, strokeWidth: 2.0)!.withRenderingMode(.alwaysTemplate)
return generateStretchableFilledCircleImage(diameter: 32.0, color: nil, strokeColor: .white, strokeWidth: 2.0)!.withRenderingMode(.alwaysTemplate)
}()
final class VideoChatParticipantVideoComponent: Component {
@@ -227,7 +227,7 @@ final class VideoChatParticipantVideoComponent: Component {
self.pinchContainerNode.contentNode.view.addSubview(self.backgroundGradientView)
//TODO:release optimize
self.pinchContainerNode.contentNode.view.layer.cornerRadius = 10.0
self.pinchContainerNode.contentNode.view.layer.cornerRadius = 16.0
self.pinchContainerNode.contentNode.view.clipsToBounds = true
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
@@ -680,7 +680,7 @@ final class VideoChatParticipantVideoComponent: Component {
if videoDescription != nil && self.videoSpec == nil && !isEffectivelyPaused {
if self.loadingEffectView == nil {
let loadingEffectView = VideoChatVideoLoadingEffectView(effectAlpha: 0.1, borderAlpha: 0.2, cornerRadius: 10.0, duration: 1.0)
let loadingEffectView = VideoChatVideoLoadingEffectView(effectAlpha: 0.1, borderAlpha: 0.2, cornerRadius: 16.0, duration: 1.0)
self.loadingEffectView = loadingEffectView
loadingEffectView.alpha = 0.0
loadingEffectView.isUserInteractionEnabled = false

View File

@@ -485,6 +485,14 @@ final class VideoChatScreenComponent: Component {
if gestureRecognizer is UIPanGestureRecognizer {
if let otherGestureRecognizer = otherGestureRecognizer as? UIPanGestureRecognizer {
if otherGestureRecognizer.view is UIScrollView {
if let view = otherGestureRecognizer.view {
if let inputView = self.inputMediaNode?.view, view.isDescendant(of: inputView) {
return false
}
if let reactionView = self.reactionContextNode?.view, view.isDescendant(of: reactionView) {
return false
}
}
return true
}
if let participantsView = self.participants.view as? VideoChatParticipantsComponent.View {
@@ -2195,7 +2203,7 @@ final class VideoChatScreenComponent: Component {
let landscapeControlsWidth: CGFloat = 104.0
var landscapeControlsOffsetX: CGFloat = 0.0
//let landscapeControlsSpacing: CGFloat = 30.0
let landscapeControlsSpacing: CGFloat = 30.0
var leftInset: CGFloat = max(environment.safeInsets.left, 16.0)
@@ -2222,6 +2230,8 @@ final class VideoChatScreenComponent: Component {
let navigationButtonDiameter: CGFloat = 40.0
let navigationButtonInset: CGFloat = 4.0
let panelColor = UIColor(rgb: 0x1f1f27)
let navigationLeftButtonSize = self.navigationLeftButton.update(
transition: .immediate,
component: AnyComponent(PlainButtonComponent(
@@ -2232,7 +2242,7 @@ final class VideoChatScreenComponent: Component {
color: .white
)),
background: AnyComponent(
GlassBackgroundComponent(size: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter), tintColor: .init(kind: .custom, color: UIColor(rgb: 0x101014)))
GlassBackgroundComponent(size: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter), isDark: false, tintColor: .init(kind: .panel, color: panelColor))
),
effectAlignment: .center,
minSize: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter),
@@ -2254,7 +2264,7 @@ final class VideoChatScreenComponent: Component {
image: closeButtonImage(dark: false)
)),
background: AnyComponent(
GlassBackgroundComponent(size: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter), tintColor: .init(kind: .custom, color: UIColor(rgb: 0x101014)))
GlassBackgroundComponent(size: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter), isDark: false, tintColor: .init(kind: .panel, color: panelColor))
),
effectAlignment: .center,
minSize: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter),
@@ -2646,24 +2656,28 @@ final class VideoChatScreenComponent: Component {
//TODO:release
let actionButtonSize = CGSize(width: actionButtonDiameter, height: actionButtonDiameter)
let firstActionButtonFrame = CGRect(origin: CGPoint(x: microphoneButtonFrame.minX - actionMicrophoneButtonSpacing - actionButtonDiameter, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: actionButtonSize)
let secondActionButtonFrame = CGRect(origin: CGPoint(x: firstActionButtonFrame.minX + 80.0, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: actionButtonSize)
var firstActionButtonFrame = CGRect(origin: CGPoint(x: microphoneButtonFrame.minX - actionMicrophoneButtonSpacing - actionButtonDiameter, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: actionButtonSize)
var secondActionButtonFrame = CGRect(origin: CGPoint(x: firstActionButtonFrame.minX + 80.0, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: actionButtonSize)
let fourthActionButtonFrame = CGRect(origin: CGPoint(x: microphoneButtonFrame.maxX + actionMicrophoneButtonSpacing, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: actionButtonSize)
let thirdActionButtonFrame = CGRect(origin: CGPoint(x: fourthActionButtonFrame.minX - 80.0, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: actionButtonSize)
let _ = firstActionButtonFrame
var fourthActionButtonFrame = CGRect(origin: CGPoint(x: microphoneButtonFrame.maxX + actionMicrophoneButtonSpacing, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: actionButtonSize)
var thirdActionButtonFrame = CGRect(origin: CGPoint(x: fourthActionButtonFrame.minX - 80.0, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: actionButtonSize)
// var leftActionButtonFrame = CGRect(origin: CGPoint(x: microphoneButtonFrame.minX - actionMicrophoneButtonSpacing - actionButtonDiameter, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: CGSize(width: actionButtonDiameter, height: actionButtonDiameter))
// var rightActionButtonFrame = CGRect(origin: CGPoint(x: microphoneButtonFrame.maxX + actionMicrophoneButtonSpacing, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: CGSize(width: actionButtonDiameter, height: actionButtonDiameter))
// if buttonsOnTheSide {
// leftActionButtonFrame.origin.x = microphoneButtonFrame.minX
// leftActionButtonFrame.origin.y = microphoneButtonFrame.minY - landscapeControlsSpacing - actionButtonDiameter
//
// rightActionButtonFrame.origin.x = microphoneButtonFrame.minX
// rightActionButtonFrame.origin.y = microphoneButtonFrame.maxY + landscapeControlsSpacing
// }
if buttonsOnTheSide {
secondActionButtonFrame.origin.x = microphoneButtonFrame.minX
secondActionButtonFrame.origin.y = microphoneButtonFrame.minY - landscapeControlsSpacing - actionButtonDiameter
firstActionButtonFrame.origin.x = microphoneButtonFrame.minX
firstActionButtonFrame.origin.y = secondActionButtonFrame.minY - landscapeControlsSpacing - actionButtonDiameter
thirdActionButtonFrame.origin.x = microphoneButtonFrame.minX
thirdActionButtonFrame.origin.y = microphoneButtonFrame.maxY + landscapeControlsSpacing
fourthActionButtonFrame.origin.x = microphoneButtonFrame.minX
fourthActionButtonFrame.origin.y = thirdActionButtonFrame.maxY + landscapeControlsSpacing
}
let participantsSize = availableSize

View File

@@ -16,6 +16,8 @@ swift_library(
"//submodules/ActivityIndicator",
"//submodules/ShimmerEffect",
"//submodules/Components/BundleIconComponent",
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
],
visibility = [
"//visibility:public",

View File

@@ -6,6 +6,7 @@ import AnimatedTextComponent
import ActivityIndicator
import BundleIconComponent
import ShimmerEffect
import GlassBackgroundComponent
public final class ButtonBadgeComponent: Component {
let fillColor: UIColor
@@ -338,6 +339,12 @@ public final class ButtonTextContentComponent: Component {
public final class ButtonComponent: Component {
public struct Background: Equatable {
public enum Style {
case glass
case legacy
}
public var style: Style
public var color: UIColor
public var foreground: UIColor
public var pressedColor: UIColor
@@ -345,12 +352,14 @@ public final class ButtonComponent: Component {
public var isShimmering: Bool
public init(
style: Style = .legacy,
color: UIColor,
foreground: UIColor,
pressedColor: UIColor,
cornerRadius: CGFloat = 10.0,
isShimmering: Bool = false
) {
self.style = style
self.color = color
self.foreground = foreground
self.pressedColor = pressedColor
@@ -360,6 +369,7 @@ public final class ButtonComponent: Component {
public func withIsShimmering(_ isShimmering: Bool) -> Background {
return Background(
style: self.style,
color: self.color,
foreground: self.foreground,
pressedColor: self.pressedColor,
@@ -431,6 +441,7 @@ public final class ButtonComponent: Component {
private weak var componentState: EmptyComponentState?
private var shimmeringView: ButtonShimmeringView?
private var chromeView: UIImageView?
private var contentItem: ContentItem?
private var activityIndicator: ActivityIndicator?
@@ -475,7 +486,12 @@ public final class ButtonComponent: Component {
self.isEnabled = (component.isEnabled || component.allowActionWhenDisabled) && !component.displaysProgress
transition.setBackgroundColor(view: self, color: component.background.color)
transition.setCornerRadius(layer: self.layer, cornerRadius: component.background.cornerRadius)
var cornerRadius: CGFloat = component.background.cornerRadius
if case .glass = component.background.style, component.background.cornerRadius == 10.0 {
cornerRadius = availableSize.height * 0.5
}
transition.setCornerRadius(layer: self.layer, cornerRadius: cornerRadius)
var contentAlpha: CGFloat = 1.0
if component.displaysProgress {
@@ -581,6 +597,33 @@ public final class ButtonComponent: Component {
})
}
if component.background.style == .glass {
let chromeView: UIImageView
var chromeTransition = transition
if let current = self.chromeView {
chromeView = current
} else {
chromeTransition = .immediate
chromeView = UIImageView()
self.chromeView = chromeView
if let shimmeringView = self.shimmeringView {
self.insertSubview(chromeView, aboveSubview: shimmeringView)
} else {
self.insertSubview(chromeView, at: 0)
}
chromeView.layer.compositingFilter = "overlayBlendMode"
chromeView.alpha = 0.8
chromeView.image = GlassBackgroundView.generateForegroundImage(size: CGSize(width: 26.0 * 2.0, height: 26.0 * 2.0), isDark: component.background.color.lightness < 0.4, fillColor: .clear)
}
chromeTransition.setFrame(view: chromeView, frame: CGRect(origin: .zero, size: availableSize))
} else if let chromeView = self.chromeView {
self.chromeView = nil
chromeView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { _ in
chromeView.removeFromSuperview()
})
}
return availableSize
}
}

View File

@@ -834,7 +834,8 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
file: arguments.parentMessage.associatedMedia[MediaId(
namespace: Namespaces.Media.CloudFile,
id: backgroundEmojiId
)] as? TelegramMediaFile
)] as? TelegramMediaFile,
emptyCorner: giftEmojiFileId != nil
)
}
var isTransparent: Bool = false
@@ -898,16 +899,19 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
}
if let giftEmojiFileId {
let giftLayerSize = CGSize(width: 20.0, height: 20.0)
let giftLayerFrame = CGRect(origin: CGPoint(x: textFrame.minX - 16.0, y: 5.0), size: giftLayerSize)
let giftLayerSize = CGSize(width: 18.0, height: 18.0)
let giftLayerFrame = CGRect(origin: CGPoint(x: realSize.width - giftLayerSize.width - 4.0, y: 3.0), size: giftLayerSize)
let giftEmojiLayer: InlineStickerItemLayer
if let current = node.giftEmojiLayer {
if let current = node.giftEmojiLayer, current.file?.fileId.id == giftEmojiFileId {
giftEmojiLayer = current
animation.animator.updateFrame(layer: giftEmojiLayer, frame: giftLayerFrame, completion: nil)
} else {
giftEmojiLayer = InlineStickerItemLayer(context: arguments.context, userLocation: .other, attemptSynchronousLoad: true, emoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: giftEmojiFileId, file: nil, custom: nil, enableAnimation: true), file: nil, cache: arguments.context.animationCache, renderer: arguments.context.animationRenderer, unique: false, placeholderColor: placeholderColor, pointSize: giftLayerSize, dynamicColor: nil, loopCount: 2)
if let giftEmojiLayer = node.giftEmojiLayer {
node.giftEmojiLayer = nil
giftEmojiLayer.removeFromSuperlayer()
}
giftEmojiLayer = InlineStickerItemLayer(context: arguments.context, userLocation: .other, attemptSynchronousLoad: true, emoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: giftEmojiFileId, file: nil, custom: nil, enableAnimation: true), file: nil, cache: arguments.context.animationCache, renderer: arguments.context.animationRenderer, unique: false, placeholderColor: placeholderColor, pointSize: CGSize(width: giftLayerSize.width * 2.0, height: giftLayerSize.height * 2.0), dynamicColor: nil, loopCount: 2)
node.giftEmojiLayer = giftEmojiLayer
node.contentNode.layer.addSublayer(giftEmojiLayer)

View File

@@ -426,11 +426,13 @@ public final class MessageInlineBlockBackgroundView: UIView {
public let context: AccountContext
public let fileId: Int64
public let file: TelegramMediaFile?
public let emptyCorner: Bool
public init(context: AccountContext, fileId: Int64, file: TelegramMediaFile?) {
public init(context: AccountContext, fileId: Int64, file: TelegramMediaFile?, emptyCorner: Bool = false) {
self.context = context
self.fileId = fileId
self.file = file
self.emptyCorner = emptyCorner
}
public static func ==(lhs: Pattern, rhs: Pattern) -> Bool {
@@ -446,7 +448,9 @@ public final class MessageInlineBlockBackgroundView: UIView {
if lhs.file?.fileId != rhs.file?.fileId {
return false
}
if lhs.emptyCorner != rhs.emptyCorner {
return false
}
return true
}
}
@@ -771,7 +775,12 @@ public final class MessageInlineBlockBackgroundView: UIView {
patternContentLayer.frame = CGRect(origin: CGPoint(x: patternOrigin.x - placement.position.x / 3.0 - itemSize.width * 0.5, y: patternOrigin.y + placement.position.y / 3.0 - itemSize.height * 0.5), size: itemSize)
var alphaFraction = abs(placement.position.x / 3.0) / min(500.0, size.width)
alphaFraction = min(1.0, max(0.0, alphaFraction))
patternContentLayer.opacity = 0.3 * Float(1.0 - alphaFraction) * Float(patternAlpha)
if maxIndex == 1 && params.pattern?.emptyCorner == true {
patternContentLayer.opacity = 0.0
} else {
patternContentLayer.opacity = 0.3 * Float(1.0 - alphaFraction) * Float(patternAlpha)
}
maxIndex += 1
}

View File

@@ -784,10 +784,12 @@ public extension GlassBackgroundView {
public final class GlassBackgroundComponent: Component {
private let size: CGSize
private let isDark: Bool
private let tintColor: GlassBackgroundView.TintColor
public init(size: CGSize, tintColor: GlassBackgroundView.TintColor) {
public init(size: CGSize, isDark: Bool, tintColor: GlassBackgroundView.TintColor) {
self.size = size
self.isDark = isDark
self.tintColor = tintColor
}
@@ -795,6 +797,9 @@ public final class GlassBackgroundComponent: Component {
if lhs.size != rhs.size {
return false
}
if lhs.isDark != rhs.isDark {
return false
}
if lhs.tintColor != rhs.tintColor {
return false
}
@@ -803,7 +808,7 @@ public final class GlassBackgroundComponent: Component {
public final class View: GlassBackgroundView {
func update(component: GlassBackgroundComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
self.update(size: component.size, cornerRadius: component.size.height / 2.0, isDark: true, tintColor: component.tintColor, transition: transition)
self.update(size: component.size, cornerRadius: component.size.height / 2.0, isDark: component.isDark, tintColor: component.tintColor, transition: transition)
self.frame = CGRect(origin: .zero, size: component.size)
return component.size

View File

@@ -8,6 +8,11 @@ import SwitchNode
import CheckNode
public final class ListActionItemComponent: Component {
public enum Style {
case glass
case legacy
}
public enum ToggleStyle {
case regular
case icons
@@ -133,6 +138,7 @@ public final class ListActionItemComponent: Component {
}
public let theme: PresentationTheme
public let style: Style
public let background: AnyComponent<Empty>?
public let title: AnyComponent<Empty>
public let titleAlignment: Alignment
@@ -146,6 +152,7 @@ public final class ListActionItemComponent: Component {
public init(
theme: PresentationTheme,
style: Style = .legacy,
background: AnyComponent<Empty>? = nil,
title: AnyComponent<Empty>,
titleAlignment: Alignment = .default,
@@ -158,6 +165,7 @@ public final class ListActionItemComponent: Component {
updateIsHighlighted: ((UIView, Bool) -> Void)? = nil
) {
self.theme = theme
self.style = style
self.background = background
self.title = title
self.titleAlignment = titleAlignment
@@ -174,6 +182,9 @@ public final class ListActionItemComponent: Component {
if lhs.theme !== rhs.theme {
return false
}
if lhs.style != rhs.style {
return false
}
if lhs.background != rhs.background {
return false
}
@@ -391,8 +402,19 @@ public final class ListActionItemComponent: Component {
contentRightInset = customAccessorySizeValue.width + customAccessory.insets.left + customAccessory.insets.right
}
var contentInsets = component.contentInsets
switch component.style {
case .glass:
if contentInsets.top == 12.0 && contentInsets.bottom == 12.0 {
contentInsets.top = 16.0
contentInsets.bottom = 16.0
}
case .legacy:
break
}
var contentHeight: CGFloat = 0.0
contentHeight += component.contentInsets.top
contentHeight += contentInsets.top
if let leftIcon = component.leftIcon {
switch leftIcon {
@@ -414,11 +436,10 @@ public final class ListActionItemComponent: Component {
contentLeftInset = floor((availableSize.width - titleSize.width) / 2.0)
}
let titleY = contentHeight
contentHeight += titleSize.height
contentHeight += component.contentInsets.bottom
contentHeight += contentInsets.bottom
if let iconValue = component.icon {
if previousComponent?.icon?.component.id != iconValue.component.id, let icon = self.icon {

View File

@@ -41,6 +41,7 @@ public final class ListSectionContentView: UIView {
public final class Configuration {
public let theme: PresentationTheme
public let style: ListSectionComponent.Style
public let isModal: Bool
public let displaySeparators: Bool
public let extendsItemHighlightToSection: Bool
@@ -48,12 +49,14 @@ public final class ListSectionContentView: UIView {
public init(
theme: PresentationTheme,
style: ListSectionComponent.Style = .legacy,
isModal: Bool = false,
displaySeparators: Bool,
extendsItemHighlightToSection: Bool,
background: ListSectionComponent.Background
) {
self.theme = theme
self.style = style
self.isModal = isModal
self.displaySeparators = displaySeparators
self.extendsItemHighlightToSection = extendsItemHighlightToSection
@@ -152,6 +155,14 @@ public final class ListSectionContentView: UIView {
}
self.externalContentBackgroundView.updateColor(color: backgroundColor, transition: transition)
let cornerRadius: CGFloat
switch configuration.style {
case .glass:
cornerRadius = 26.0
case .legacy:
cornerRadius = 11.0
}
var innerContentHeight: CGFloat = 0.0
var validItemIds: [AnyHashable] = []
for index in 0 ..< readyItems.count {
@@ -264,18 +275,18 @@ public final class ListSectionContentView: UIView {
let backgroundFrame: CGRect
var backgroundAlpha: CGFloat = 1.0
var contentCornerRadius: CGFloat = 11.0
var contentCornerRadius: CGFloat = cornerRadius
switch configuration.background {
case let .none(clipped):
backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size)
backgroundAlpha = 0.0
self.externalContentBackgroundView.update(size: backgroundFrame.size, corners: DynamicCornerRadiusView.Corners(minXMinY: 11.0, maxXMinY: 11.0, minXMaxY: 11.0, maxXMaxY: 11.0), transition: transition)
self.externalContentBackgroundView.update(size: backgroundFrame.size, corners: DynamicCornerRadiusView.Corners(minXMinY: cornerRadius, maxXMinY: cornerRadius, minXMaxY: cornerRadius, maxXMaxY: cornerRadius), transition: transition)
if !clipped {
contentCornerRadius = 0.0
}
case .all:
backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size)
self.externalContentBackgroundView.update(size: backgroundFrame.size, corners: DynamicCornerRadiusView.Corners(minXMinY: 11.0, maxXMinY: 11.0, minXMaxY: 11.0, maxXMaxY: 11.0), transition: transition)
self.externalContentBackgroundView.update(size: backgroundFrame.size, corners: DynamicCornerRadiusView.Corners(minXMinY: cornerRadius, maxXMinY: cornerRadius, minXMaxY: cornerRadius, maxXMaxY: cornerRadius), transition: transition)
case let .range(from, corners):
if let itemView = self.itemViews[from], itemView.frame.minY < size.height {
backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: itemView.frame.minY), size: CGSize(width: size.width, height: size.height - itemView.frame.minY))
@@ -306,7 +317,13 @@ public final class ListSectionComponent: Component {
case range(from: AnyHashable, corners: DynamicCornerRadiusView.Corners)
}
public enum Style {
case glass
case legacy
}
public let theme: PresentationTheme
public let style: Style
public let background: Background
public let header: AnyComponent<Empty>?
public let footer: AnyComponent<Empty>?
@@ -317,6 +334,7 @@ public final class ListSectionComponent: Component {
public init(
theme: PresentationTheme,
style: Style = .legacy,
background: Background = .all,
header: AnyComponent<Empty>?,
footer: AnyComponent<Empty>?,
@@ -326,6 +344,7 @@ public final class ListSectionComponent: Component {
extendsItemHighlightToSection: Bool = false
) {
self.theme = theme
self.style = style
self.background = background
self.header = header
self.footer = footer
@@ -339,6 +358,9 @@ public final class ListSectionComponent: Component {
if lhs.theme !== rhs.theme {
return false
}
if lhs.style != rhs.style {
return false
}
if lhs.background != rhs.background {
return false
}
@@ -464,6 +486,7 @@ public final class ListSectionComponent: Component {
let contentResult = self.contentView.update(
configuration: ListSectionContentView.Configuration(
theme: component.theme,
style: component.style,
isModal: component.isModal,
displaySeparators: component.displaySeparators,
extendsItemHighlightToSection: component.extendsItemHighlightToSection,
@@ -638,6 +661,7 @@ public final class ListSubSectionComponent: Component {
let contentResult = self.contentView.update(
configuration: ListSectionContentView.Configuration(
theme: component.theme,
style: .legacy,
isModal: component.isModal,
displaySeparators: component.displaySeparators,
extendsItemHighlightToSection: false,

View File

@@ -54,6 +54,7 @@ swift_library(
"//submodules/TelegramUI/Components/EmojiActionIconComponent",
"//submodules/TelegramUI/Components/TabSelectorComponent",
"//submodules/TelegramUI/Components/PlainButtonComponent",
"//submodules/TextFormat",
],
visibility = [
"//visibility:public",

View File

@@ -33,6 +33,8 @@ import PeerNameColorItem
import EmojiActionIconComponent
import TabSelectorComponent
import WallpaperResources
import EdgeEffect
import TextFormat
private let giftListTag = GenericComponentViewTag()
@@ -143,8 +145,7 @@ final class UserAppearanceScreenComponent: Component {
private let topOverscrollLayer = SimpleLayer()
private let scrollView: ScrollView
private let actionButton = ComponentView<Empty>()
private let bottomPanelBackgroundView: BlurredBackgroundView
private let bottomPanelSeparator: SimpleLayer
private let edgeEffectView: EdgeEffectView
private let backButton = PeerInfoHeaderNavigationButton()
@@ -155,12 +156,13 @@ final class UserAppearanceScreenComponent: Component {
}
private var currentSection: Section = .profile
private let previewSection = ComponentView<Empty>()
private let boostSection = ComponentView<Empty>()
private let bannerSection = ComponentView<Empty>()
private let replySection = ComponentView<Empty>()
private let resetColorSection = ComponentView<Empty>()
private let profilePreview = ComponentView<Empty>()
private let profileColorSection = ComponentView<Empty>()
private let profileResetColorSection = ComponentView<Empty>()
private let profileGiftsSection = ComponentView<Empty>()
private let namePreview = ComponentView<Empty>()
private let nameColorSection = ComponentView<Empty>()
private let nameGiftsSection = ComponentView<Empty>()
private var isUpdating: Bool = false
@@ -195,6 +197,8 @@ final class UserAppearanceScreenComponent: Component {
private weak var emojiStatusSelectionController: ViewController?
private var cachedChevronImage: (UIImage, PresentationTheme)?
override init(frame: CGRect) {
self.scrollView = ScrollView()
self.scrollView.showsVerticalScrollIndicator = false
@@ -208,8 +212,7 @@ final class UserAppearanceScreenComponent: Component {
}
self.scrollView.alwaysBounceVertical = true
self.bottomPanelBackgroundView = BlurredBackgroundView(color: .clear, enableBlur: true)
self.bottomPanelSeparator = SimpleLayer()
self.edgeEffectView = EdgeEffectView()
super.init(frame: frame)
@@ -218,8 +221,7 @@ final class UserAppearanceScreenComponent: Component {
self.scrollView.layer.addSublayer(self.topOverscrollLayer)
self.addSubview(self.bottomPanelBackgroundView)
self.layer.addSublayer(self.bottomPanelSeparator)
self.addSubview(self.edgeEffectView)
self.backButton.action = { [weak self] _, _ in
if let self, let controller = self.environment?.controller() {
@@ -297,16 +299,10 @@ final class UserAppearanceScreenComponent: Component {
if self.scrolledUp != scrolledUp {
self.scrolledUp = scrolledUp
if !self.isUpdating {
self.state?.updated()
self.state?.updated(transition: .easeInOut(duration: 0.3))
}
}
let bottomNavigationAlphaDistance: CGFloat = 16.0
let bottomNavigationAlpha: CGFloat = max(0.0, min(1.0, (self.scrollView.contentSize.height - self.scrollView.bounds.maxY) / bottomNavigationAlphaDistance))
transition.setAlpha(view: self.bottomPanelBackgroundView, alpha: bottomNavigationAlpha)
transition.setAlpha(layer: self.bottomPanelSeparator, alpha: bottomNavigationAlpha)
switch self.currentSection {
case .profile:
if let giftListView = self.profileGiftsSection.findTaggedView(tag: giftListTag) as? GiftListItemComponent.View {
@@ -600,6 +596,9 @@ final class UserAppearanceScreenComponent: Component {
}
switch subject {
case .reply:
if case .collectible = resolvedState.nameColor {
self.updatedPeerNameColor = .preset(.blue)
}
if let result {
self.updatedPeerNameEmoji = result.fileId.id
} else {
@@ -681,6 +680,10 @@ final class UserAppearanceScreenComponent: Component {
self.backgroundColor = environment.theme.list.blocksBackgroundColor
}
if self.cachedChevronImage == nil || self.cachedChevronImage?.1 !== environment.theme {
self.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: environment.theme.list.itemAccentColor)!, environment.theme)
}
if self.contentsDataDisposable == nil {
self.contentsDataDisposable = (ContentsData.get(context: component.context)
|> deliverOnMainQueue).start(next: { [weak self] contentsData in
@@ -804,7 +807,7 @@ final class UserAppearanceScreenComponent: Component {
}
if let intValue = value.base as? Int32 {
self.currentSection = Section(rawValue: intValue) ?? .profile
self.state?.updated(transition: .immediate)
self.state?.updated(transition: .easeInOut(duration: 0.3))
}
}
)
@@ -829,14 +832,20 @@ final class UserAppearanceScreenComponent: Component {
var contentHeight: CGFloat = 0.0
let sectionTransition = transition
let itemCornerRadius: CGFloat = 10.0
let itemCornerRadius: CGFloat = 26.0
switch self.currentSection {
case .profile:
if let replySectionView = self.replySection.view, replySectionView.superview != nil {
replySectionView.removeFromSuperview()
var transition = transition
if self.profilePreview.view == nil {
transition = .immediate
}
if let namePreviewView = self.namePreview.view, namePreviewView.superview != nil {
namePreviewView.removeFromSuperview()
}
if let nameColorSectionView = self.nameColorSection.view, nameColorSectionView.superview != nil {
nameColorSectionView.removeFromSuperview()
}
if let nameGiftsSectionView = self.nameGiftsSection.view, nameGiftsSectionView.superview != nil {
nameGiftsSectionView.removeFromSuperview()
@@ -850,46 +859,35 @@ final class UserAppearanceScreenComponent: Component {
hasHeaderColor = true
}
let previewSectionSize = self.previewSection.update(
transition: sectionTransition,
component: AnyComponent(ListSectionComponent(
theme: environment.theme,
background: .none(clipped: false),
header: nil,
footer: nil,
items: [
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor(
itemGenerator: PeerNameColorProfilePreviewItem(
context: component.context,
theme: environment.theme,
componentTheme: environment.theme,
strings: environment.strings,
topInset: 0.0,
sectionId: 0,
peer: peer,
subtitleString: environment.strings.Presence_online,
files: self.cachedIconFiles,
nameDisplayOrder: presentationData.nameDisplayOrder,
showBackground: false
),
params: ListViewItemLayoutParams(width: availableSize.width - sideInset * 2.0, leftInset: 0.0, rightInset: 0.0, availableHeight: 10000.0, isStandalone: true)
))),
],
displaySeparators: false,
extendsItemHighlightToSection: true
)),
let profilePreviewSize = self.profilePreview.update(
transition: transition,
component: AnyComponent(TopBottomCornersComponent(topCornerRadius: itemCornerRadius, bottomCornerRadius: !self.scrolledUp ? itemCornerRadius : 0.0, component: AnyComponent(ListItemComponentAdaptor(
itemGenerator: PeerNameColorProfilePreviewItem(
context: component.context,
theme: environment.theme,
componentTheme: environment.theme,
strings: environment.strings,
topInset: 0.0,
sectionId: 0,
peer: peer,
subtitleString: environment.strings.Presence_online,
files: self.cachedIconFiles,
nameDisplayOrder: presentationData.nameDisplayOrder,
showBackground: false
),
params: ListViewItemLayoutParams(width: availableSize.width - sideInset * 2.0, leftInset: 0.0, rightInset: 0.0, availableHeight: 10000.0, isStandalone: true)
)))),
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0)
)
let previewSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: environment.navigationHeight + 12.0), size: previewSectionSize)
if let previewSectionView = self.previewSection.view {
if previewSectionView.superview == nil {
self.addSubview(previewSectionView)
let profilePreviewFrame = CGRect(origin: CGPoint(x: sideInset, y: environment.navigationHeight + 12.0), size: profilePreviewSize)
if let profilePreviewView = self.profilePreview.view {
if profilePreviewView.superview == nil {
self.addSubview(profilePreviewView)
}
sectionTransition.setFrame(view: previewSectionView, frame: previewSectionFrame)
transition.setFrame(view: profilePreviewView, frame: profilePreviewFrame)
}
contentHeight += previewSectionSize.height
contentHeight += environment.navigationHeight + 12.0
contentHeight += profilePreviewSize.height - 18.0
var profileLogoContents: [AnyComponentWithIdentity<Empty>] = []
profileLogoContents.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
@@ -900,15 +898,50 @@ final class UserAppearanceScreenComponent: Component {
)),
maximumNumberOfLines: 0
))))
let bannerSectionSize = self.bannerSection.update(
transition: sectionTransition,
//TODO:localize
let footerAttributes = MarkdownAttributes(
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor),
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: environment.theme.list.freeTextColor),
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.itemAccentColor),
linkAttribute: { contents in
return (TelegramTextAttributes.URL, contents)
}
)
let previewFooterText = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString("You can also change color of your name and customize replies to you. [Change >]()", attributes: footerAttributes))
if let range = previewFooterText.string.range(of: ">"), let chevronImage = self.cachedChevronImage?.0 {
previewFooterText.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: previewFooterText.string))
}
let profileColorSectionSize = self.profileColorSection.update(
transition: transition,
component: AnyComponent(ListSectionComponent(
theme: environment.theme,
background: .range(from: 1, corners: DynamicCornerRadiusView.Corners(minXMinY: !hasHeaderColor ? itemCornerRadius : 0.0, maxXMinY: !hasHeaderColor ? itemCornerRadius : 0.0, minXMaxY: itemCornerRadius, maxXMaxY: itemCornerRadius)),
style: .glass,
background: .range(from: 0, corners: DynamicCornerRadiusView.Corners(minXMinY: !hasHeaderColor ? itemCornerRadius : 0.0, maxXMinY: !hasHeaderColor ? itemCornerRadius : 0.0, minXMaxY: itemCornerRadius, maxXMaxY: itemCornerRadius)),
header: nil,
footer: nil,
footer: AnyComponent(MultilineTextComponent(
text: .plain(previewFooterText),
maximumNumberOfLines: 0,
highlightColor: environment.theme.list.itemAccentColor.withAlphaComponent(0.1),
highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0),
highlightAction: { attributes in
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
} else {
return nil
}
},
tapAction: { [weak self] _, _ in
guard let self else {
return
}
self.currentSection = .name
self.state?.updated(transition: .easeInOut(duration: 0.3))
}
)),
items: [
AnyComponentWithIdentity(id: 1, component: AnyComponent(ListItemComponentAdaptor(
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor(
itemGenerator: PeerNameColorItem(
theme: environment.theme,
colors: component.context.peerNameColors,
@@ -928,8 +961,9 @@ final class UserAppearanceScreenComponent: Component {
),
params: listItemParams
))),
AnyComponentWithIdentity(id: 2, component: AnyComponent(ListActionItemComponent(
AnyComponentWithIdentity(id: 1, component: AnyComponent(ListActionItemComponent(
theme: environment.theme,
style: .glass,
title: AnyComponent(HStack(profileLogoContents, spacing: 6.0)),
icon: ListActionItemComponent.Icon(component: AnyComponentWithIdentity(id: 0, component: AnyComponent(EmojiActionIconComponent(
context: component.context,
@@ -956,25 +990,27 @@ final class UserAppearanceScreenComponent: Component {
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0)
)
let bannerSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: bannerSectionSize)
if let bannerSectionView = self.bannerSection.view {
if bannerSectionView.superview == nil {
self.scrollView.addSubview(bannerSectionView)
let profileColorSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: profileColorSectionSize)
if let profileColorSectionView = self.profileColorSection.view {
if profileColorSectionView.superview == nil {
self.scrollView.addSubview(profileColorSectionView)
}
sectionTransition.setFrame(view: bannerSectionView, frame: bannerSectionFrame)
transition.setFrame(view: profileColorSectionView, frame: profileColorSectionFrame)
}
contentHeight += bannerSectionSize.height
contentHeight += profileColorSectionSize.height
contentHeight += sectionSpacing
let resetColorSectionSize = self.resetColorSection.update(
transition: sectionTransition,
let profileResetColorSectionSize = self.profileResetColorSection.update(
transition: transition,
component: AnyComponent(ListSectionComponent(
theme: environment.theme,
style: .glass,
header: nil,
footer: nil,
items: [
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListActionItemComponent(
theme: environment.theme,
style: .glass,
title: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(
string: environment.strings.Channel_Appearance_ResetProfileColor,
@@ -1011,18 +1047,18 @@ final class UserAppearanceScreenComponent: Component {
displayResetProfileColor = true
}
let resetColorSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: resetColorSectionSize)
if let resetColorSectionView = self.resetColorSection.view {
if resetColorSectionView.superview == nil {
self.scrollView.addSubview(resetColorSectionView)
let profileResetColorSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: profileResetColorSectionSize)
if let profileResetColorSectionView = self.profileResetColorSection.view {
if profileResetColorSectionView.superview == nil {
self.scrollView.addSubview(profileResetColorSectionView)
}
sectionTransition.setPosition(view: resetColorSectionView, position: resetColorSectionFrame.center)
sectionTransition.setBounds(view: resetColorSectionView, bounds: CGRect(origin: CGPoint(), size: resetColorSectionFrame.size))
sectionTransition.setScale(view: resetColorSectionView, scale: displayResetProfileColor ? 1.0 : 0.001)
sectionTransition.setAlpha(view: resetColorSectionView, alpha: displayResetProfileColor ? 1.0 : 0.0)
transition.setPosition(view: profileResetColorSectionView, position: profileResetColorSectionFrame.center)
transition.setBounds(view: profileResetColorSectionView, bounds: CGRect(origin: CGPoint(), size: profileResetColorSectionFrame.size))
transition.setScale(view: profileResetColorSectionView, scale: displayResetProfileColor ? 1.0 : 0.001)
transition.setAlpha(view: profileResetColorSectionView, alpha: displayResetProfileColor ? 1.0 : 0.0)
}
if displayResetProfileColor {
contentHeight += resetColorSectionSize.height
contentHeight += profileResetColorSectionSize.height
contentHeight += sectionSpacing
}
@@ -1032,9 +1068,10 @@ final class UserAppearanceScreenComponent: Component {
selectedGiftId = id
}
let giftsSectionSize = self.profileGiftsSection.update(
transition: sectionTransition,
transition: transition,
component: AnyComponent(ListSectionComponent(
theme: environment.theme,
style: .glass,
header: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(
string: environment.strings.NameColor_GiftTitle,
@@ -1106,19 +1143,24 @@ final class UserAppearanceScreenComponent: Component {
if giftsSectionView.superview == nil {
self.scrollView.addSubview(giftsSectionView)
}
sectionTransition.setFrame(view: giftsSectionView, frame: giftsSectionFrame)
transition.setFrame(view: giftsSectionView, frame: giftsSectionFrame)
}
contentHeight += giftsSectionSize.height
contentHeight += sectionSpacing
}
case .name:
if let previewSectionView = self.previewSection.view, previewSectionView.superview != nil {
previewSectionView.removeFromSuperview()
var transition = transition
if self.namePreview.view == nil {
transition = .immediate
}
if let bannerSectionView = self.bannerSection.view, bannerSectionView.superview != nil {
bannerSectionView.removeFromSuperview()
if let profilePreviewView = self.profilePreview.view, profilePreviewView.superview != nil {
profilePreviewView.removeFromSuperview()
}
if let resetColorSectionView = self.resetColorSection.view, resetColorSectionView.superview != nil {
if let profileColorSectionView = self.profileColorSection.view, profileColorSectionView.superview != nil {
profileColorSectionView.removeFromSuperview()
}
if let resetColorSectionView = self.profileResetColorSection.view, resetColorSectionView.superview != nil {
resetColorSectionView.removeFromSuperview()
}
if let profileGiftsSectionView = self.profileGiftsSection.view, profileGiftsSectionView.superview != nil {
@@ -1165,10 +1207,42 @@ final class UserAppearanceScreenComponent: Component {
replyColor = collectibleColor.mainColor(dark: environment.theme.overallDarkAppearance)
}
let replySectionSize = self.replySection.update(
transition: sectionTransition,
let namePreviewSize = self.namePreview.update(
transition: transition,
component: AnyComponent(TopBottomCornersComponent(topCornerRadius: itemCornerRadius, bottomCornerRadius: !self.scrolledUp ? itemCornerRadius : 0.0, component: AnyComponent(ListItemComponentAdaptor(
itemGenerator: PeerNameColorChatPreviewItem(
context: component.context,
theme: chatPreviewTheme,
componentTheme: chatPreviewTheme,
strings: environment.strings,
sectionId: 0,
fontSize: presentationData.chatFontSize,
chatBubbleCorners: presentationData.chatBubbleCorners,
wallpaper: chatPreviewWallpaper,
dateTimeFormat: environment.dateTimeFormat,
nameDisplayOrder: presentationData.nameDisplayOrder,
messageItems: [messageItem]
),
params: ListViewItemLayoutParams(width: availableSize.width - sideInset * 2.0, leftInset: 0.0, rightInset: 0.0, availableHeight: 10000.0, isStandalone: true)
)))),
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0)
)
let namePreviewFrame = CGRect(origin: CGPoint(x: sideInset, y: environment.navigationHeight + 12.0), size: namePreviewSize)
if let namePreviewView = self.namePreview.view {
if namePreviewView.superview == nil {
self.addSubview(namePreviewView)
}
transition.setFrame(view: namePreviewView, frame: namePreviewFrame)
}
contentHeight += namePreviewSize.height - 18.0
let nameColorSectionSize = self.nameColorSection.update(
transition: transition,
component: AnyComponent(ListSectionComponent(
theme: environment.theme,
style: .glass,
background: .range(from: 0, corners: DynamicCornerRadiusView.Corners(minXMinY: 0.0, maxXMinY: 0.0, minXMaxY: itemCornerRadius, maxXMaxY: itemCornerRadius)),
header: nil,
footer: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(
@@ -1180,31 +1254,18 @@ final class UserAppearanceScreenComponent: Component {
)),
items: [
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor(
itemGenerator: PeerNameColorChatPreviewItem(
context: component.context,
theme: chatPreviewTheme,
componentTheme: chatPreviewTheme,
strings: environment.strings,
sectionId: 0,
fontSize: presentationData.chatFontSize,
chatBubbleCorners: presentationData.chatBubbleCorners,
wallpaper: chatPreviewWallpaper,
dateTimeFormat: environment.dateTimeFormat,
nameDisplayOrder: presentationData.nameDisplayOrder,
messageItems: [messageItem]
),
params: listItemParams
))),
AnyComponentWithIdentity(id: 1, component: AnyComponent(ListItemComponentAdaptor(
itemGenerator: PeerNameColorItem(
theme: environment.theme,
colors: component.context.peerNameColors,
mode: .name,
currentColor: resolvedState.nameColor.nameColor,
updated: { [weak self] value in
guard let self, let value else {
guard let self, let resolvedState = self.resolveState(), let value else {
return
}
if case .collectible = resolvedState.nameColor {
self.updatedPeerNameEmoji = .some(nil)
}
self.updatedPeerNameColor = .preset(value)
self.state?.updated(transition: .spring(duration: 0.4))
},
@@ -1212,8 +1273,9 @@ final class UserAppearanceScreenComponent: Component {
),
params: listItemParams
))),
AnyComponentWithIdentity(id: 2, component: AnyComponent(ListActionItemComponent(
AnyComponentWithIdentity(id: 1, component: AnyComponent(ListActionItemComponent(
theme: environment.theme,
style: .glass,
title: AnyComponent(HStack(replyLogoContents, spacing: 6.0)),
icon: ListActionItemComponent.Icon(component: AnyComponentWithIdentity(id: 0, component: AnyComponent(EmojiActionIconComponent(
context: component.context,
@@ -1236,14 +1298,14 @@ final class UserAppearanceScreenComponent: Component {
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0)
)
let replySectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: replySectionSize)
if let replySectionView = self.replySection.view {
if replySectionView.superview == nil {
self.scrollView.addSubview(replySectionView)
let nameColorSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: nameColorSectionSize)
if let nameColorSectionView = self.nameColorSection.view {
if nameColorSectionView.superview == nil {
self.scrollView.addSubview(nameColorSectionView)
}
sectionTransition.setFrame(view: replySectionView, frame: replySectionFrame)
transition.setFrame(view: nameColorSectionView, frame: nameColorSectionFrame)
}
contentHeight += replySectionSize.height
contentHeight += nameColorSectionSize.height
contentHeight += sectionSpacing
if !self.starGifts.isEmpty {
@@ -1252,9 +1314,10 @@ final class UserAppearanceScreenComponent: Component {
selectedGiftId = collectibleColor.collectibleId
}
let giftsSectionSize = self.nameGiftsSection.update(
transition: sectionTransition,
transition: transition,
component: AnyComponent(ListSectionComponent(
theme: environment.theme,
style: .glass,
header: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(
string: environment.strings.NameColor_GiftTitle,
@@ -1284,6 +1347,7 @@ final class UserAppearanceScreenComponent: Component {
}
self.updatedPeerNameColor = .collectible(peerColor)
self.updatedPeerNameEmoji = peerColor.backgroundEmojiId
self.state?.updated(transition: .spring(duration: 0.4))
},
tag: giftListTag
@@ -1300,7 +1364,7 @@ final class UserAppearanceScreenComponent: Component {
if giftsSectionView.superview == nil {
self.scrollView.addSubview(giftsSectionView)
}
sectionTransition.setFrame(view: giftsSectionView, frame: giftsSectionFrame)
transition.setFrame(view: giftsSectionView, frame: giftsSectionFrame)
}
contentHeight += giftsSectionSize.height
contentHeight += sectionSpacing
@@ -1309,10 +1373,12 @@ final class UserAppearanceScreenComponent: Component {
contentHeight += bottomContentInset
var buttonTitle = environment.strings.Channel_Appearance_ApplyButton
if let emojiStatus = resolvedState.emojiStatus, case .starGift = emojiStatus.content, resolvedState.changes.contains(.emojiStatus) {
buttonTitle = environment.strings.NameColor_WearCollectible
}
//TODO:localize
let buttonSideInset: CGFloat = 36.0
let buttonTitle = "Apply Style" // environment.strings.Channel_Appearance_ApplyButton
// if let emojiStatus = resolvedState.emojiStatus, case .starGift = emojiStatus.content, resolvedState.changes.contains(.emojiStatus) {
// buttonTitle = environment.strings.NameColor_WearCollectible
// }
var buttonContents: [AnyComponentWithIdentity<Empty>] = []
buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(buttonTitle), component: AnyComponent(
@@ -1323,6 +1389,7 @@ final class UserAppearanceScreenComponent: Component {
transition: transition,
component: AnyComponent(ButtonComponent(
background: ButtonComponent.Background(
style: .glass,
color: environment.theme.list.itemCheckColors.fillColor,
foreground: environment.theme.list.itemCheckColors.foregroundColor,
pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8)
@@ -1341,7 +1408,7 @@ final class UserAppearanceScreenComponent: Component {
}
)),
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 50.0)
containerSize: CGSize(width: availableSize.width - buttonSideInset * 2.0, height: 52.0)
)
contentHeight += buttonSize.height
@@ -1350,7 +1417,7 @@ final class UserAppearanceScreenComponent: Component {
let buttonY = availableSize.height - bottomInset - environment.safeInsets.bottom - buttonSize.height
let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: buttonY), size: buttonSize)
let buttonFrame = CGRect(origin: CGPoint(x: buttonSideInset, y: buttonY), size: buttonSize)
if let buttonView = self.actionButton.view {
if buttonView.superview == nil {
self.addSubview(buttonView)
@@ -1359,26 +1426,24 @@ final class UserAppearanceScreenComponent: Component {
transition.setAlpha(view: buttonView, alpha: 1.0)
}
let bottomPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: buttonY - 8.0), size: CGSize(width: availableSize.width, height: availableSize.height - buttonY + 8.0))
transition.setFrame(view: self.bottomPanelBackgroundView, frame: bottomPanelFrame)
self.bottomPanelBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
self.bottomPanelBackgroundView.update(size: bottomPanelFrame.size, transition: transition.containedViewLayoutTransition)
self.bottomPanelSeparator.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
transition.setFrame(layer: self.bottomPanelSeparator, frame: CGRect(origin: CGPoint(x: bottomPanelFrame.minX, y: bottomPanelFrame.minY), size: CGSize(width: bottomPanelFrame.width, height: UIScreenPixel)))
let edgeEffectHeight: CGFloat = availableSize.height - buttonY + 8.0
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - edgeEffectHeight), size: CGSize(width: availableSize.width, height: edgeEffectHeight))
transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
self.edgeEffectView.update(content: environment.theme.list.blocksBackgroundColor, isInverted: false, rect: edgeEffectFrame, edge: .bottom, edgeSize: edgeEffectFrame.height, containerSize: availableSize, transition: transition)
let previousBounds = self.scrollView.bounds
let contentSize = CGSize(width: availableSize.width, height: contentHeight)
if self.scrollView.frame != CGRect(origin: CGPoint(), size: availableSize) {
self.scrollView.frame = CGRect(origin: CGPoint(), size: availableSize)
let scrollViewFrame = CGRect(origin: CGPoint(x: 0.0, y: environment.navigationHeight + 30.0), size: CGSize(width: availableSize.width, height: availableSize.height - environment.navigationHeight - 30.0))
if self.scrollView.frame != scrollViewFrame {
self.scrollView.frame = scrollViewFrame
}
if self.scrollView.contentSize != contentSize {
self.scrollView.contentSize = contentSize
}
let scrollInsets = UIEdgeInsets(top: environment.navigationHeight, left: 0.0, bottom: availableSize.height - bottomPanelFrame.minY, right: 0.0)
if self.scrollView.verticalScrollIndicatorInsets != scrollInsets {
self.scrollView.verticalScrollIndicatorInsets = scrollInsets
}
// let scrollInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: availableSize.height - bottomPanelFrame.minY, right: 0.0)
// if self.scrollView.verticalScrollIndicatorInsets != scrollInsets {
// self.scrollView.verticalScrollIndicatorInsets = scrollInsets
// }
if !previousBounds.isEmpty, !transition.animation.isImmediate {
let bounds = self.scrollView.bounds
@@ -1388,7 +1453,8 @@ final class UserAppearanceScreenComponent: Component {
}
}
self.topOverscrollLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: -3000.0), size: CGSize(width: availableSize.width, height: 3000.0))
self.topOverscrollLayer.backgroundColor = environment.theme.list.itemBlocksBackgroundColor.cgColor
self.topOverscrollLayer.frame = CGRect(origin: CGPoint(x: sideInset, y: -1000.0), size: CGSize(width: availableSize.width - sideInset * 2.0, height: 1315.0))
self.updateScrolling(transition: transition)
@@ -1397,7 +1463,7 @@ final class UserAppearanceScreenComponent: Component {
size: availableSize,
metrics: environment.metrics,
deviceMetrics: environment.deviceMetrics,
intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomPanelFrame.height, right: 0.0),
intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: edgeEffectHeight, right: 0.0),
safeInsets: UIEdgeInsets(top: 0.0, left: environment.safeInsets.left, bottom: 0.0, right: environment.safeInsets.right),
additionalInsets: .zero,
statusBarHeight: environment.statusBarHeight,
@@ -1494,3 +1560,79 @@ private extension PeerColor {
}
}
}
final class TopBottomCornersComponent: Component {
private let topCornerRadius: CGFloat
private let bottomCornerRadius: CGFloat
private let component: AnyComponent<Empty>
public init(
topCornerRadius: CGFloat,
bottomCornerRadius: CGFloat,
component: AnyComponent<Empty>
) {
self.topCornerRadius = topCornerRadius
self.bottomCornerRadius = bottomCornerRadius
self.component = component
}
public static func == (lhs: TopBottomCornersComponent, rhs: TopBottomCornersComponent) -> Bool {
if lhs.topCornerRadius != rhs.topCornerRadius {
return false
}
if lhs.bottomCornerRadius != rhs.bottomCornerRadius {
return false
}
if lhs.component != rhs.component {
return false
}
return true
}
public final class View: UIView {
private let containerView: UIView
private let hostView: ComponentHostView<Empty>
public override init(frame: CGRect) {
self.containerView = UIView()
self.hostView = ComponentHostView<Empty>()
super.init(frame: frame)
self.clipsToBounds = true
self.containerView.clipsToBounds = true
self.addSubview(self.containerView)
self.containerView.addSubview(self.hostView)
}
public required init?(coder: NSCoder) {
preconditionFailure()
}
func update(component: TopBottomCornersComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
let size = self.hostView.update(
transition: transition,
component: component.component,
environment: {},
containerSize: availableSize
)
transition.setCornerRadius(layer: self.containerView.layer, cornerRadius: component.topCornerRadius)
transition.setCornerRadius(layer: self.layer, cornerRadius: component.bottomCornerRadius)
transition.setFrame(view: self.containerView, frame: CGRect(origin: .zero, size: CGSize(width: availableSize.width, height: availableSize.height + component.bottomCornerRadius)))
return size
}
}
public func makeView() -> View {
return View()
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}

View File

@@ -590,6 +590,7 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent {
let flipButtonBackground = flipButtonBackground.update(
component: GlassBackgroundComponent(
size: CGSize(width: 40.0, height: 40.0),
isDark: true,
tintColor: .init(kind: .panel, color: environment.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7))
),
availableSize: CGSize(width: 40.0, height: 40.0),
@@ -691,6 +692,7 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent {
let flashButtonBackground = flashButtonBackground.update(
component: GlassBackgroundComponent(
size: CGSize(width: 40.0, height: 40.0),
isDark: true,
tintColor: .init(kind: .panel, color: environment.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7))
),
availableSize: CGSize(width: 40.0, height: 40.0),
@@ -764,6 +766,7 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent {
id: "background",
component: AnyComponent(GlassBackgroundComponent(
size: CGSize(width: 40.0, height: 40.0),
isDark: true,
tintColor: .init(kind: .panel, color: environment.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7))
))
),