Merge commit 'a91658373baee67ebcef073a68b21b87787ca6eb'

This commit is contained in:
Isaac 2025-09-26 00:14:28 +08:00
commit 73b67e9a4e
11 changed files with 199 additions and 130 deletions

View File

@ -217,9 +217,9 @@ public enum DeviceMetrics: CaseIterable, Equatable {
case .iPhone14Pro, .iPhone14ProMax:
return 55.0
case .iPhone16Pro, .iPhone16ProMax:
return 55.0
return 62.0
case .iPhoneAir:
return 55.0
return 62.0
case let .unknown(_, _, _, screenCornerRadius):
return screenCornerRadius
default:

View File

@ -965,7 +965,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) }
dict[-963180333] = { return Api.SponsoredPeer.parse_sponsoredPeer($0) }
dict[-2136190013] = { return Api.StarGift.parse_starGift($0) }
dict[973640632] = { return Api.StarGift.parse_starGiftUnique($0) }
dict[-1329630181] = { return Api.StarGift.parse_starGiftUnique($0) }
dict[-650279524] = { return Api.StarGiftAttribute.parse_starGiftAttributeBackdrop($0) }
dict[970559507] = { return Api.StarGiftAttribute.parse_starGiftAttributeModel($0) }
dict[-524291476] = { return Api.StarGiftAttribute.parse_starGiftAttributeOriginalDetails($0) }

View File

