Various fixes

This commit is contained in:
Ilya Laktyushin 2024-12-06 18:32:57 +04:00
parent c016f0dd7e
commit 704156e55f
21 changed files with 148 additions and 76 deletions

View File

@ -13304,6 +13304,8 @@ Sorry for the inconvenience.";
"Chat.VideoProcessingInfo" = "The video will be published once converted and optimized.";
"Camera.CollageRetake" = "Retake";
"Camera.CollageDelete" = "Delete";
"Camera.CollageManagementTooltip" = "Tap a tile to delete or reorder it.";
"Camera.CollageReorderingInfo" = "Hold and drag tiles to reorder them.";

View File

@ -519,10 +519,13 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
default:
break
}
} else if case let .channel(peer) = peer, case .group = peer.info, peer.hasPermission(.inviteMembers) {
canManage = true
} else if case let .channel(peer) = peer, case .broadcast = peer.info, peer.hasPermission(.addAdmins) {
canManage = true
}
if canManage {
} else if case let .channel(peer) = peer, case .group = peer.info, peer.hasPermission(.inviteMembers) {
} else {
enabled = false
}
@ -2413,10 +2416,13 @@ public final class ChatListNode: ListView {
default:
break
}
} else if case let .channel(peer) = peer, case .group = peer.info, peer.hasPermission(.inviteMembers) {
canManage = true
} else if case let .channel(peer) = peer, case .broadcast = peer.info, peer.hasPermission(.addAdmins) {
canManage = true
}
if canManage {
} else if case let .channel(peer) = peer, case .group = peer.info, peer.hasPermission(.inviteMembers) {
} else {
return false
}

View File

@ -605,6 +605,7 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState,
var result: [ChatListNodeEntry] = []
var hasContacts = false
if !view.hasEarlier {
var existingPeerIds = Set<EnginePeer.Id>()
for item in view.items {
@ -620,8 +621,9 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState,
peer: contact.peer,
presence: contact.presence
)))
hasContacts = true
}
if !contacts.isEmpty {
if hasContacts {
result.append(.SectionHeader(presentationData: state.presentationData, displayHide: !view.items.isEmpty))
}
}

View File

@ -1277,9 +1277,20 @@ public final class ChatPresentationInterfaceState: Equatable {
}
}
public func canBypassRestrictions(chatPresentationInterfaceState: ChatPresentationInterfaceState) -> Bool {
guard let boostsToUnrestrict = chatPresentationInterfaceState.boostsToUnrestrict, boostsToUnrestrict > 0 else {
return false
}
if let appliedBoosts = chatPresentationInterfaceState.appliedBoosts, appliedBoosts >= boostsToUnrestrict {
return true
}
return false
}
public func canSendMessagesToChat(_ state: ChatPresentationInterfaceState) -> Bool {
if let peer = state.renderedPeer?.peer {
if canSendMessagesToPeer(peer) {
let canBypassRestrictions = canBypassRestrictions(chatPresentationInterfaceState: state)
if canSendMessagesToPeer(peer, ignoreDefault: canBypassRestrictions) {
return true
} else {
return false

View File

@ -97,7 +97,11 @@ final class NavigationTransitionCoordinator {
case .Push:
self.container.addSubnode(topNode)
case .Pop:
self.container.insertSubnode(bottomNode, belowSubnode: topNode)
if topNode.supernode == self.container {
self.container.insertSubnode(bottomNode, belowSubnode: topNode)
} else {
self.container.addSubnode(topNode)
}
}
if !self.isFlat {

View File

@ -871,6 +871,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
if !isVideo {
canEdit = true
isImage = true
}
} else if let media = media as? TelegramMediaWebpage, case let .Loaded(content) = media.content {
let type = webEmbedType(content: content)

View File

@ -908,7 +908,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1577421297] = { return Api.StarsGiftOption.parse_starsGiftOption($0) }
dict[-1798404822] = { return Api.StarsGiveawayOption.parse_starsGiveawayOption($0) }
dict[1411605001] = { return Api.StarsGiveawayWinnersOption.parse_starsGiveawayWinnersOption($0) }
dict[2033461574] = { return Api.StarsRevenueStatus.parse_starsRevenueStatus($0) }
dict[-21080943] = { return Api.StarsRevenueStatus.parse_starsRevenueStatus($0) }
dict[779004698] = { return Api.StarsSubscription.parse_starsSubscription($0) }
dict[88173912] = { return Api.StarsSubscriptionPricing.parse_starsSubscriptionPricing($0) }
dict[198776256] = { return Api.StarsTopupOption.parse_starsTopupOption($0) }

View File

@ -904,18 +904,18 @@ public extension Api {
}
public extension Api {
enum StarsRevenueStatus: TypeConstructorDescription {
case starsRevenueStatus(flags: Int32, currentBalance: Int64, availableBalance: Int64, overallRevenue: Int64, nextWithdrawalAt: Int32?)
case starsRevenueStatus(flags: Int32, currentBalance: Api.StarsAmount, availableBalance: Api.StarsAmount, overallRevenue: Api.StarsAmount, nextWithdrawalAt: Int32?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .starsRevenueStatus(let flags, let currentBalance, let availableBalance, let overallRevenue, let nextWithdrawalAt):
if boxed {
buffer.appendInt32(2033461574)
buffer.appendInt32(-21080943)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(currentBalance, buffer: buffer, boxed: false)
serializeInt64(availableBalance, buffer: buffer, boxed: false)
serializeInt64(overallRevenue, buffer: buffer, boxed: false)
currentBalance.serialize(buffer, true)
availableBalance.serialize(buffer, true)
overallRevenue.serialize(buffer, true)
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(nextWithdrawalAt!, buffer: buffer, boxed: false)}
break
}
@ -931,12 +931,18 @@ public extension Api {
public static func parse_starsRevenueStatus(_ reader: BufferReader) -> StarsRevenueStatus? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int64?
_2 = reader.readInt64()
var _3: Int64?
_3 = reader.readInt64()
var _4: Int64?
_4 = reader.readInt64()
var _2: Api.StarsAmount?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.StarsAmount
}
var _3: Api.StarsAmount?
if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.StarsAmount
}
var _4: Api.StarsAmount?
if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.StarsAmount
}
var _5: Int32?
if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() }
let _c1 = _1 != nil

View File

@ -24,7 +24,7 @@ public enum TelegramChannelPermission {
}
public extension TelegramChannel {
func hasPermission(_ permission: TelegramChannelPermission) -> Bool {
func hasPermission(_ permission: TelegramChannelPermission, ignoreDefault: Bool = false) -> Bool {
if self.flags.contains(.isCreator) {
if case .canBeAnonymous = permission {
if let adminRights = self.adminRights {
@ -50,7 +50,7 @@ public extension TelegramChannel {
if let bannedRights = self.bannedRights, bannedRights.flags.contains(.banSendText) {
return false
}
if let defaultBannedRights = self.defaultBannedRights, defaultBannedRights.flags.contains(.banSendText) {
if let defaultBannedRights = self.defaultBannedRights, defaultBannedRights.flags.contains(.banSendText) && !ignoreDefault {
return false
}
return true
@ -69,7 +69,7 @@ public extension TelegramChannel {
if let bannedRights = self.bannedRights, bannedRights.flags.contains(.banSendPhotos) {
return false
}
if let defaultBannedRights = self.defaultBannedRights, defaultBannedRights.flags.contains(.banSendPhotos) {
if let defaultBannedRights = self.defaultBannedRights, defaultBannedRights.flags.contains(.banSendText) && !ignoreDefault {
return false
}
return true
@ -88,7 +88,7 @@ public extension TelegramChannel {
if let bannedRights = self.bannedRights, bannedRights.flags.contains(.banSendVideos) {
return false
}
if let defaultBannedRights = self.defaultBannedRights, defaultBannedRights.flags.contains(.banSendVideos) {
if let defaultBannedRights = self.defaultBannedRights, defaultBannedRights.flags.contains(.banSendVideos) && !ignoreDefault {
return false
}
return true
@ -121,7 +121,7 @@ public extension TelegramChannel {
if let bannedRights = self.bannedRights, bannedRights.flags.intersection(flags) == flags {
return false
}
if let defaultBannedRights = self.defaultBannedRights, defaultBannedRights.flags.intersection(flags) == flags {
if let defaultBannedRights = self.defaultBannedRights, defaultBannedRights.flags.intersection(flags) == flags && !ignoreDefault {
return false
}
return true

View File

@ -146,7 +146,7 @@ extension StarsRevenueStats.Balances {
init(apiStarsRevenueStatus: Api.StarsRevenueStatus) {
switch apiStarsRevenueStatus {
case let .starsRevenueStatus(flags, currentBalance, availableBalance, overallRevenue, nextWithdrawalAt):
self.init(currentBalance: StarsAmount(value: currentBalance, nanos: 0), availableBalance: StarsAmount(value: availableBalance, nanos: 0), overallRevenue: StarsAmount(value: overallRevenue, nanos: 0), withdrawEnabled: ((flags & (1 << 0)) != 0), nextWithdrawalTimestamp: nextWithdrawalAt)
self.init(currentBalance: StarsAmount(apiAmount: currentBalance), availableBalance: StarsAmount(apiAmount: availableBalance), overallRevenue: StarsAmount(apiAmount: overallRevenue), withdrawEnabled: ((flags & (1 << 0)) != 0), nextWithdrawalTimestamp: nextWithdrawalAt)
}
}
}

View File

@ -6,7 +6,7 @@ import Postbox
private final class LinkHelperClass: NSObject {
}
public func canSendMessagesToPeer(_ peer: Peer) -> Bool {
public func canSendMessagesToPeer(_ peer: Peer, ignoreDefault: Bool = false) -> Bool {
if let peer = peer as? TelegramUser, peer.addressName == "replies" {
return false
} else if peer is TelegramUser || peer is TelegramGroup {
@ -14,7 +14,7 @@ public func canSendMessagesToPeer(_ peer: Peer) -> Bool {
} else if let peer = peer as? TelegramSecretChat {
return peer.embeddedState == .active
} else if let peer = peer as? TelegramChannel {
return peer.hasPermission(.sendSomething)
return peer.hasPermission(.sendSomething, ignoreDefault: ignoreDefault)
} else {
return false
}

View File

@ -504,6 +504,10 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate {
self.contextAction?(item.uniqueId, self.extractedContainerView, nil)
}
func stopPlayback() {
self.videoPlayer?.pause()
}
func resetPlayback() {
self.videoPlayer?.seek(to: .zero)
self.videoPlayer?.play()
@ -677,13 +681,13 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate {
var delayAppearance = false
if let snapshotView = self.snapshotView {
if snapshotView is UIImageView {
} else {
delayAppearance = true
Queue.mainQueue().after(0.2, {
snapshotView.removeFromSuperview()
})
}
Queue.mainQueue().after(0.2, {
snapshotView.removeFromSuperview()
})
self.snapshotView = nil
}
if let previewLayer = self.previewLayer {
@ -991,6 +995,12 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate {
}
}
func stopPlayback() {
for (_, itemView) in self.itemViews {
itemView.stopPlayback()
}
}
func resetPlayback() {
for (_, itemView) in self.itemViews {
itemView.resetPlayback()
@ -1054,7 +1064,7 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate {
var itemList: [ContextMenuItem] = []
if self.collage.cameraIndex == nil {
itemList.append(.action(ContextMenuActionItem(text: "Retake", icon: { theme in
itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.Camera_CollageRetake, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Camera"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in
f(.default)
@ -1066,7 +1076,7 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate {
if self.itemViews.count > 2 {
itemList.append(.separator)
itemList.append(.action(ContextMenuActionItem(text: "Delete", textColor: .destructive, icon: { theme in
itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.Camera_CollageDelete, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
}, action: { [weak self] _, f in
f(.dismissWithoutContent)

View File

@ -2615,13 +2615,9 @@ public class CameraScreenImpl: ViewController, CameraScreen {
view.animateOutToEditor(transition: transition)
}
Queue.mainQueue().after(2.0, {
if self.cameraState.isCollageEnabled {
self.collage = nil
if let collageView = self.collageView {
collageView.removeFromSuperview()
self.collageView = nil
}
Queue.mainQueue().after(1.5, {
if let collageView = self.collageView {
collageView.stopPlayback()
}
})
}
@ -2679,6 +2675,10 @@ public class CameraScreenImpl: ViewController, CameraScreen {
if !toGallery {
self.resumeCameraCapture(fromGallery: false)
if let collageView = self.collageView {
collageView.resetPlayback()
}
self.cameraIsActive = true
self.requestUpdateLayout(transition: .immediate)

View File

@ -999,14 +999,22 @@ final class CaptureControlsComponent: Component {
func animateInFromEditor(transition: ComponentTransition) {
self.animatedOut = false
guard let component = self.component else {
return
}
if let view = self.galleryButtonView.view {
transition.setScale(view: view, scale: 1.0)
transition.setAlpha(view: view, alpha: 1.0)
if !component.hideControls {
transition.setAlpha(view: view, alpha: 1.0)
}
}
if let view = self.flipButtonView.view {
transition.setScale(view: view, scale: 1.0)
transition.setAlpha(view: view, alpha: 1.0)
if !component.hideControls {
transition.setAlpha(view: view, alpha: 1.0)
}
}
if let view = self.shutterButtonView.view {

View File

@ -432,8 +432,8 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
buttons = [
self.deleteButton,
tagButton,
self.forwardButton,
self.shareButton
self.shareButton,
self.forwardButton
]
} else {
buttons = [

View File

@ -406,7 +406,7 @@ final class GiftSetupScreenComponent: Component {
purpose: .starGift(peerId: component.peerId, requiredStars: starGift.price),
completion: { [weak starsContext] stars in
starsContext?.add(balance: StarsAmount(value: stars, nanos: 0))
Queue.mainQueue().after(0.1) {
Queue.mainQueue().after(2.0) {
proceed()
}
}

View File

@ -433,7 +433,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView {
updateTimeout(nil)
})))
let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(sourceView: sourceView, position: self.currentIsCaptionAbove ? .bottom : .top)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
self.present(contextController)
}
@ -551,12 +551,15 @@ private final class HeaderContextReferenceContentSource: ContextReferenceContent
var keepInPlace: Bool {
return true
}
let position: ContextControllerReferenceViewInfo.ActionsPosition
init(sourceView: UIView) {
init(sourceView: UIView, position: ContextControllerReferenceViewInfo.ActionsPosition) {
self.sourceView = sourceView
self.position = position
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds, actionsPosition: .top)
return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds, actionsPosition: self.position)
}
}

View File

@ -188,7 +188,7 @@ public final class MediaEditor {
private var additionalPlayers: [AVPlayer] = []
private let additionalPlayersPromise = Promise<[AVPlayer]>([])
private var additionalPlayerAudioMix: AVMutableAudioMix?
private var additionalPlayerAudioMixes: [AVMutableAudioMix] = []
private var audioPlayer: AVPlayer?
private let audioPlayerPromise = Promise<AVPlayer?>(nil)
@ -1732,11 +1732,11 @@ public final class MediaEditor {
}
self.updateValues(mode: .skipRendering) { values in
var values = values.withUpdatedCollage(collage)
if mainVideoIsMuted {
values = values.withUpdatedVideoVolume(0.0)
}
return values
return values.withUpdatedCollage(collage)
}
if mainVideoIsMuted {
self.setVideoVolume(0.0)
}
self.setupAdditionalVideoPlayback()
@ -1756,7 +1756,7 @@ public final class MediaEditor {
self.additionalPlayers.forEach { $0.pause() }
self.additionalPlayers = []
self.additionalPlayersPromise.set(.single([]))
self.additionalPlayerAudioMix = nil
self.additionalPlayerAudioMixes = []
if let textureSource = self.renderer.textureSource as? UniversalTextureSource {
textureSource.forceUpdates = true
@ -1837,19 +1837,29 @@ public final class MediaEditor {
}
var additionalInputs: [UniversalTextureSource.Input] = []
var additionalPlayers: [AVPlayer] = []
var audioMixes: [AVMutableAudioMix] = []
for (input, player, volume) in results {
additionalInputs.append(input)
if let player {
if let volume {
player.volume = Float(volume)
}
additionalPlayers.append(player)
if let asset = player.currentItem?.asset {
let audioMix = AVMutableAudioMix()
let audioMixInputParameters = AVMutableAudioMixInputParameters(track: asset.tracks(withMediaType: .audio).first)
if let volume {
audioMixInputParameters.setVolume(Float(volume), at: .zero)
}
audioMix.inputParameters = [audioMixInputParameters]
player.currentItem?.audioMix = audioMix
audioMixes.append(audioMix)
}
}
}
self.additionalPlayers = additionalPlayers
self.additionalPlayersPromise.set(.single(additionalPlayers))
self.additionalPlayerAudioMixes = audioMixes
(self.renderer.textureSource as? UniversalTextureSource)?.setAdditionalInputs(additionalInputs)
@ -1878,7 +1888,7 @@ public final class MediaEditor {
self.additionalPlayers = [player]
self.additionalPlayersPromise.set(.single([player]))
self.additionalPlayerAudioMix = audioMix
self.additionalPlayerAudioMixes = [audioMix]
(self.renderer.textureSource as? UniversalTextureSource)?.setAdditionalInputs([.video(playerItem, nil)])
}
@ -1991,11 +2001,21 @@ public final class MediaEditor {
}
}
if let audioMix = self.additionalPlayerAudioMix, let asset = self.additionalPlayers.first?.currentItem?.asset {
let audioMixInputParameters = AVMutableAudioMixInputParameters(track: asset.tracks(withMediaType: .audio).first)
audioMixInputParameters.setVolume(Float(volume ?? 1.0), at: .zero)
audioMix.inputParameters = [audioMixInputParameters]
self.additionalPlayers.first?.currentItem?.audioMix = audioMix
if let trackId {
if let index = self.playerIndexForTrackId(trackId), index < self.additionalPlayerAudioMixes.count && index < self.additionalPlayers.count, let asset = self.additionalPlayers[index].currentItem?.asset {
let audioMix = self.additionalPlayerAudioMixes[index]
let audioMixInputParameters = AVMutableAudioMixInputParameters(track: asset.tracks(withMediaType: .audio).first)
audioMixInputParameters.setVolume(Float(volume ?? 1.0), at: .zero)
audioMix.inputParameters = [audioMixInputParameters]
self.additionalPlayers[index].currentItem?.audioMix = audioMix
}
} else {
if let audioMix = self.additionalPlayerAudioMixes.first, let asset = self.additionalPlayers.first?.currentItem?.asset {
let audioMixInputParameters = AVMutableAudioMixInputParameters(track: asset.tracks(withMediaType: .audio).first)
audioMixInputParameters.setVolume(Float(volume ?? 1.0), at: .zero)
audioMix.inputParameters = [audioMixInputParameters]
self.additionalPlayers.first?.currentItem?.audioMix = audioMix
}
}
}

View File

@ -1091,17 +1091,17 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega
self.videoTransparentFramesContainer.layer.cornerRadius = framesCornerRadius
self.videoOpaqueFramesContainer.layer.cornerRadius = framesCornerRadius
let scrubberSize = CGSize(width: availableSize.width, height: isSelected ? fullTrackHeight : collapsedTrackHeight)
var screenSpanDuration = duration
if track.isAudio && track.isMain {
if track.isAudio && track.isMain && !track.isTimeline {
screenSpanDuration = min(30.0, track.duration)
}
let minimalAudioWidth = handleWidth * 2.0
var containerTotalWidth = scrubberSize.width
if track.isAudio || !track.isMain, screenSpanDuration > 0.0 {
if !track.isTimeline, track.isAudio || !track.isMain, screenSpanDuration > 0.0 {
let trackFraction = track.duration / screenSpanDuration
if trackFraction < 1.0 - .ulpOfOne || trackFraction > 1.0 + .ulpOfOne {
containerTotalWidth = max(minimalAudioWidth, ceil(availableSize.width * trackFraction))
@ -1765,4 +1765,12 @@ private extension MediaScrubberComponent.Track {
return false
}
}
var isTimeline: Bool {
if case let .audio(_, _, _, _, isTimeline) = self.content {
return isTimeline
} else {
return false
}
}
}

View File

@ -339,7 +339,8 @@ func canReplyInChat(_ chatPresentationInterfaceState: ChatPresentationInterfaceS
case .peer:
if let channel = peer as? TelegramChannel {
if case .member = channel.participationStatus {
canReply = channel.hasPermission(.sendSomething)
let canBypassRestrictions = canBypassRestrictions(chatPresentationInterfaceState: chatPresentationInterfaceState)
canReply = channel.hasPermission(.sendSomething, ignoreDefault: canBypassRestrictions)
}
if case .broadcast = channel.info {
canReply = true

View File

@ -9,16 +9,6 @@ import ChatBotStartInputPanelNode
import ChatChannelSubscriberInputPanelNode
import ChatMessageSelectionInputPanelNode
func canBypassRestrictions(chatPresentationInterfaceState: ChatPresentationInterfaceState) -> Bool {
guard let boostsToUnrestrict = chatPresentationInterfaceState.boostsToUnrestrict else {
return false
}
if let appliedBoosts = chatPresentationInterfaceState.appliedBoosts, appliedBoosts >= boostsToUnrestrict {
return true
}
return false
}
func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatInputPanelNode?, currentSecondaryPanel: ChatInputPanelNode?, textInputPanelNode: ChatTextInputPanelNode?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> (primary: ChatInputPanelNode?, secondary: ChatInputPanelNode?) {
if let renderedPeer = chatPresentationInterfaceState.renderedPeer, renderedPeer.peer?.restrictionText(platform: "ios", contentSettings: context.currentContentSettings.with { $0 }) != nil {
return (nil, nil)