@ -289,7 +289,7 @@ public extension Api {
public extension Api {
enum StarGift: TypeConstructorDescription {
case starGift(flags: Int32, id: Int64, sticker: Api.Document, stars: Int64, availabilityRemains: Int32?, availabilityTotal: Int32?, availabilityResale: Int64?, convertStars: Int64, firstSaleDate: Int32?, lastSaleDate: Int32?, upgradeStars: Int64?, resellMinStars: Int64?, title: String?, releasedBy: Api.Peer?, perUserTotal: Int32?, perUserRemains: Int32?, lockedUntilDate: Int32?)
case starGiftUnique(flags: Int32, id: Int64, giftId: Int64, title: String, slug: String, num: Int32, ownerId: Api.Peer?, ownerName: String?, ownerAddress: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32, giftAddress: String?, resellAmount: [Api.StarsAmount]?, releasedBy: Api.Peer?, valueAmount: Int64?, valueCurrency: String?, themePeer: Api.Peer?, peerColor: Api.PeerColor?)
case starGiftUnique(flags: Int32, id: Int64, giftId: Int64, title: String, slug: String, num: Int32, ownerId: Api.Peer?, ownerName: String?, ownerAddress: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32, giftAddress: String?, resellAmount: [Api.StarsAmount]?, releasedBy: Api.Peer?, valueAmount: Int64?, valueCurrency: String?, themePeer: Api.Peer?, peerColor: Api.PeerColor?, hostId: Api.Peer?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -315,9 +315,9 @@ public extension Api {
if Int(flags) & Int(1 << 8) != 0 {serializeInt32(perUserRemains!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 9) != 0 {serializeInt32(lockedUntilDate!, buffer: buffer, boxed: false)}
break
case .starGiftUnique(let flags, let id, let giftId, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy, let valueAmount, let valueCurrency, let themePeer, let peerColor):
case .starGiftUnique(let flags, let id, let giftId, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy, let valueAmount, let valueCurrency, let themePeer, let peerColor, let hostId):
if boxed {
buffer.appendInt32(973640632)
buffer.appendInt32(-1329630181)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(id, buffer: buffer, boxed: false)
@ -346,6 +346,7 @@ public extension Api {
if Int(flags) & Int(1 << 8) != 0 {serializeString(valueCurrency!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 10) != 0 {themePeer!.serialize(buffer, true)}
if Int(flags) & Int(1 << 11) != 0 {peerColor!.serialize(buffer, true)}
if Int(flags) & Int(1 << 12) != 0 {hostId!.serialize(buffer, true)}
break
}
}
@ -354,8 +355,8 @@ public extension Api {
switch self {
case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let availabilityResale, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars, let resellMinStars, let title, let releasedBy, let perUserTotal, let perUserRemains, let lockedUntilDate):
return ("starGift", [("flags", flags as Any), ("id", id as Any), ("sticker", sticker as Any), ("stars", stars as Any), ("availabilityRemains", availabilityRemains as Any), ("availabilityTotal", availabilityTotal as Any), ("availabilityResale", availabilityResale as Any), ("convertStars", convertStars as Any), ("firstSaleDate", firstSaleDate as Any), ("lastSaleDate", lastSaleDate as Any), ("upgradeStars", upgradeStars as Any), ("resellMinStars", resellMinStars as Any), ("title", title as Any), ("releasedBy", releasedBy as Any), ("perUserTotal", perUserTotal as Any), ("perUserRemains", perUserRemains as Any), ("lockedUntilDate", lockedUntilDate as Any)])
case .starGiftUnique(let flags, let id, let giftId, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy, let valueAmount, let valueCurrency, let themePeer, let peerColor):
return ("starGiftUnique", [("flags", flags as Any), ("id", id as Any), ("giftId", giftId as Any), ("title", title as Any), ("slug", slug as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("ownerName", ownerName as Any), ("ownerAddress", ownerAddress as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any), ("giftAddress", giftAddress as Any), ("resellAmount", resellAmount as Any), ("releasedBy", releasedBy as Any), ("valueAmount", valueAmount as Any), ("valueCurrency", valueCurrency as Any), ("themePeer", themePeer as Any), ("peerColor", peerColor as Any)])
case .starGiftUnique(let flags, let id, let giftId, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy, let valueAmount, let valueCurrency, let themePeer, let peerColor, let hostId):
return ("starGiftUnique", [("flags", flags as Any), ("id", id as Any), ("giftId", giftId as Any), ("title", title as Any), ("slug", slug as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("ownerName", ownerName as Any), ("ownerAddress", ownerAddress as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any), ("giftAddress", giftAddress as Any), ("resellAmount", resellAmount as Any), ("releasedBy", releasedBy as Any), ("valueAmount", valueAmount as Any), ("valueCurrency", valueCurrency as Any), ("themePeer", themePeer as Any), ("peerColor", peerColor as Any), ("hostId", hostId as Any)])
}
}
@ -473,6 +474,10 @@ public extension Api {
if Int(_1!) & Int(1 << 11) != 0 {if let signature = reader.readInt32() {
_19 = Api.parse(reader, signature: signature) as? Api.PeerColor
} }
var _20: Api.Peer?
if Int(_1!) & Int(1 << 12) != 0 {if let signature = reader.readInt32() {
_20 = Api.parse(reader, signature: signature) as? Api.Peer
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
@ -492,8 +497,9 @@ public extension Api {
let _c17 = (Int(_1!) & Int(1 << 8) == 0) || _17 != nil
let _c18 = (Int(_1!) & Int(1 << 10) == 0) || _18 != nil
let _c19 = (Int(_1!) & Int(1 << 11) == 0) || _19 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 {
return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, giftId: _3!, title: _4!, slug: _5!, num: _6!, ownerId: _7, ownerName: _8, ownerAddress: _9, attributes: _10!, availabilityIssued: _11!, availabilityTotal: _12!, giftAddress: _13, resellAmount: _14, releasedBy: _15, valueAmount: _16, valueCurrency: _17, themePeer: _18, peerColor: _19)
let _c20 = (Int(_1!) & Int(1 << 12) == 0) || _20 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 {
return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, giftId: _3!, title: _4!, slug: _5!, num: _6!, ownerId: _7, ownerName: _8, ownerAddress: _9, attributes: _10!, availabilityIssued: _11!, availabilityTotal: _12!, giftAddress: _13, resellAmount: _14, releasedBy: _15, valueAmount: _16, valueCurrency: _17, themePeer: _18, peerColor: _19, hostId: _20)
}
else {
return nil

View File

@ -520,10 +520,11 @@ final class VideoChatMicButtonComponent: Component {
var titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) * 0.5), y: size.height + 16.0), size: titleSize)
if component.isCompact {
titleFrame.origin.y -= 11.0
}
} else {
if subtitleText != nil {
titleFrame.origin.y -= 5.0
}
}
if let titleView = self.title.view {
if titleView.superview == nil {
titleView.isUserInteractionEnabled = false

View File

@ -2034,7 +2034,7 @@ final class VideoChatParticipantsComponent: Component {
} else {
let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1)
let value: CGFloat = 1.0 - Display.bezierPoint(0.42, 0.0, 0.58, 1.0, step)
return UIColor(white: 0.0, alpha: baseGradientAlpha * value).multipliedWith(.red)
return UIColor(white: 0.0, alpha: baseGradientAlpha * value)
}
}
let locations = (0 ..< numSteps).map { i -> CGFloat in

View File

@ -2206,7 +2206,7 @@ final class VideoChatScreenComponent: Component {
let landscapeControlsWidth: CGFloat = 104.0
var landscapeControlsOffsetX: CGFloat = 0.0
let landscapeControlsSpacing: CGFloat = 30.0
let landscapeControlsSpacing: CGFloat = 20.0
var leftInset: CGFloat = max(environment.safeInsets.left, 16.0)
@ -2554,11 +2554,58 @@ final class VideoChatScreenComponent: Component {
self.encryptionKeyBackground = nil
}
let videoButtonContent: VideoChatActionButtonComponent.Content?
let videoControlButtonContent: VideoChatActionButtonComponent.Content
var buttonAudio: VideoChatActionButtonComponent.Content.Audio = .speaker
var buttonIsEnabled = false
if let (availableOutputs, maybeCurrentOutput) = self.audioOutputState, let currentOutput = maybeCurrentOutput {
buttonIsEnabled = availableOutputs.count > 1
switch currentOutput {
case .builtin:
buttonAudio = .builtin
case .speaker:
buttonAudio = .speaker
case .headphones:
buttonAudio = .headphones
case let .port(port):
var type: VideoChatActionButtonComponent.Content.BluetoothType = .generic
let portName = port.name.lowercased()
if portName.contains("airpods max") {
type = .airpodsMax
} else if portName.contains("airpods pro") {
type = .airpodsPro
} else if portName.contains("airpods") {
type = .airpods
}
buttonAudio = .bluetooth(type)
}
if availableOutputs.count <= 1 {
buttonAudio = .none
}
}
if let callState = self.callState, let muteState = callState.muteState, !muteState.canUnmute {
videoButtonContent = nil
videoControlButtonContent = .audio(audio: buttonAudio, isEnabled: buttonIsEnabled)
} else {
let isVideoActive = self.callState?.isMyVideoActive ?? false
videoButtonContent = .video(isActive: isVideoActive)
if isVideoActive {
videoControlButtonContent = .rotateCamera
} else {
videoControlButtonContent = .audio(audio: buttonAudio, isEnabled: buttonIsEnabled)
}
}
let actionButtonDiameter: CGFloat = 56.0
let expandedMicrophoneButtonDiameter: CGFloat = actionButtonDiameter
let collapsedMicrophoneButtonDiameter: CGFloat = actionButtonDiameter // 116.0
let buttonsWidth: CGFloat = actionButtonDiameter * 5.0
let buttonsCount = videoButtonContent == nil ? 4 : 5
let buttonsWidth: CGFloat = actionButtonDiameter * CGFloat(buttonsCount)
let remainingButtonsSpace: CGFloat
if isTwoColumnLayout {
remainingButtonsSpace = mainColumnWidth - buttonsWidth
@ -2566,7 +2613,7 @@ final class VideoChatScreenComponent: Component {
remainingButtonsSpace = availableSize.width - 16.0 * 2.0 - buttonsWidth
}
let actionMicrophoneButtonSpacing = min(25.0, floor(remainingButtonsSpace * 0.25))
let actionMicrophoneButtonSpacing = min(33.0, floor(remainingButtonsSpace / CGFloat(buttonsCount - 1)))
var collapsedMicrophoneButtonFrame: CGRect = CGRect(origin: CGPoint(x: floor((availableSize.width - collapsedMicrophoneButtonDiameter) * 0.5), y: availableSize.height - 48.0 - environment.safeInsets.bottom - collapsedMicrophoneButtonDiameter), size: CGSize(width: collapsedMicrophoneButtonDiameter, height: collapsedMicrophoneButtonDiameter))
if self.isAnimatedOutFromPrivateCall {
@ -2607,7 +2654,7 @@ final class VideoChatScreenComponent: Component {
}
}
let microphoneButtonFrame: CGRect
var microphoneButtonFrame: CGRect
if areButtonsCollapsed {
microphoneButtonFrame = expandedMicrophoneButtonFrame
} else {
@ -2632,24 +2679,39 @@ final class VideoChatScreenComponent: Component {
expandedParticipantsClippingY = expandedMicrophoneButtonFrame.minY - 24.0
}
let actionButtonSize = CGSize(width: actionButtonDiameter, height: actionButtonDiameter)
var firstActionButtonFrame = CGRect(origin: CGPoint(x: microphoneButtonFrame.minX - (actionButtonDiameter + actionMicrophoneButtonSpacing) * 2.0, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: actionButtonSize)
var secondActionButtonFrame = CGRect(origin: CGPoint(x: firstActionButtonFrame.minX + actionMicrophoneButtonSpacing + actionButtonDiameter, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: actionButtonSize)
var fourthActionButtonFrame = CGRect(origin: CGPoint(x: microphoneButtonFrame.maxX + 2.0 * actionMicrophoneButtonSpacing + actionButtonDiameter, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: actionButtonSize)
var thirdActionButtonFrame = CGRect(origin: CGPoint(x: microphoneButtonFrame.maxX + actionMicrophoneButtonSpacing, y: microphoneButtonFrame.minY + floor((microphoneButtonFrame.height - actionButtonDiameter) * 0.5)), size: actionButtonSize)
if buttonsCount == 4 {
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
secondActionButtonFrame.origin.x = microphoneButtonFrame.minX
thirdActionButtonFrame.origin.x = microphoneButtonFrame.minX
thirdActionButtonFrame.origin.y = microphoneButtonFrame.maxY + landscapeControlsSpacing
fourthActionButtonFrame.origin.x = microphoneButtonFrame.minX
microphoneButtonFrame.origin.y = availableSize.height * 0.5 - landscapeControlsSpacing * 0.5 - actionButtonDiameter
firstActionButtonFrame.origin.y = microphoneButtonFrame.minY - landscapeControlsSpacing - actionButtonDiameter
thirdActionButtonFrame.origin.y = microphoneButtonFrame.maxY + landscapeControlsSpacing
fourthActionButtonFrame.origin.y = microphoneButtonFrame.maxY + landscapeControlsSpacing + actionButtonDiameter + landscapeControlsSpacing
} else {
microphoneButtonFrame.origin.x = availableSize.width * 0.5 - actionMicrophoneButtonSpacing * 0.5 - actionButtonDiameter
firstActionButtonFrame.origin.x = microphoneButtonFrame.minX - actionMicrophoneButtonSpacing - actionButtonDiameter
thirdActionButtonFrame.origin.x = microphoneButtonFrame.maxX + actionMicrophoneButtonSpacing
fourthActionButtonFrame.origin.x = microphoneButtonFrame.maxX + actionMicrophoneButtonSpacing + actionButtonDiameter + actionMicrophoneButtonSpacing
}
} else if buttonsOnTheSide {
firstActionButtonFrame.origin.x = microphoneButtonFrame.minX
secondActionButtonFrame.origin.x = microphoneButtonFrame.minX
thirdActionButtonFrame.origin.x = microphoneButtonFrame.minX
fourthActionButtonFrame.origin.x = microphoneButtonFrame.minX
secondActionButtonFrame.origin.y = microphoneButtonFrame.minY - landscapeControlsSpacing - actionButtonDiameter
firstActionButtonFrame.origin.y = secondActionButtonFrame.minY - landscapeControlsSpacing - actionButtonDiameter
thirdActionButtonFrame.origin.y = microphoneButtonFrame.maxY + landscapeControlsSpacing
fourthActionButtonFrame.origin.y = thirdActionButtonFrame.maxY + landscapeControlsSpacing
}
@ -3065,83 +3127,6 @@ final class VideoChatScreenComponent: Component {
transition.setBounds(view: microphoneButtonView, bounds: CGRect(origin: CGPoint(), size: microphoneButtonFrame.size))
}
let videoButtonContent: VideoChatActionButtonComponent.Content
let videoControlButtonContent: VideoChatActionButtonComponent.Content
var buttonAudio: VideoChatActionButtonComponent.Content.Audio = .speaker
var buttonIsEnabled = false
if let (availableOutputs, maybeCurrentOutput) = self.audioOutputState, let currentOutput = maybeCurrentOutput {
buttonIsEnabled = availableOutputs.count > 1
switch currentOutput {
case .builtin:
buttonAudio = .builtin
case .speaker:
buttonAudio = .speaker
case .headphones:
buttonAudio = .headphones
case let .port(port):
var type: VideoChatActionButtonComponent.Content.BluetoothType = .generic
let portName = port.name.lowercased()
if portName.contains("airpods max") {
type = .airpodsMax
} else if portName.contains("airpods pro") {
type = .airpodsPro
} else if portName.contains("airpods") {
type = .airpods
}
buttonAudio = .bluetooth(type)
}
if availableOutputs.count <= 1 {
buttonAudio = .none
}
}
if let callState = self.callState, let muteState = callState.muteState, !muteState.canUnmute {
videoButtonContent = .audio(audio: buttonAudio, isEnabled: buttonIsEnabled)
videoControlButtonContent = .audio(audio: buttonAudio, isEnabled: buttonIsEnabled)
} else {
let isVideoActive = self.callState?.isMyVideoActive ?? false
videoButtonContent = .video(isActive: isVideoActive)
if isVideoActive {
videoControlButtonContent = .rotateCamera
} else {
videoControlButtonContent = .audio(audio: buttonAudio, isEnabled: buttonIsEnabled)
}
}
let _ = self.videoButton.update(
transition: transition,
component: AnyComponent(PlainButtonComponent(
content: AnyComponent(VideoChatActionButtonComponent(
strings: environment.strings,
content: videoButtonContent,
microphoneState: actionButtonMicrophoneState,
isCollapsed: areButtonsActuallyCollapsed || buttonsOnTheSide
)),
effectAlignment: .center,
action: { [weak self] in
guard let self else {
return
}
if let callState = self.callState, let muteState = callState.muteState, !muteState.canUnmute {
self.onAudioRoutePressed()
} else {
self.onCameraPressed()
}
},
animateAlpha: false
)),
environment: {},
containerSize: CGSize(width: actionButtonDiameter, height: actionButtonDiameter)
)
if let videoButtonView = self.videoButton.view {
if videoButtonView.superview == nil {
self.containerView.addSubview(videoButtonView)
}
transition.setPosition(view: videoButtonView, position: secondActionButtonFrame.center)
transition.setBounds(view: videoButtonView, bounds: CGRect(origin: CGPoint(), size: secondActionButtonFrame.size))
}
let _ = self.speakerButton.update(
transition: transition,
@ -3178,6 +3163,47 @@ final class VideoChatScreenComponent: Component {
transition.setBounds(view: speakerButtonView, bounds: CGRect(origin: CGPoint(), size: firstActionButtonFrame.size))
}
if let videoButtonContent {
let _ = self.videoButton.update(
transition: transition,
component: AnyComponent(PlainButtonComponent(
content: AnyComponent(VideoChatActionButtonComponent(
strings: environment.strings,
content: videoButtonContent,
microphoneState: actionButtonMicrophoneState,
isCollapsed: areButtonsActuallyCollapsed || buttonsOnTheSide
)),
effectAlignment: .center,
action: { [weak self] in
guard let self else {
return
}
if let callState = self.callState, let muteState = callState.muteState, !muteState.canUnmute {
self.onAudioRoutePressed()
} else {
self.onCameraPressed()
}
},
animateAlpha: false
)),
environment: {},
containerSize: CGSize(width: actionButtonDiameter, height: actionButtonDiameter)
)
if let videoButtonView = self.videoButton.view {
if videoButtonView.superview == nil {
self.containerView.addSubview(videoButtonView)
}
transition.setPosition(view: videoButtonView, position: secondActionButtonFrame.center)
transition.setBounds(view: videoButtonView, bounds: CGRect(origin: CGPoint(), size: secondActionButtonFrame.size))
}
} else if let videoButtonView = self.videoButton.view {
let transition = ComponentTransition(animation: .curve(duration: 0.25, curve: .easeInOut))
transition.animateScale(view: videoButtonView, from: 1.0, to: 0.01)
transition.animateAlpha(view: videoButtonView, from: 1.0, to: 0.0, completion: { _ in
videoButtonView.removeFromSuperview()
})
}
let _ = self.messageButton.update(
transition: transition,
component: AnyComponent(PlainButtonComponent(
@ -3239,7 +3265,7 @@ final class VideoChatScreenComponent: Component {
var inputPanelBottomInset: CGFloat = 0.0
var inputPanelSize: CGSize = .zero
if self.inputPanelIsActive {
let inputPanelAvailableWidth = availableSize.width
let inputPanelAvailableWidth = availableSize.width - environment.safeInsets.left - environment.safeInsets.right
var inputPanelAvailableHeight = 103.0
let keyboardWasHidden = self.inputPanelExternalState.isKeyboardHidden
@ -3462,7 +3488,11 @@ final class VideoChatScreenComponent: Component {
inputPanelBottomInset = inputHeight - environment.safeInsets.bottom
} else {
if self.inputPanelExternalState.isEditing {
if buttonsOnTheSide {
inputPanelBottomInset = 16.0
} else {
inputPanelBottomInset = availableSize.height - microphoneButtonFrame.minY
}
} else {
inputPanelBottomInset = -inputPanelSize.height - environment.safeInsets.bottom
}
@ -3758,7 +3788,8 @@ final class VideoChatScreenComponent: Component {
}
}
let messagesBottomInset: CGFloat = max(inputPanelBottomInset + inputPanelSize.height + 31.0, availableSize.height - microphoneButtonFrame.minY + 16.0) + reactionsInset
let normalMessagesBottomInset: CGFloat = buttonsOnTheSide ? 16.0 : availableSize.height - microphoneButtonFrame.minY + 16.0
let messagesBottomInset: CGFloat = max(inputPanelBottomInset + inputPanelSize.height + 31.0, normalMessagesBottomInset) + reactionsInset
let messagesListSize = self.messagesList.update(
transition: transition,
component: AnyComponent(MessageListComponent(
@ -3768,9 +3799,9 @@ final class VideoChatScreenComponent: Component {
sendActionTransition: sendActionTransition
)),
environment: {},
containerSize: CGSize(width: availableSize.width, height: availableSize.height - messagesBottomInset)
containerSize: CGSize(width: availableSize.width - environment.safeInsets.left - environment.safeInsets.right, height: availableSize.height - messagesBottomInset)
)
let messagesListFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - messagesListSize.height - messagesBottomInset), size: messagesListSize)
let messagesListFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - messagesListSize.width) / 2.0), y: availableSize.height - messagesListSize.height - messagesBottomInset), size: messagesListSize)
if let messagesListView = self.messagesList.view {
if messagesListView.superview == nil {
messagesListView.isUserInteractionEnabled = false

View File

@ -362,7 +362,8 @@ func managedUniqueStarGifts(accountPeerId: PeerId, postbox: Postbox, network: Ne
valueCurrency: nil,
flags: [],
themePeerId: nil,
peerColor: nil
peerColor: nil,
hostPeerId: nil
)
if let entry = CodableEntry(RecentStarGiftItem(gift)) {
items.append(OrderedItemListEntry(id: RecentStarGiftItemId(id).rawValue, contents: entry))

View File

@ -322,6 +322,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
case flags
case themePeerId
case peerColor
case hostPeerId
}
public struct Flags: OptionSet {
@ -610,8 +611,9 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
public let flags: Flags
public let themePeerId: EnginePeer.Id?
public let peerColor: PeerCollectibleColor?
public let hostPeerId: EnginePeer.Id?
public init(id: Int64, giftId: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability, giftAddress: String?, resellAmounts: [CurrencyAmount]?, resellForTonOnly: Bool, releasedBy: EnginePeer.Id?, valueAmount: Int64?, valueCurrency: String?, flags: Flags, themePeerId: EnginePeer.Id?, peerColor: PeerCollectibleColor?) {
public init(id: Int64, giftId: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability, giftAddress: String?, resellAmounts: [CurrencyAmount]?, resellForTonOnly: Bool, releasedBy: EnginePeer.Id?, valueAmount: Int64?, valueCurrency: String?, flags: Flags, themePeerId: EnginePeer.Id?, peerColor: PeerCollectibleColor?, hostPeerId: EnginePeer.Id?) {
self.id = id
self.giftId = giftId
self.title = title
@ -629,6 +631,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
self.flags = flags
self.themePeerId = themePeerId
self.peerColor = peerColor
self.hostPeerId = hostPeerId
}
public init(from decoder: Decoder) throws {
@ -664,6 +667,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
self.flags = try container.decodeIfPresent(Int32.self, forKey: .flags).flatMap { Flags(rawValue: $0) } ?? []
self.themePeerId = try container.decodeIfPresent(Int64.self, forKey: .themePeerId).flatMap { EnginePeer.Id($0) }
self.peerColor = try container.decodeIfPresent(PeerCollectibleColor.self, forKey: .peerColor)
self.hostPeerId = try container.decodeIfPresent(Int64.self, forKey: .hostPeerId).flatMap { EnginePeer.Id($0) }
}
public init(decoder: PostboxDecoder) {
@ -698,6 +702,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
self.flags = decoder.decodeOptionalInt32ForKey(CodingKeys.flags.rawValue).flatMap { Flags(rawValue: $0) } ?? []
self.themePeerId = decoder.decodeOptionalInt64ForKey(CodingKeys.themePeerId.rawValue).flatMap { EnginePeer.Id($0) }
self.peerColor = decoder.decodeCodable(PeerCollectibleColor.self, forKey: CodingKeys.peerColor.rawValue)
self.hostPeerId = decoder.decodeOptionalInt64ForKey(CodingKeys.hostPeerId.rawValue).flatMap { EnginePeer.Id($0) }
}
public func encode(to encoder: Encoder) throws {
@ -726,6 +731,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
try container.encode(self.flags.rawValue, forKey: .flags)
try container.encodeIfPresent(self.themePeerId?.toInt64(), forKey: .themePeerId)
try container.encodeIfPresent(self.peerColor, forKey: .peerColor)
try container.encodeIfPresent(self.hostPeerId?.toInt64(), forKey: .hostPeerId)
}
public func encode(_ encoder: PostboxEncoder) {
@ -778,6 +784,11 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
} else {
encoder.encodeNil(forKey: CodingKeys.peerColor.rawValue)
}
if let hostPeerId = self.hostPeerId {
encoder.encodeInt64(hostPeerId.toInt64(), forKey: CodingKeys.hostPeerId.rawValue)
} else {
encoder.encodeNil(forKey: CodingKeys.hostPeerId.rawValue)
}
}
public func withResellAmounts(_ resellAmounts: [CurrencyAmount]?) -> UniqueGift {
@ -798,7 +809,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
valueCurrency: self.valueCurrency,
flags: self.flags,
themePeerId: self.themePeerId,
peerColor: self.peerColor
peerColor: self.peerColor,
hostPeerId: self.hostPeerId
)
}
@ -820,7 +832,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
valueCurrency: self.valueCurrency,
flags: self.flags,
themePeerId: self.themePeerId,
peerColor: self.peerColor
peerColor: self.peerColor,
hostPeerId: self.hostPeerId
)
}
@ -842,7 +855,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
valueCurrency: self.valueCurrency,
flags: self.flags,
themePeerId: themePeerId,
peerColor: self.peerColor
peerColor: self.peerColor,
hostPeerId: self.hostPeerId
)
}
@ -864,7 +878,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
valueCurrency: self.valueCurrency,
flags: self.flags,
themePeerId: self.themePeerId,
peerColor: self.peerColor
peerColor: self.peerColor,
hostPeerId: self.hostPeerId
)
}
}
@ -977,7 +992,7 @@ extension StarGift {
return nil
}
self = .generic(StarGift.Gift(id: id, title: title, file: file, price: stars, convertStars: convertStars, availability: availability, soldOut: soldOut, flags: flags, upgradeStars: upgradeStars, releasedBy: releasedBy?.peerId, perUserLimit: perUserLimit, lockedUntilDate: lockedUntilDate))
case let .starGiftUnique(apiFlags, id, giftId, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, resellAmounts, releasedBy, valueAmount, valueCurrency, themePeer, peerColor):
case let .starGiftUnique(apiFlags, id, giftId, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, resellAmounts, releasedBy, valueAmount, valueCurrency, themePeer, peerColor, hostPeerId):
let owner: StarGift.UniqueGift.Owner
if let ownerAddress {
owner = .address(ownerAddress)
@ -1009,7 +1024,7 @@ extension StarGift {
break
}
self = .unique(StarGift.UniqueGift(id: id, giftId: giftId, title: title, number: num, slug: slug, owner: owner, attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0) }, availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal), giftAddress: giftAddress, resellAmounts: resellAmounts, resellForTonOnly: (apiFlags & (1 << 7)) != 0, releasedBy: releasedBy?.peerId, valueAmount: valueAmount, valueCurrency: valueCurrency, flags: flags, themePeerId: themePeer?.peerId, peerColor: peerCollectibleColor))
self = .unique(StarGift.UniqueGift(id: id, giftId: giftId, title: title, number: num, slug: slug, owner: owner, attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0) }, availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal), giftAddress: giftAddress, resellAmounts: resellAmounts, resellForTonOnly: (apiFlags & (1 << 7)) != 0, releasedBy: releasedBy?.peerId, valueAmount: valueAmount, valueCurrency: valueCurrency, flags: flags, themePeerId: themePeer?.peerId, peerColor: peerCollectibleColor, hostPeerId: hostPeerId?.peerId))
}
}
}

View File

@ -332,14 +332,6 @@ public extension Peer {
}
var profileColor: PeerNameColor? {
if let emojiStatus {
switch emojiStatus.content {
case let .starGift(_, _, _, _, _, _, outerColor, _, _):
return PeerNameColor.other(outerColor)
default:
break
}
}
switch self {
case let user as TelegramUser:
return user.profileColor
@ -402,7 +394,7 @@ public extension Peer {
public extension TelegramPeerUsername {
var isActive: Bool {
return self.flags.contains(.isActive) || self.flags.contains(.isEditable)
return self.flags.contains(.isActive)
}
}

View File

@ -1501,7 +1501,18 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
}
if let note = cachedData.note, !note.text.isEmpty {
items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.PeerInfo_Notes, rightLabel: presentationData.strings.PeerInfo_NotesInfo, text: note.text, entities: note.entities, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: []), action: nil, linkItemAction: bioLinkAction, button: nil, contextAction: noteContextAction, requestLayout: { animated in
var entities = note.entities
if !context.isPremium {
entities = entities.filter { entity in
switch entity.type {
case .Url, .TextUrl:
return false
default:
return true
}
}
}
items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.PeerInfo_Notes, rightLabel: presentationData.strings.PeerInfo_NotesInfo, text: note.text, entities: entities, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: []), action: nil, linkItemAction: bioLinkAction, button: nil, contextAction: noteContextAction, requestLayout: { animated in
interaction.requestLayout(animated)
}))
}
@ -4577,7 +4588,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
let noteUpdateSignal: Signal<Never, ContactUpdateError>
if noteUpdated, let note {
let entities = generateChatInputTextEntities(note)
let entities = generateTextEntities(note.string, enabledTypes: [.mention, .hashtag, .allUrl], currentEntities: generateChatInputTextEntities(note))
noteUpdateSignal = context.engine.contacts.updateContactNote(peerId: peer.id, text: note.string, entities: entities)
|> mapError { _ -> ContactUpdateError in
return .generic

View File

@ -768,7 +768,8 @@ final class UserAppearanceScreenComponent: Component {
valueCurrency: nil,
flags: [],
themePeerId: nil,
peerColor: nil
peerColor: nil,
hostPeerId: nil
)
signal = component.context.engine.accountData.setStarGiftStatus(starGift: gift, expirationDate: emojiStatus.expirationDate)
} else {
@ -1110,7 +1111,16 @@ final class UserAppearanceScreenComponent: Component {
let updatedSection = Section(rawValue: intValue) ?? .profile
if self.currentSection != updatedSection {
if (updatedSection == .name && self.selectedProfileGift != nil) || (updatedSection == .profile && self.selectedNameGift != nil) {
switch updatedSection {
case .profile:
self.selectedNameGift = nil
self.updatedPeerNameColor = nil
self.updatedPeerNameEmoji = nil
case .name:
self.selectedProfileGift = nil
self.updatedPeerProfileColor = nil
self.updatedPeerProfileEmoji = nil
}
} else {
self.currentSection = updatedSection
self.state?.updated(transition: .easeInOut(duration: 0.3).withUserData(TransitionHint(animateTabChange: true)))
@ -1871,6 +1881,8 @@ public class UserAppearanceScreen: ViewControllerComponentContainer {
self.automaticallyControlPresentationContextLayout = false
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.title = ""
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)