Merge branch 'master' into beta

This commit is contained in:
Ali 2020-12-13 23:31:06 +04:00
commit 975a03a1b6
32 changed files with 1440 additions and 1072 deletions

View File

@ -5962,7 +5962,7 @@ Sorry for the inconvenience.";
"VoiceChat.InvitedPeerText" = "You invited %@ to the voice chat";
"VoiceChat.RemovedPeerText" = "You removed %@ from this group";
"Notification.VoiceChatStarted" = "Voice chat started";
"Notification.VoiceChatStarted" = "%1$@ started a voice chat";
"Notification.VoiceChatEnded" = "Voice chat ended (%@)";
"VoiceChat.Panel.TapToJoin" = "Tap to join";

View File

@ -474,6 +474,8 @@ public protocol ChatController: ViewController {
func updatePresentationMode(_ mode: ChatControllerPresentationMode)
func beginMessageSearch(_ query: String)
func displayPromoAnnouncement(text: String)
var isSendButtonVisible: Bool { get }
}
public protocol ChatMessagePreviewItemNode: class {

View File

@ -19,7 +19,8 @@ typedef enum {
TGCameraControllerPassportIdIntent,
TGCameraControllerPassportMultipleIntent,
TGCameraControllerAvatarIntent,
TGCameraControllerSignupAvatarIntent
TGCameraControllerSignupAvatarIntent,
TGCameraControllerGenericPhotoOnlyIntent
} TGCameraControllerIntent;
@interface TGCameraControllerWindow : TGOverlayControllerWindow

View File

@ -1250,7 +1250,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
}
}];
bool hasCamera = !self.inhibitMultipleCapture && ((_intent == TGCameraControllerGenericIntent && !_shortcut) || (_intent == TGCameraControllerPassportMultipleIntent));
bool hasCamera = !self.inhibitMultipleCapture && (((_intent == TGCameraControllerGenericIntent || _intent == TGCameraControllerGenericPhotoOnlyIntent) && !_shortcut) || (_intent == TGCameraControllerPassportMultipleIntent));
TGMediaPickerGalleryModel *model = [[TGMediaPickerGalleryModel alloc] initWithContext:windowContext items:galleryItems focusItem:focusItem selectionContext:_items.count > 1 ? selectionContext : nil editingContext:editingContext hasCaptions:self.allowCaptions allowCaptionEntities:self.allowCaptionEntities hasTimer:self.hasTimer onlyCrop:_intent == TGCameraControllerPassportIntent || _intent == TGCameraControllerPassportIdIntent || _intent == TGCameraControllerPassportMultipleIntent inhibitDocumentCaptions:self.inhibitDocumentCaptions hasSelectionPanel:true hasCamera:hasCamera recipientName:self.recipientName];
model.inhibitMute = self.inhibitMute;
model.controller = galleryController;

View File

@ -55,6 +55,8 @@ public final class CachedGroupData: CachedPeerData {
public let messageIds: Set<MessageId>
public let associatedHistoryMessageId: MessageId? = nil
public let activeCall: CachedChannelData.ActiveCall?
public init() {
self.participants = nil
self.exportedInvitation = nil
@ -68,9 +70,11 @@ public final class CachedGroupData: CachedPeerData {
self.hasScheduledMessages = false
self.invitedBy = nil
self.photo = nil
self.activeCall = nil
}
public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?, photo: TelegramMediaImage?) {
public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?, photo: TelegramMediaImage?, activeCall: CachedChannelData.ActiveCall?) {
self.participants = participants
self.exportedInvitation = exportedInvitation
self.botInfos = botInfos
@ -81,6 +85,7 @@ public final class CachedGroupData: CachedPeerData {
self.hasScheduledMessages = hasScheduledMessages
self.invitedBy = invitedBy
self.photo = photo
self.activeCall = activeCall
var messageIds = Set<MessageId>()
if let pinnedMessageId = self.pinnedMessageId {
@ -132,6 +137,12 @@ public final class CachedGroupData: CachedPeerData {
self.photo = nil
}
if let activeCall = decoder.decodeObjectForKey("activeCall", decoder: { CachedChannelData.ActiveCall(decoder: $0) }) as? CachedChannelData.ActiveCall {
self.activeCall = activeCall
} else {
self.activeCall = nil
}
var messageIds = Set<MessageId>()
if let pinnedMessageId = self.pinnedMessageId {
messageIds.insert(pinnedMessageId)
@ -196,6 +207,12 @@ public final class CachedGroupData: CachedPeerData {
} else {
encoder.encodeNil(forKey: "ph")
}
if let activeCall = self.activeCall {
encoder.encodeObject(activeCall, forKey: "activeCall")
} else {
encoder.encodeNil(forKey: "activeCall")
}
}
public func isEqual(to: CachedPeerData) -> Bool {
@ -203,46 +220,54 @@ public final class CachedGroupData: CachedPeerData {
return false
}
if self.activeCall != other.activeCall {
return false
}
return self.participants == other.participants && self.exportedInvitation == other.exportedInvitation && self.botInfos == other.botInfos && self.peerStatusSettings == other.peerStatusSettings && self.pinnedMessageId == other.pinnedMessageId && self.about == other.about && self.flags == other.flags && self.hasScheduledMessages == other.hasScheduledMessages && self.invitedBy == other.invitedBy
}
public func withUpdatedParticipants(_ participants: CachedGroupParticipants?) -> CachedGroupData {
return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
}
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
}
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
}
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
}
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
}
public func withUpdatedAbout(_ about: String?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
}
public func withUpdatedFlags(_ flags: CachedGroupFlags) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
}
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
}
public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy, photo: self.photo)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall)
}
public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: photo)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall)
}
public func withUpdatedActiveCall(_ activeCall: CachedChannelData.ActiveCall?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall)
}
}

View File

@ -11,8 +11,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) }
dict[1210199983] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) }
dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) }
dict[461151667] = { return Api.ChatFull.parse_chatFull($0) }
dict[-281384243] = { return Api.ChatFull.parse_channelFull($0) }
dict[231260545] = { return Api.ChatFull.parse_chatFull($0) }
dict[-1159937629] = { return Api.PollResults.parse_pollResults($0) }
dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) }
dict[-636267638] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) }
@ -266,8 +266,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-13975905] = { return Api.Update.parse_updateChannelUserTyping($0) }
dict[-309990731] = { return Api.Update.parse_updatePinnedMessages($0) }
dict[-2054649973] = { return Api.Update.parse_updatePinnedChannelMessages($0) }
dict[321954198] = { return Api.Update.parse_updateChat($0) }
dict[-219423922] = { return Api.Update.parse_updateGroupCallParticipants($0) }
dict[1462009966] = { return Api.Update.parse_updateGroupCall($0) }
dict[-1537295973] = { return Api.Update.parse_updateGroupCall($0) }
dict[1059076315] = { return Api.Update.parse_updateBotInlineQuery($0) }
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
@ -346,7 +347,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1494368259] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaContact($0) }
dict[-1768777083] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaGeo($0) }
dict[2002815875] = { return Api.KeyboardButtonRow.parse_keyboardButtonRow($0) }
dict[-290164953] = { return Api.StickerSet.parse_stickerSet($0) }
dict[1088567208] = { return Api.StickerSet.parse_stickerSet($0) }
dict[354925740] = { return Api.SecureSecretSettings.parse_secureSecretSettings($0) }
dict[539045032] = { return Api.photos.Photo.parse_photo($0) }
dict[-208488460] = { return Api.InputContact.parse_inputPhoneContact($0) }

View File

@ -2052,30 +2052,11 @@ public extension Api {
}
public enum ChatFull: TypeConstructorDescription {
case chatFull(flags: Int32, id: Int32, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?)
case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?)
case chatFull(flags: Int32, id: Int32, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId):
if boxed {
buffer.appendInt32(461151667)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
serializeString(about, buffer: buffer, boxed: false)
participants.serialize(buffer, true)
if Int(flags) & Int(1 << 2) != 0 {chatPhoto!.serialize(buffer, true)}
notifySettings.serialize(buffer, true)
exportedInvite.serialize(buffer, true)
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(botInfo!.count))
for item in botInfo! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)}
break
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call):
if boxed {
buffer.appendInt32(-281384243)
@ -2113,66 +2094,38 @@ public extension Api {
serializeInt32(pts, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 21) != 0 {call!.serialize(buffer, true)}
break
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call):
if boxed {
buffer.appendInt32(231260545)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
serializeString(about, buffer: buffer, boxed: false)
participants.serialize(buffer, true)
if Int(flags) & Int(1 << 2) != 0 {chatPhoto!.serialize(buffer, true)}
notifySettings.serialize(buffer, true)
exportedInvite.serialize(buffer, true)
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(botInfo!.count))
for item in botInfo! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 12) != 0 {call!.serialize(buffer, true)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId):
return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId)])
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call):
return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts), ("call", call)])
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call):
return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId), ("call", call)])
}
}
public static func parse_chatFull(_ reader: BufferReader) -> ChatFull? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: String?
_3 = parseString(reader)
var _4: Api.ChatParticipants?
if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.ChatParticipants
}
var _5: Api.Photo?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.Photo
} }
var _6: Api.PeerNotifySettings?
if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings
}
var _7: Api.ExportedChatInvite?
if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite
}
var _8: [Api.BotInfo]?
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotInfo.self)
} }
var _9: Int32?
if Int(_1!) & Int(1 << 6) != 0 {_9 = reader.readInt32() }
var _10: Int32?
if Int(_1!) & Int(1 << 11) != 0 {_10 = reader.readInt32() }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
let _c6 = _6 != nil
let _c7 = _7 != nil
let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil
let _c10 = (Int(_1!) & Int(1 << 11) == 0) || _10 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7!, botInfo: _8, pinnedMsgId: _9, folderId: _10)
}
else {
return nil
}
}
public static func parse_channelFull(_ reader: BufferReader) -> ChatFull? {
var _1: Int32?
_1 = reader.readInt32()
@ -2279,6 +2232,59 @@ public extension Api {
return nil
}
}
public static func parse_chatFull(_ reader: BufferReader) -> ChatFull? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: String?
_3 = parseString(reader)
var _4: Api.ChatParticipants?
if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.ChatParticipants
}
var _5: Api.Photo?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.Photo
} }
var _6: Api.PeerNotifySettings?
if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings
}
var _7: Api.ExportedChatInvite?
if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite
}
var _8: [Api.BotInfo]?
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotInfo.self)
} }
var _9: Int32?
if Int(_1!) & Int(1 << 6) != 0 {_9 = reader.readInt32() }
var _10: Int32?
if Int(_1!) & Int(1 << 11) != 0 {_10 = reader.readInt32() }
var _11: Api.InputGroupCall?
if Int(_1!) & Int(1 << 12) != 0 {if let signature = reader.readInt32() {
_11 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
let _c6 = _6 != nil
let _c7 = _7 != nil
let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil
let _c10 = (Int(_1!) & Int(1 << 11) == 0) || _10 != nil
let _c11 = (Int(_1!) & Int(1 << 12) == 0) || _11 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7!, botInfo: _8, pinnedMsgId: _9, folderId: _10, call: _11)
}
else {
return nil
}
}
}
public enum PollResults: TypeConstructorDescription {
@ -6418,8 +6424,9 @@ public extension Api {
case updateChannelUserTyping(flags: Int32, channelId: Int32, topMsgId: Int32?, userId: Int32, action: Api.SendMessageAction)
case updatePinnedMessages(flags: Int32, peer: Api.Peer, messages: [Int32], pts: Int32, ptsCount: Int32)
case updatePinnedChannelMessages(flags: Int32, channelId: Int32, messages: [Int32], pts: Int32, ptsCount: Int32)
case updateChat(chatId: Int32)
case updateGroupCallParticipants(call: Api.InputGroupCall, participants: [Api.GroupCallParticipant], version: Int32)
case updateGroupCall(channelId: Int32, call: Api.GroupCall)
case updateGroupCall(chatId: Int32, call: Api.GroupCall)
case updateBotInlineQuery(flags: Int32, queryId: Int64, userId: Int32, query: String, geo: Api.GeoPoint?, peerType: Api.InlineQueryPeerType?, offset: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
@ -7145,6 +7152,12 @@ public extension Api {
serializeInt32(pts, buffer: buffer, boxed: false)
serializeInt32(ptsCount, buffer: buffer, boxed: false)
break
case .updateChat(let chatId):
if boxed {
buffer.appendInt32(321954198)
}
serializeInt32(chatId, buffer: buffer, boxed: false)
break
case .updateGroupCallParticipants(let call, let participants, let version):
if boxed {
buffer.appendInt32(-219423922)
@ -7157,11 +7170,11 @@ public extension Api {
}
serializeInt32(version, buffer: buffer, boxed: false)
break
case .updateGroupCall(let channelId, let call):
case .updateGroupCall(let chatId, let call):
if boxed {
buffer.appendInt32(1462009966)
buffer.appendInt32(-1537295973)
}
serializeInt32(channelId, buffer: buffer, boxed: false)
serializeInt32(chatId, buffer: buffer, boxed: false)
call.serialize(buffer, true)
break
case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset):
@ -7349,10 +7362,12 @@ public extension Api {
return ("updatePinnedMessages", [("flags", flags), ("peer", peer), ("messages", messages), ("pts", pts), ("ptsCount", ptsCount)])
case .updatePinnedChannelMessages(let flags, let channelId, let messages, let pts, let ptsCount):
return ("updatePinnedChannelMessages", [("flags", flags), ("channelId", channelId), ("messages", messages), ("pts", pts), ("ptsCount", ptsCount)])
case .updateChat(let chatId):
return ("updateChat", [("chatId", chatId)])
case .updateGroupCallParticipants(let call, let participants, let version):
return ("updateGroupCallParticipants", [("call", call), ("participants", participants), ("version", version)])
case .updateGroupCall(let channelId, let call):
return ("updateGroupCall", [("channelId", channelId), ("call", call)])
case .updateGroupCall(let chatId, let call):
return ("updateGroupCall", [("chatId", chatId), ("call", call)])
case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset):
return ("updateBotInlineQuery", [("flags", flags), ("queryId", queryId), ("userId", userId), ("query", query), ("geo", geo), ("peerType", peerType), ("offset", offset)])
}
@ -8799,6 +8814,17 @@ public extension Api {
return nil
}
}
public static func parse_updateChat(_ reader: BufferReader) -> Update? {
var _1: Int32?
_1 = reader.readInt32()
let _c1 = _1 != nil
if _c1 {
return Api.Update.updateChat(chatId: _1!)
}
else {
return nil
}
}
public static func parse_updateGroupCallParticipants(_ reader: BufferReader) -> Update? {
var _1: Api.InputGroupCall?
if let signature = reader.readInt32() {
@ -8830,7 +8856,7 @@ public extension Api {
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.Update.updateGroupCall(channelId: _1!, call: _2!)
return Api.Update.updateGroupCall(chatId: _1!, call: _2!)
}
else {
return nil
@ -10990,13 +11016,13 @@ public extension Api {
}
public enum StickerSet: TypeConstructorDescription {
case stickerSet(flags: Int32, installedDate: Int32?, id: Int64, accessHash: Int64, title: String, shortName: String, thumb: Api.PhotoSize?, thumbDcId: Int32?, count: Int32, hash: Int32)
case stickerSet(flags: Int32, installedDate: Int32?, id: Int64, accessHash: Int64, title: String, shortName: String, thumbs: [Api.PhotoSize]?, thumbDcId: Int32?, count: Int32, hash: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .stickerSet(let flags, let installedDate, let id, let accessHash, let title, let shortName, let thumb, let thumbDcId, let count, let hash):
case .stickerSet(let flags, let installedDate, let id, let accessHash, let title, let shortName, let thumbs, let thumbDcId, let count, let hash):
if boxed {
buffer.appendInt32(-290164953)
buffer.appendInt32(1088567208)
}
serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(installedDate!, buffer: buffer, boxed: false)}
@ -11004,7 +11030,11 @@ public extension Api {
serializeInt64(accessHash, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
serializeString(shortName, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 4) != 0 {thumb!.serialize(buffer, true)}
if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(thumbs!.count))
for item in thumbs! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 4) != 0 {serializeInt32(thumbDcId!, buffer: buffer, boxed: false)}
serializeInt32(count, buffer: buffer, boxed: false)
serializeInt32(hash, buffer: buffer, boxed: false)
@ -11014,8 +11044,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .stickerSet(let flags, let installedDate, let id, let accessHash, let title, let shortName, let thumb, let thumbDcId, let count, let hash):
return ("stickerSet", [("flags", flags), ("installedDate", installedDate), ("id", id), ("accessHash", accessHash), ("title", title), ("shortName", shortName), ("thumb", thumb), ("thumbDcId", thumbDcId), ("count", count), ("hash", hash)])
case .stickerSet(let flags, let installedDate, let id, let accessHash, let title, let shortName, let thumbs, let thumbDcId, let count, let hash):
return ("stickerSet", [("flags", flags), ("installedDate", installedDate), ("id", id), ("accessHash", accessHash), ("title", title), ("shortName", shortName), ("thumbs", thumbs), ("thumbDcId", thumbDcId), ("count", count), ("hash", hash)])
}
}
@ -11032,9 +11062,9 @@ public extension Api {
_5 = parseString(reader)
var _6: String?
_6 = parseString(reader)
var _7: Api.PhotoSize?
if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.PhotoSize
var _7: [Api.PhotoSize]?
if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() {
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhotoSize.self)
} }
var _8: Int32?
if Int(_1!) & Int(1 << 4) != 0 {_8 = reader.readInt32() }
@ -11053,7 +11083,7 @@ public extension Api {
let _c9 = _9 != nil
let _c10 = _10 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
return Api.StickerSet.stickerSet(flags: _1!, installedDate: _2, id: _3!, accessHash: _4!, title: _5!, shortName: _6!, thumb: _7, thumbDcId: _8, count: _9!, hash: _10!)
return Api.StickerSet.stickerSet(flags: _1!, installedDate: _2, id: _3!, accessHash: _4!, title: _5!, shortName: _6!, thumbs: _7, thumbDcId: _8, count: _9!, hash: _10!)
}
else {
return nil

View File

@ -7241,12 +7241,12 @@ public extension Api {
})
}
public static func createGroupCall(channel: Api.InputChannel, randomId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
public static func createGroupCall(peer: Api.InputPeer, randomId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(-467076606)
channel.serialize(buffer, true)
buffer.appendInt32(-1120031776)
peer.serialize(buffer, true)
serializeInt32(randomId, buffer: buffer, boxed: false)
return (FunctionDescription(name: "phone.createGroupCall", parameters: [("channel", channel), ("randomId", randomId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
return (FunctionDescription(name: "phone.createGroupCall", parameters: [("peer", peer), ("randomId", randomId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {

View File

@ -280,10 +280,13 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
if case let .peer(peerId) = groupCallPanelSource {
availableGroupCall = context.account.viewTracker.peerView(peerId)
|> map { peerView -> CachedChannelData.ActiveCall? in
guard let cachedData = peerView.cachedData as? CachedChannelData else {
if let cachedData = peerView.cachedData as? CachedChannelData {
return cachedData.activeCall
} else if let cachedData = peerView.cachedData as? CachedGroupData {
return cachedData.activeCall
} else {
return nil
}
return cachedData.activeCall
}
|> distinctUntilChanged
|> mapToSignal { activeCall -> Signal<GroupCallPanelData?, NoError> in

View File

@ -37,6 +37,7 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
case speaker
case airpods
case airpodsPro
case headphones
case accept
case end
}
@ -221,6 +222,8 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAirpodsButton"), color: imageColor)
case .airpodsPro:
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAirpodsProButton"), color: imageColor)
case .headphones:
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallHeadphonesButton"), color: imageColor)
case .accept:
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAcceptButton"), color: imageColor)
case .end:

View File

@ -51,6 +51,7 @@ private enum ButtonDescription: Equatable {
case bluetooth
case airpods
case airpodsPro
case headphones
}
enum EndType {
@ -205,7 +206,7 @@ final class CallControllerButtonsNode: ASDisplayNode {
case .speaker:
soundOutput = .speaker
case .headphones:
soundOutput = .bluetooth
soundOutput = .headphones
case let .bluetooth(type):
switch type {
case .generic:
@ -296,7 +297,7 @@ final class CallControllerButtonsNode: ASDisplayNode {
case .speaker:
soundOutput = .speaker
case .headphones:
soundOutput = .builtin
soundOutput = .headphones
case let .bluetooth(type):
switch type {
case .generic:
@ -467,6 +468,9 @@ final class CallControllerButtonsNode: ASDisplayNode {
case .airpodsPro:
image = .airpodsPro
title = strings.Call_Audio
case .headphones:
image = .headphones
title = strings.Call_Audio
}
buttonContent = CallControllerButtonItemNode.Content(
appearance: .blurred(isFilled: isFilled),

View File

@ -296,7 +296,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
membersText = self.strings.VoiceChat_Panel_Members(Int32(data.participantCount))
}
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }.filter { $0.id != self.context.account.peerId }, animated: false)
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }, animated: false)
self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor)
@ -321,7 +321,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
strongSelf.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: strongSelf.theme.chat.inputPanel.secondaryTextColor)
strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.map { $0.peer }.filter { $0.id != strongSelf.context.account.peerId }, animated: false)
strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.map { $0.peer }, animated: false)
if let (size, leftInset, rightInset) = strongSelf.validLayout {
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate)
@ -400,7 +400,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor)
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }.filter { $0.id != self.context.account.peerId }, animated: false)
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }, animated: false)
updateAudioLevels = true
}

View File

@ -82,8 +82,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|> distinctUntilChanged
}
public var hasActiveGroupCall: Bool {
return self.currentGroupCall != nil
public var hasActiveCall: Bool {
return self.currentCall != nil || self.currentGroupCall != nil
}
private let currentCallPromise = Promise<PresentationCall?>(nil)

View File

@ -20,8 +20,8 @@ private let areaSize = CGSize(width: 440.0, height: 440.0)
private let blobSize = CGSize(width: 244.0, height: 244.0)
final class VoiceChatActionButton: HighlightTrackingButtonNode {
enum State {
enum ActiveState {
enum State: Equatable {
enum ActiveState: Equatable {
case cantSpeak
case muted
case on
@ -31,6 +31,14 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
case active(state: ActiveState)
}
var stateValue: State {
return self.currentParams?.state ?? .connecting
}
var statePromise = ValuePromise<State>()
var state: Signal<State, NoError> {
return self.statePromise.get()
}
private let containerNode: ASDisplayNode
private let backgroundNode: VoiceChatActionButtonBackgroundNode
private let iconNode: VoiceChatMicrophoneNode
@ -167,7 +175,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
let subtitleSize = self.subtitleLabel.updateLayout(CGSize(width: size.width, height: .greatestFiniteMagnitude))
let totalHeight = titleSize.height + subtitleSize.height + 1.0
self.titleLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: floor(size.height - totalHeight / 2.0) - 110.0), size: titleSize)
self.titleLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: floor(size.height - totalHeight / 2.0) - 112.0), size: titleSize)
self.subtitleLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - subtitleSize.width) / 2.0), y: self.titleLabel.frame.maxY + 1.0), size: subtitleSize)
self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
@ -209,7 +217,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
}
private func applyIconParams() {
guard let (size, _, state, _, small, title, subtitle, snap) = self.currentParams else {
guard let (_, _, state, _, _, _, _, snap) = self.currentParams else {
return
}
@ -250,6 +258,8 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
let previousState = previous?.state
self.currentParams = (size, buttonSize, state, dark, small, title, subtitle, previous?.snap ?? false)
self.statePromise.set(state)
var backgroundState: VoiceChatActionButtonBackgroundNode.State
switch state {
case let .active(state):
@ -392,6 +402,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
private var state: State
private var hasState = false
private var transition: State?
var audioLevel: CGFloat = 0.0 {

View File

@ -106,7 +106,7 @@ private final class VoiceChatControllerTitleNode: ASDisplayNode {
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(17.0), textColor: UIColor(rgb: 0xffffff))
self.infoNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(13.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.5))
let constrainedSize = CGSize(width: size.width - 80.0, height: size.height)
let constrainedSize = CGSize(width: size.width - 120.0, height: size.height)
let titleSize = self.titleNode.measure(constrainedSize)
let infoSize = self.infoNode.measure(constrainedSize)
let titleInfoSpacing: CGFloat = 0.0
@ -128,7 +128,6 @@ public final class VoiceChatController: ViewController {
let isEmpty: Bool
let crossFade: Bool
let count: Int
let isExpanded: Bool
let animated: Bool
}
@ -363,14 +362,14 @@ public final class VoiceChatController: ViewController {
}
}
private func preparedTransition(from fromEntries: [ListEntry], to toEntries: [ListEntry], isLoading: Bool, isEmpty: Bool, crossFade: Bool, context: AccountContext, presentationData: PresentationData, interaction: Interaction, isExpanded: Bool) -> ListTransition {
private func preparedTransition(from fromEntries: [ListEntry], to toEntries: [ListEntry], isLoading: Bool, isEmpty: Bool, crossFade: Bool, context: AccountContext, presentationData: PresentationData, interaction: Interaction) -> ListTransition {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) }
return ListTransition(deletions: deletions, insertions: insertions, updates: updates, isLoading: isLoading, isEmpty: isEmpty, crossFade: crossFade, count: toEntries.count, isExpanded: isExpanded, animated: true)
return ListTransition(deletions: deletions, insertions: insertions, updates: updates, isLoading: isLoading, isEmpty: isEmpty, crossFade: crossFade, count: toEntries.count, animated: true)
}
private weak var controller: VoiceChatController?
@ -414,7 +413,6 @@ public final class VoiceChatController: ViewController {
private var currentCallMembers: [GroupCallParticipantsContext.Participant]?
private var currentInvitedPeers: [Peer]?
private var currentSpeakingPeers: Set<PeerId>?
private var currentIsExpanded: Bool = false
private var currentContentOffset: CGFloat?
private var ignoreScrolling = false
private var accountPeer: Peer?
@ -476,6 +474,7 @@ public final class VoiceChatController: ViewController {
self.listNode = ListView()
self.listNode.verticalScrollIndicatorColor = UIColor(white: 1.0, alpha: 0.3)
self.listNode.clipsToBounds = true
self.listNode.scroller.bounces = false
self.topPanelNode = ASDisplayNode()
self.topPanelNode.clipsToBounds = false
@ -886,7 +885,7 @@ public final class VoiceChatController: ViewController {
}
}
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: callMembers?.participants ?? [], invitedPeers: invitedPeers, speakingPeers: callMembers?.speakingParticipants ?? [], isExpanded: strongSelf.currentIsExpanded)
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: callMembers?.participants ?? [], invitedPeers: invitedPeers, speakingPeers: callMembers?.speakingParticipants ?? [])
let subtitle = strongSelf.presentationData.strings.VoiceChat_Panel_Members(Int32(max(1, callMembers?.totalCount ?? 0)))
strongSelf.currentSubtitle = subtitle
@ -907,7 +906,7 @@ public final class VoiceChatController: ViewController {
}
if !strongSelf.didSetDataReady {
strongSelf.accountPeer = accountPeer
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: strongSelf.currentCallMembers ?? [], invitedPeers: strongSelf.currentInvitedPeers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? Set(), isExpanded: strongSelf.currentIsExpanded)
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: strongSelf.currentCallMembers ?? [], invitedPeers: strongSelf.currentInvitedPeers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? Set())
if let peer = peerViewMainPeer(view), let channel = peer as? TelegramChannel {
let addressName = channel.addressName ?? ""
@ -1100,40 +1099,11 @@ public final class VoiceChatController: ViewController {
self.listNode.updateFloatingHeaderOffset = { [weak self] offset, transition in
if let strongSelf = self {
strongSelf.currentContentOffset = offset
strongSelf.updateFloatingHeaderOffset(offset: offset, transition: transition)
}
}
self.listNode.endedInteractiveDragging = { [weak self] in
guard let strongSelf = self else {
return
}
if strongSelf.ignoreScrolling {
Queue.mainQueue().after(0.5) {
strongSelf.ignoreScrolling = false
if strongSelf.animation == nil && !strongSelf.animatingExpansion {
strongSelf.updateFloatingHeaderOffset(offset: offset, transition: transition)
}
}
}
self.listNode.visibleContentOffsetChanged = { [weak self] offset in
guard let strongSelf = self else {
return
}
switch offset {
case let .known(value):
// strongSelf.updateFloatingHeaderOffset(offset: -value + strongSelf.listNode.insets.top, transition: strongSelf.listNode.isTracking ? .immediate : .animated(duration: 0.4, curve: .linear))
if value > 5, !strongSelf.currentIsExpanded && strongSelf.listNode.isTracking && !strongSelf.ignoreScrolling {
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers:strongSelf.currentCallMembers ?? [], invitedPeers: strongSelf.currentInvitedPeers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? Set(), isExpanded: true)
strongSelf.ignoreScrolling = true
} else if value < -5, strongSelf.currentIsExpanded && strongSelf.listNode.isTracking && !strongSelf.ignoreScrolling {
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: strongSelf.currentCallMembers ?? [], invitedPeers: strongSelf.currentInvitedPeers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? [], isExpanded: false)
strongSelf.ignoreScrolling = true
}
default:
break
}
}
}
deinit {
@ -1223,7 +1193,7 @@ public final class VoiceChatController: ViewController {
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring))
}
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? [], invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set(), isExpanded: self.currentIsExpanded)
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? [], invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set())
}
@objc private func actionButtonPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer) {
@ -1268,7 +1238,7 @@ public final class VoiceChatController: ViewController {
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring))
}
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? [], invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set(), isExpanded: self.currentIsExpanded)
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? [], invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set())
default:
break
}
@ -1341,7 +1311,7 @@ public final class VoiceChatController: ViewController {
}
}
private func updateFloatingHeaderOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
private func updateFloatingHeaderOffset(offset: CGFloat, transition: ContainedViewLayoutTransition, completion: (() -> Void)? = nil) {
guard let (layout, _) = self.validLayout else {
return
}
@ -1352,9 +1322,22 @@ public final class VoiceChatController: ViewController {
let bottomAreaHeight: CGFloat = 268.0
let bottomPanelHeight = bottomAreaHeight + layout.intrinsicInsets.bottom
let listSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset - bottomPanelHeight)
let topInset = self.topInset ?? listSize.height
let topInset: CGFloat
if let (panInitialTopInset, panOffset) = self.panGestureArguments {
if self.isExpanded {
topInset = min(self.topInset ?? listSize.height, panInitialTopInset + max(0.0, panOffset))
} else {
topInset = max(0.0, panInitialTopInset + min(0.0, panOffset))
}
} else if let _ = self.animation {
topInset = self.listNode.frame.minY - listTopInset
} else if let currentTopInset = self.topInset {
topInset = self.isExpanded ? 0.0 : currentTopInset
} else {
topInset = listSize.height
}
var offset = offset + topInset
let offset = offset + topInset
self.floatingHeaderOffset = offset
let rawPanelOffset = offset + listTopInset - topPanelHeight
@ -1374,7 +1357,7 @@ public final class VoiceChatController: ViewController {
if !topPanelFrame.equalTo(previousTopPanelFrame) {
self.topPanelNode.frame = topPanelFrame
let positionDelta = CGPoint(x: 0.0, y: topPanelFrame.minY - previousTopPanelFrame.minY)
transition.animateOffsetAdditive(node: self.topPanelNode, offset: positionDelta.y)
transition.animateOffsetAdditive(layer: self.topPanelNode.layer, offset: positionDelta.y, completion: completion)
self.backgroundNode.frame = backgroundFrame
let backgroundPositionDelta = CGPoint(x: 0.0, y: previousBackgroundFrame.minY - backgroundFrame.minY)
@ -1387,6 +1370,8 @@ public final class VoiceChatController: ViewController {
self.rightBorderNode.frame = rightBorderFrame
let rightBorderPositionDelta = CGPoint(x: 0.0, y: previousRightBorderFrame.minY - rightBorderFrame.minY)
transition.animatePositionAdditive(node: self.rightBorderNode, offset: rightBorderPositionDelta)
} else {
completion?()
}
self.topPanelBackgroundNode.frame = CGRect(x: 0.0, y: topPanelHeight - 24.0, width: layout.size.width, height: 24.0)
@ -1422,19 +1407,17 @@ public final class VoiceChatController: ViewController {
}
var isFullscreen = false
func updateColors(fullscreen: Bool) {
guard self.isFullscreen != fullscreen, let (layout, _) = self.validLayout else {
func updateIsFullscreen(_ isFullscreen: Bool) {
guard self.isFullscreen != isFullscreen, let (layout, _) = self.validLayout else {
return
}
self.isFullscreen = fullscreen
self.controller?.statusBar.statusBarStyle = fullscreen ? .White : .Ignore
let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .linear)
self.isFullscreen = isFullscreen
self.controller?.statusBar.statusBarStyle = isFullscreen ? .White : .Ignore
let topPanelHeight: CGFloat = 63.0
let topEdgeFrame: CGRect
if self.isFullscreen {
if isFullscreen {
let offset: CGFloat
if let statusBarHeight = layout.statusBarHeight {
offset = statusBarHeight
@ -1445,16 +1428,17 @@ public final class VoiceChatController: ViewController {
} else {
topEdgeFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: topPanelHeight)
}
let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .linear)
transition.updateFrame(node: self.topPanelEdgeNode, frame: topEdgeFrame)
transition.updateCornerRadius(node: self.topPanelEdgeNode, cornerRadius: fullscreen ? layout.deviceMetrics.screenCornerRadius - 0.5 : 12.0)
// transition.updateBackgroundColor(node: self.dimNode, color: fullscreen ? fullscreenBackgroundColor : dimColor)
transition.updateBackgroundColor(node: self.topPanelBackgroundNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
transition.updateBackgroundColor(node: self.topPanelEdgeNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
transition.updateBackgroundColor(node: self.backgroundNode, color: fullscreen ? panelBackgroundColor : secondaryPanelBackgroundColor)
transition.updateBackgroundColor(node: self.bottomPanelBackgroundNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
transition.updateBackgroundColor(node: self.leftBorderNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
transition.updateBackgroundColor(node: self.rightBorderNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
transition.updateBackgroundColor(node: self.rightBorderNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
transition.updateCornerRadius(node: self.topPanelEdgeNode, cornerRadius: isFullscreen ? layout.deviceMetrics.screenCornerRadius - 0.5 : 12.0)
transition.updateBackgroundColor(node: self.topPanelBackgroundNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
transition.updateBackgroundColor(node: self.topPanelEdgeNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
transition.updateBackgroundColor(node: self.backgroundNode, color: isFullscreen ? panelBackgroundColor : secondaryPanelBackgroundColor)
transition.updateBackgroundColor(node: self.bottomPanelBackgroundNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
transition.updateBackgroundColor(node: self.leftBorderNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
transition.updateBackgroundColor(node: self.rightBorderNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
transition.updateBackgroundColor(node: self.rightBorderNode, color: isFullscreen ? fullscreenBackgroundColor : panelBackgroundColor)
if let snapshotView = self.topCornersNode.view.snapshotContentTree() {
snapshotView.frame = self.topCornersNode.frame
@ -1464,7 +1448,7 @@ public final class VoiceChatController: ViewController {
snapshotView?.removeFromSuperview()
})
}
self.topCornersNode.image = cornersImage(top: true, bottom: false, dark: fullscreen)
self.topCornersNode.image = cornersImage(top: true, bottom: false, dark: isFullscreen)
if let snapshotView = self.bottomCornersNode.view.snapshotContentTree() {
snapshotView.frame = self.bottomCornersNode.bounds
@ -1474,10 +1458,10 @@ public final class VoiceChatController: ViewController {
snapshotView?.removeFromSuperview()
})
}
self.bottomCornersNode.image = cornersImage(top: false, bottom: true, dark: fullscreen)
self.bottomCornersNode.image = cornersImage(top: false, bottom: true, dark: isFullscreen)
self.optionsButton.setImage(optionsButtonImage(dark: fullscreen), animated: transition.isAnimated)
self.closeButton.setImage(closeButtonImage(dark: fullscreen), animated: transition.isAnimated)
self.optionsButton.setImage(optionsButtonImage(dark: isFullscreen), animated: transition.isAnimated)
self.closeButton.setImage(closeButtonImage(dark: isFullscreen), animated: transition.isAnimated)
self.updateTitle(transition: transition)
}
@ -1536,6 +1520,7 @@ public final class VoiceChatController: ViewController {
let soundImage: CallControllerButtonItemNode.Content.Image
var soundAppearance: CallControllerButtonItemNode.Content.Appearance = audioButtonAppearance
var soundTitle: String = self.presentationData.strings.Call_Speaker
switch audioMode {
case .none, .builtin:
soundImage = .speaker
@ -1543,7 +1528,8 @@ public final class VoiceChatController: ViewController {
soundImage = .speaker
soundAppearance = .blurred(isFilled: true)
case .headphones:
soundImage = .bluetooth
soundImage = .headphones
soundTitle = self.presentationData.strings.Call_Audio
case let .bluetooth(type):
switch type {
case .generic:
@ -1553,10 +1539,11 @@ public final class VoiceChatController: ViewController {
case .airpodsPro:
soundImage = .airpodsPro
}
soundTitle = self.presentationData.strings.Call_Audio
}
let sideButtonSize = CGSize(width: 60.0, height: 60.0)
self.audioOutputNode.update(size: sideButtonSize, content: CallControllerButtonItemNode.Content(appearance: soundAppearance, image: soundImage), text: self.presentationData.strings.VoiceChat_Audio, transition: .animated(duration: 0.3, curve: .linear))
self.audioOutputNode.update(size: sideButtonSize, content: CallControllerButtonItemNode.Content(appearance: soundAppearance, image: soundImage), text: soundTitle, transition: .animated(duration: 0.3, curve: .linear))
self.leaveNode.update(size: sideButtonSize, content: CallControllerButtonItemNode.Content(appearance: .color(.custom(0x602522)), image: .end), text: self.presentationData.strings.VoiceChat_Leave, transition: .immediate)
}
@ -1583,25 +1570,43 @@ public final class VoiceChatController: ViewController {
insets.right = layout.safeInsets.right + sideInset
let topPanelHeight: CGFloat = 63.0
let topEdgeFrame: CGRect
if self.isFullscreen {
let offset: CGFloat
if let statusBarHeight = layout.statusBarHeight {
offset = statusBarHeight
} else {
offset = 44.0
}
topEdgeFrame = CGRect(x: 0.0, y: -offset, width: layout.size.width, height: topPanelHeight + offset)
if let _ = self.panGestureArguments {
} else {
topEdgeFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: topPanelHeight)
let topEdgeFrame: CGRect
if self.isFullscreen {
let offset: CGFloat
if let statusBarHeight = layout.statusBarHeight {
offset = statusBarHeight
} else {
offset = 44.0
}
topEdgeFrame = CGRect(x: 0.0, y: -offset, width: layout.size.width, height: topPanelHeight + offset)
} else {
topEdgeFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: topPanelHeight)
}
transition.updateFrame(node: self.topPanelEdgeNode, frame: topEdgeFrame)
}
transition.updateFrame(node: self.topPanelEdgeNode, frame: topEdgeFrame)
let bottomPanelHeight = bottomAreaHeight + layout.intrinsicInsets.bottom
let listTopInset = layoutTopInset + topPanelHeight
let listSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset - bottomPanelHeight)
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(x: 0.0, y: listTopInset + (self.topInset ?? listSize.height)), size: listSize))
let topInset: CGFloat
if let (panInitialTopInset, panOffset) = self.panGestureArguments {
if self.isExpanded {
topInset = min(self.topInset ?? listSize.height, panInitialTopInset + max(0.0, panOffset))
} else {
topInset = max(0.0, panInitialTopInset + min(0.0, panOffset))
}
} else if let currentTopInset = self.topInset {
topInset = self.isExpanded ? 0.0 : currentTopInset
} else {
topInset = listSize.height
}
if self.animation == nil {
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(x: 0.0, y: listTopInset + topInset), size: listSize))
}
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listSize, insets: insets, duration: duration, curve: curve)
@ -1695,7 +1700,7 @@ public final class VoiceChatController: ViewController {
}, completion: { _ in
if self.actionButton.supernode !== self.bottomPanelNode {
self.bottomPanelNode.addSubnode(self.actionButton)
self.containerLayoutUpdated(layout, navigationHeight:navigationHeight, transition: .immediate)
self.containerLayoutUpdated(layout, navigationHeight :navigationHeight, transition: .immediate)
}
self.controller?.currentOverlayController?.dismiss()
@ -1778,24 +1783,24 @@ public final class VoiceChatController: ViewController {
let listTopInset = layoutTopInset + 63.0
let listSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset - bottomPanelHeight)
let previousIsExpanded = self.currentIsExpanded
self.currentIsExpanded = transition.isExpanded
self.topInset = max(0.0, transition.isExpanded ? 0.0 : max(listSize.height - itemsHeight, listSize.height - 46.0 - floor(56.0 * 3.5)))
self.topInset = max(0.0, max(listSize.height - itemsHeight, listSize.height - 46.0 - floor(56.0 * 3.5)))
let frameTransition: ContainedViewLayoutTransition
if previousIsExpanded != self.currentIsExpanded {
frameTransition = .animated(duration: 0.4, curve: .spring)
} else {
frameTransition = .animated(duration: 0.4, curve: .easeInOut)
if !self.isExpanded {
let targetY = listTopInset + (self.topInset ?? listSize.height)
if self.listNode.frame.minY != targetY && !self.animatingExpansion && self.panGestureArguments == nil {
self.animation = ListViewAnimation(from: self.listNode.frame.minY, to: targetY, duration: 0.4, curve: listViewAnimationCurveEaseInOut, beginAt: CACurrentMediaTime(), update: { [weak self] _, currentValue in
if let strongSelf = self {
var frame = strongSelf.listNode.frame
frame.origin.y = currentValue
strongSelf.listNode.frame = frame
strongSelf.updateFloatingHeaderOffset(offset: strongSelf.currentContentOffset ?? 0.0, transition: .immediate)
}
})
self.updateAnimation()
}
}
frameTransition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(x: 0.0, y: listTopInset + (self.topInset ?? listSize.height)), size: listSize))
let (duration, curve) = listViewAnimationDurationAndCurve(transition: frameTransition)
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listSize, insets: insets, duration: duration, curve: curve)
self.updateColors(fullscreen: transition.isExpanded)
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: nil, updateSizeAndInsets: updateSizeAndInsets, updateOpaqueState: nil, completion: { [weak self] _ in
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: nil, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
@ -1804,12 +1809,42 @@ public final class VoiceChatController: ViewController {
strongSelf.controller?.contentsReady.set(true)
}
})
if previousIsExpanded != self.currentIsExpanded {
self.updateFloatingHeaderOffset(offset: self.currentContentOffset ?? 0.0, transition: frameTransition)
}
private var animator: ConstantDisplayLinkAnimator?
private var animation: ListViewAnimation?
private func updateAnimation() {
var animate = false
let timestamp = CACurrentMediaTime()
if let animation = self.animation {
animation.applyAt(timestamp)
if animation.completeAt(timestamp) {
self.animation = nil
} else {
animate = true
}
}
if animate {
let animator: ConstantDisplayLinkAnimator
if let current = self.animator {
animator = current
} else {
animator = ConstantDisplayLinkAnimator(update: { [weak self] in
self?.updateAnimation()
})
self.animator = animator
}
animator.isPaused = false
} else {
self.animator?.isPaused = true
}
}
private func updateMembers(muteState: GroupCallParticipantsContext.Participant.MuteState?, callMembers: [GroupCallParticipantsContext.Participant], invitedPeers: [Peer], speakingPeers: Set<PeerId>, isExpanded: Bool) {
private func updateMembers(muteState: GroupCallParticipantsContext.Participant.MuteState?, callMembers: [GroupCallParticipantsContext.Participant], invitedPeers: [Peer], speakingPeers: Set<PeerId>) {
self.currentCallMembers = callMembers
self.currentSpeakingPeers = speakingPeers
self.currentInvitedPeers = invitedPeers
@ -1874,7 +1909,7 @@ public final class VoiceChatController: ViewController {
self.currentEntries = entries
let presentationData = self.presentationData.withUpdated(theme: self.darkTheme)
let transition = preparedTransition(from: previousEntries, to: entries, isLoading: false, isEmpty: false, crossFade: false, context: self.context, presentationData: presentationData, interaction: self.itemInteraction!, isExpanded: isExpanded ?? self.currentIsExpanded)
let transition = preparedTransition(from: previousEntries, to: entries, isLoading: false, isEmpty: false, crossFade: false, context: self.context, presentationData: presentationData, interaction: self.itemInteraction!)
self.enqueueTransition(transition)
}
@ -1882,56 +1917,190 @@ public final class VoiceChatController: ViewController {
// if let callState = self.callState, case .connected = callState.networkState, let muteState = callState.muteState, !muteState.canUnmute {
// return false
// }
if let recognizer = gestureRecognizer as? UIPanGestureRecognizer {
let location = recognizer.location(in: self.view)
if let view = super.hitTest(location, with: nil) {
if let gestureRecognizers = view.gestureRecognizers, view != self.view {
for gestureRecognizer in gestureRecognizers {
if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer, gestureRecognizer.isEnabled {
if panGestureRecognizer.state != .began {
panGestureRecognizer.isEnabled = false
panGestureRecognizer.isEnabled = true
}
}
}
}
}
}
// if let recognizer = gestureRecognizer as? UIPanGestureRecognizer {
// let location = recognizer.location(in: self.view)
// if let view = super.hitTest(location, with: nil) {
// if let gestureRecognizers = view.gestureRecognizers, view != self.view {
// for gestureRecognizer in gestureRecognizers {
// if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer, gestureRecognizer.isEnabled {
// if panGestureRecognizer.state != .began {
// panGestureRecognizer.isEnabled = false
// panGestureRecognizer.isEnabled = true
// }
// }
// }
// }
// }
// }
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer {
return true
}
return false
}
private var isExpanded = false
private var animatingExpansion = false
private var panGestureArguments: (topInset: CGFloat, offset: CGFloat)?
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
break
let topInset: CGFloat
if self.isExpanded {
topInset = 0.0
} else if let currentTopInset = self.topInset {
topInset = currentTopInset
} else {
topInset = self.listNode.frame.height
}
self.panGestureArguments = (topInset, 0.0)
case .changed:
var translation = recognizer.translation(in: self.contentContainer.view).y
var topInset: CGFloat = 0.0
if let (currentTopInset, currentPanOffset) = self.panGestureArguments {
topInset = currentTopInset
if case let .known(value) = self.listNode.visibleContentOffset(), value > 0 {
translation = currentPanOffset
if self.isExpanded {
recognizer.setTranslation(CGPoint(), in: self.contentContainer.view)
}
}
self.panGestureArguments = (currentTopInset, translation)
}
let currentOffset = topInset + translation
if currentOffset < 20.0 {
self.updateIsFullscreen(true)
} else if currentOffset > 40.0 {
self.updateIsFullscreen(false)
}
if self.isExpanded {
} else {
if currentOffset > 0.0 {
self.listNode.scroller.panGestureRecognizer.setTranslation(CGPoint(), in: self.listNode.scroller)
}
}
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .immediate)
self.updateFloatingHeaderOffset(offset: self.currentContentOffset ?? 0.0, transition: .immediate)
}
if !self.isExpanded {
var bounds = self.contentContainer.bounds
bounds.origin.y = -translation
bounds.origin.y = min(0.0, bounds.origin.y)
self.contentContainer.bounds = bounds
}
case .ended:
let translation = recognizer.translation(in: self.contentContainer.view)
var velocity = recognizer.velocity(in: self.contentContainer.view)
if case let .known(value) = self.listNode.visibleContentOffset(), value > 0 {
velocity = CGPoint()
}
var bounds = self.contentContainer.bounds
bounds.origin.y = -translation.y
bounds.origin.y = min(0.0, bounds.origin.y)
self.contentContainer.bounds = bounds
case .ended:
let translation = recognizer.translation(in: self.contentContainer.view)
var bounds = self.contentContainer.bounds
bounds.origin.y = -translation.y
let velocity = recognizer.velocity(in: self.contentContainer.view)
if (bounds.minY < -60.0 || velocity.y > 300.0) {
self.controller?.dismiss(closing: false)
let offset: CGFloat
if let (inset, panOffset) = self.panGestureArguments {
offset = inset + panOffset
} else {
var bounds = self.contentContainer.bounds
let previousBounds = bounds
bounds.origin.y = 0.0
self.contentContainer.bounds = bounds
self.contentContainer.layer.animateBounds(from: previousBounds, to: self.contentContainer.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
offset = 0.0
}
let topInset: CGFloat
if let currentTopInset = self.topInset {
topInset = currentTopInset
} else {
topInset = self.listNode.frame.height
}
if self.isExpanded {
self.panGestureArguments = nil
if velocity.y > 300.0 || offset > topInset / 2.0 {
self.isExpanded = false
self.updateIsFullscreen(false)
self.animatingExpansion = true
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
}
self.updateFloatingHeaderOffset(offset: self.currentContentOffset ?? 0.0, transition: .animated(duration: 0.3, curve: .easeInOut), completion: {
self.animatingExpansion = false
})
} else {
self.updateIsFullscreen(true)
self.animatingExpansion = true
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
}
self.updateFloatingHeaderOffset(offset: self.currentContentOffset ?? 0.0, transition: .animated(duration: 0.3, curve: .easeInOut), completion: {
self.animatingExpansion = false
})
}
} else {
self.panGestureArguments = nil
var dismissing = false
if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) {
self.controller?.dismiss(closing: false)
dismissing = true
} else if velocity.y < -300.0 || offset < topInset / 2.0 {
self.isExpanded = true
self.updateIsFullscreen(true)
self.animatingExpansion = true
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
}
self.updateFloatingHeaderOffset(offset: self.currentContentOffset ?? 0.0, transition: .animated(duration: 0.3, curve: .easeInOut), completion: {
self.animatingExpansion = false
})
} else {
self.updateIsFullscreen(false)
self.animatingExpansion = true
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
}
self.updateFloatingHeaderOffset(offset: self.currentContentOffset ?? 0.0, transition: .animated(duration: 0.3, curve: .easeInOut), completion: {
self.animatingExpansion = false
})
}
if !dismissing {
var bounds = self.contentContainer.bounds
let previousBounds = bounds
bounds.origin.y = 0.0
self.contentContainer.bounds = bounds
self.contentContainer.layer.animateBounds(from: previousBounds, to: self.contentContainer.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
}
}
case .cancelled:
self.panGestureArguments = nil
let previousBounds = self.contentContainer.bounds
var bounds = self.contentContainer.bounds
bounds.origin.y = 0.0
self.contentContainer.bounds = bounds
self.contentContainer.layer.animateBounds(from: previousBounds, to: self.contentContainer.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
}
self.updateFloatingHeaderOffset(offset: self.currentContentOffset ?? 0.0, transition: .animated(duration: 0.3, curve: .easeInOut), completion: {
self.animatingExpansion = false
})
default:
break
}
@ -1941,7 +2110,7 @@ public final class VoiceChatController: ViewController {
let result = super.hitTest(point, with: event)
if result === self.topPanelNode.view {
return self.listNode.view
return self.view
}
if result === self.bottomPanelNode.view {
@ -2071,7 +2240,11 @@ public final class VoiceChatController: ViewController {
if let navigationController = self.navigationController as? NavigationController {
let count = navigationController.viewControllers.count
if count == 2 || navigationController.viewControllers[count - 2] is ChatController {
self.detachActionButton()
if case .active(.cantSpeak) = self.controllerNode.actionButton.stateValue {
} else if let chatController = navigationController.viewControllers[count - 2] as? ChatController, chatController.isSendButtonVisible {
} else {
self.detachActionButton()
}
}
}
} else {

View File

@ -53,6 +53,7 @@ public final class VoiceChatOverlayController: ViewController {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
if hidden {
if slide {
actionButton.isHidden = false
transition.updateSublayerTransformOffset(layer: actionButton.layer, offset: CGPoint(x: slideOffset, y: 0.0))
} else {
actionButton.layer.removeAllAnimations()
@ -64,10 +65,10 @@ public final class VoiceChatOverlayController: ViewController {
}
} else {
actionButton.isHidden = false
actionButton.layer.removeAllAnimations()
if slide {
transition.updateSublayerTransformOffset(layer: actionButton.layer, offset: CGPoint())
} else {
actionButton.layer.removeAllAnimations()
actionButton.layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4)
}
}
@ -91,10 +92,13 @@ public final class VoiceChatOverlayController: ViewController {
return
}
actionButton.update(snap: true, animated: !self.isSlidOffscreen)
actionButton.update(snap: true, animated: !self.isSlidOffscreen && !self.isButtonHidden)
if self.isSlidOffscreen {
actionButton.layer.sublayerTransform = CATransform3DMakeTranslation(slideOffset, 0.0, 0.0)
return
} else if self.isButtonHidden {
actionButton.isHidden = true
return
}
let targetPosition = actionButton.position
@ -125,12 +129,14 @@ public final class VoiceChatOverlayController: ViewController {
}
private var animating = false
private var dismissed = false
func animateOut(reclaim: Bool, completion: @escaping (Bool) -> Void) {
guard let actionButton = self.controller?.actionButton, let layout = self.validLayout else {
return
}
if reclaim {
self.dismissed = true
let targetPosition = CGPoint(x: layout.size.width / 2.0, y: layout.size.height - layout.intrinsicInsets.bottom - 268.0 / 2.0)
if self.isSlidOffscreen {
self.isSlidOffscreen = false
@ -139,6 +145,13 @@ public final class VoiceChatOverlayController: ViewController {
actionButton.update(snap: false, animated: false)
actionButton.position = CGPoint(x: targetPosition.x, y: 268.0 / 2.0)
completion(true)
} else if self.isButtonHidden {
actionButton.isHidden = false
actionButton.layer.removeAllAnimations()
actionButton.layer.sublayerTransform = CATransform3DIdentity
actionButton.update(snap: false, animated: false)
actionButton.position = CGPoint(x: targetPosition.x, y: 268.0 / 2.0)
completion(true)
} else {
self.animating = true
let sourcePoint = actionButton.position
@ -193,17 +206,18 @@ public final class VoiceChatOverlayController: ViewController {
return nil
}
private var didAnimateIn = false
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.validLayout = layout
if let actionButton = self.controller?.actionButton, !self.animating {
if let actionButton = self.controller?.actionButton, !self.animating && !self.dismissed {
let convertedRect = actionButton.view.convert(actionButton.bounds, to: self.view)
let insets = layout.insets(options: [.input])
transition.updatePosition(node: actionButton, position: CGPoint(x: layout.size.width - layout.safeInsets.right - 21.0, y: layout.size.height - insets.bottom - 22.0))
if actionButton.supernode !== self {
if actionButton.supernode !== self && !self.didAnimateIn {
self.didAnimateIn = true
self.addSubnode(actionButton)
self.animateIn(from: convertedRect)
}
}
@ -224,15 +238,18 @@ public final class VoiceChatOverlayController: ViewController {
super.init(navigationBarPresentationData: nil)
self.statusBar.statusBarStyle = .Ignore
self.additionalSideInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 75.0)
if case .active(.cantSpeak) = actionButton.stateValue {
} else {
self.additionalSideInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 75.0)
}
if let navigationController = navigationController {
let controllers: Signal<[UIViewController], NoError> = .single([])
|> then(navigationController.viewControllersSignal)
let overlayControllers: Signal<[UIViewController], NoError> = .single([])
|> then(navigationController.overlayControllersSignal)
self.disposable = (combineLatest(queue: Queue.mainQueue(), controllers, overlayControllers)).start(next: { [weak self] controllers, overlayControllers in
self.disposable = (combineLatest(queue: Queue.mainQueue(), controllers, overlayControllers, actionButton.state)).start(next: { [weak self] controllers, overlayControllers, state in
if let strongSelf = self {
var hasVoiceChatController = false
var overlayControllersCount = 0
@ -248,19 +265,37 @@ public final class VoiceChatOverlayController: ViewController {
}
}
var slide = true
var hidden = true
var animated = true
if controllers.count == 1 || controllers.last is ChatController {
hidden = false
if let chatController = controllers.last as? ChatController, chatController.isSendButtonVisible {
slide = false
animated = false
} else {
hidden = false
}
}
if overlayControllersCount > 0 {
hidden = true
}
if case .active(.cantSpeak) = state {
hidden = true
}
if hasVoiceChatController {
hidden = false
animated = false
}
strongSelf.controllerNode.update(hidden: hidden, slide: true, animated: animated)
strongSelf.controllerNode.update(hidden: hidden, slide: slide, animated: animated)
let previousInsets = strongSelf.additionalSideInsets
strongSelf.additionalSideInsets = hidden ? UIEdgeInsets() : UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 75.0)
if previousInsets != strongSelf.additionalSideInsets {
navigationController.requestLayout(transition: .animated(duration: 0.3, curve: .easeInOut))
}
}
})
}

View File

@ -1001,6 +1001,8 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
updatedState.readOutbox(MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), namespace: Namespaces.Message.Cloud, id: maxId), timestamp: nil)
case let .updateChannel(channelId):
updatedState.addExternallyUpdatedPeerId(PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId))
case let .updateChat(chatId):
updatedState.addExternallyUpdatedPeerId(PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId))
case let .updateReadHistoryInbox(_, folderId, peer, maxId, stillUnreadCount, pts, _):
updatedState.resetIncomingReadState(groupId: PeerGroupId(rawValue: folderId ?? 0), peerId: peer.peerId, namespace: Namespaces.Message.Cloud, maxIncomingReadId: maxId, count: stillUnreadCount, pts: pts)
case let .updateReadHistoryOutbox(peer, maxId, _, _):
@ -1334,6 +1336,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
}
case let .updateGroupCall(channelId, call):
updatedState.updateGroupCall(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), call: call)
updatedState.updateGroupCall(peerId: PeerId(namespace: Namespaces.Peer.CloudGroup, id: channelId), call: call)
case let .updateLangPackTooLong(langCode):
updatedState.updateLangPack(langCode: langCode, difference: nil)
case let .updateLangPack(difference):
@ -2966,6 +2969,8 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
if let current = current as? CachedChannelData {
return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
} else if let current = current as? CachedGroupData {
return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
} else {
return current
}
@ -2997,6 +3002,12 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
} else {
return current
}
} else if let current = current as? CachedGroupData {
if let activeCall = current.activeCall, activeCall.id == callId {
return current.withUpdatedActiveCall(nil)
} else {
return current
}
} else {
return current
}

View File

@ -126,8 +126,8 @@ public enum CreateGroupCallError {
}
public func createGroupCall(account: Account, peerId: PeerId) -> Signal<GroupCallInfo, CreateGroupCallError> {
return account.postbox.transaction { transaction -> Api.InputChannel? in
return transaction.getPeer(peerId).flatMap(apiInputChannel)
return account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(peerId).flatMap(apiInputPeer)
}
|> castError(CreateGroupCallError.self)
|> mapToSignal { inputPeer -> Signal<GroupCallInfo, CreateGroupCallError> in
@ -135,7 +135,7 @@ public func createGroupCall(account: Account, peerId: PeerId) -> Signal<GroupCal
return .fail(.generic)
}
return account.network.request(Api.functions.phone.createGroupCall(channel: inputPeer, randomId: Int32.random(in: Int32.min ... Int32.max)))
return account.network.request(Api.functions.phone.createGroupCall(peer: inputPeer, randomId: Int32.random(in: Int32.min ... Int32.max)))
|> mapError { error -> CreateGroupCallError in
if error.errorDescription == "ANONYMOUS_CALLS_DISABLED" {
return .anonymousNotAllowed
@ -162,6 +162,8 @@ public func createGroupCall(account: Account, peerId: PeerId) -> Signal<GroupCal
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
if let cachedData = cachedData as? CachedChannelData {
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash))
} else if let cachedData = cachedData as? CachedGroupData {
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash))
} else {
return cachedData
}
@ -283,23 +285,71 @@ public func joinGroupCall(account: Account, peerId: PeerId, callId: Int64, acces
return .generic
}
|> mapToSignal { updates -> Signal<JoinGroupCallResult, JoinGroupCallError> in
let admins = account.postbox.transaction { transaction -> Api.InputChannel? in
return transaction.getPeer(peerId).flatMap(apiInputChannel)
}
|> castError(JoinGroupCallError.self)
|> mapToSignal { inputChannel -> Signal<Api.channels.ChannelParticipants, JoinGroupCallError> in
guard let inputChannel = inputChannel else {
return .fail(.generic)
let admins: Signal<(Set<PeerId>, [Api.User]), JoinGroupCallError>
if peerId.namespace == Namespaces.Peer.CloudChannel {
admins = account.postbox.transaction { transaction -> Api.InputChannel? in
return transaction.getPeer(peerId).flatMap(apiInputChannel)
}
return account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsAdmins, offset: 0, limit: 100, hash: 0))
|> mapError { _ -> JoinGroupCallError in
return .generic
|> castError(JoinGroupCallError.self)
|> mapToSignal { inputChannel -> Signal<Api.channels.ChannelParticipants, JoinGroupCallError> in
guard let inputChannel = inputChannel else {
return .fail(.generic)
}
return account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsAdmins, offset: 0, limit: 100, hash: 0))
|> mapError { _ -> JoinGroupCallError in
return .generic
}
}
|> map { admins -> (Set<PeerId>, [Api.User]) in
var adminIds = Set<PeerId>()
var apiUsers: [Api.User] = []
switch admins {
case let .channelParticipants(_, participants, users):
apiUsers.append(contentsOf: users)
for participant in participants {
let parsedParticipant = ChannelParticipant(apiParticipant: participant)
switch parsedParticipant {
case .creator:
adminIds.insert(parsedParticipant.peerId)
case let .member(_, _, adminInfo, _, _):
if let adminInfo = adminInfo, adminInfo.rights.flags.contains(.canManageCalls) {
adminIds.insert(parsedParticipant.peerId)
}
}
}
default:
break
}
return (adminIds, apiUsers)
}
} else if peerId.namespace == Namespaces.Peer.CloudGroup {
admins = account.postbox.transaction { transaction -> (Set<PeerId>, [Api.User]) in
var result = Set<PeerId>()
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedGroupData {
if let participants = cachedData.participants {
for participant in participants.participants {
if case .creator = participant {
result.insert(participant.peerId)
} else if case .admin = participant {
result.insert(participant.peerId)
}
}
}
}
return (result, [])
}
|> castError(JoinGroupCallError.self)
} else {
admins = .fail(.generic)
}
let channel = account.postbox.transaction { transaction -> TelegramChannel? in
return transaction.getPeer(peerId) as? TelegramChannel
let peer = account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(peerId)
}
|> castError(JoinGroupCallError.self)
@ -313,15 +363,23 @@ public func joinGroupCall(account: Account, peerId: PeerId, callId: Int64, acces
return .generic
},
admins,
channel
peer
)
|> mapToSignal { result, state, admins, channel -> Signal<JoinGroupCallResult, JoinGroupCallError> in
guard let channel = channel else {
|> mapToSignal { result, state, admins, peer -> Signal<JoinGroupCallResult, JoinGroupCallError> in
guard let peer = peer else {
return .fail(.generic)
}
var state = state
state.isCreator = channel.flags.contains(.isCreator)
if let channel = peer as? TelegramChannel {
state.isCreator = channel.flags.contains(.isCreator)
} else if let group = peer as? TelegramGroup {
if case .creator = group.role {
state.isCreator = true
} else {
state.isCreator = false
}
}
account.stateManager.addUpdates(updates)
@ -351,26 +409,9 @@ public func joinGroupCall(account: Account, peerId: PeerId, callId: Int64, acces
}
var apiUsers: [Api.User] = []
var adminIds = Set<PeerId>()
switch admins {
case let .channelParticipants(_, participants, users):
apiUsers.append(contentsOf: users)
for participant in participants {
let parsedParticipant = ChannelParticipant(apiParticipant: participant)
switch parsedParticipant {
case .creator:
adminIds.insert(parsedParticipant.peerId)
case let .member(_, _, adminInfo, _, _):
if let adminInfo = adminInfo, adminInfo.rights.flags.contains(.canManageCalls) {
adminIds.insert(parsedParticipant.peerId)
}
}
}
default:
break
}
let (adminIds, adminUsers) = admins
apiUsers.append(contentsOf: adminUsers)
state.adminIds = adminIds
@ -440,6 +481,8 @@ public func stopGroupCall(account: Account, peerId: PeerId, callId: Int64, acces
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
if let cachedData = cachedData as? CachedChannelData {
return cachedData.withUpdatedActiveCall(nil)
} else if let cachedData = cachedData as? CachedGroupData {
return cachedData.withUpdatedActiveCall(nil)
} else {
return cachedData
}

View File

@ -50,7 +50,7 @@ extension StickerPackCollectionInfo {
}
var thumbnailRepresentation: TelegramMediaImageRepresentation?
if let thumb = thumb, let thumbDcId = thumbDcId {
if let thumb = thumb?.first, let thumbDcId = thumbDcId {
thumbnailRepresentation = telegramStickerPackThumbnailRepresentationFromApiSize(datacenterId: thumbDcId, size: thumb)
}

View File

@ -292,6 +292,14 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
hasScheduledMessages = true
}
var updatedActiveCall: CachedChannelData.ActiveCall?
if let inputCall = chatFull.call {
switch inputCall {
case let .inputGroupCall(id, accessHash):
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash)
}
}
transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in
let previous: CachedGroupData
if let current = current as? CachedGroupData {
@ -309,6 +317,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
.withUpdatedHasScheduledMessages(hasScheduledMessages)
.withUpdatedInvitedBy(invitedBy)
.withUpdatedPhoto(photo)
.withUpdatedActiveCall(updatedActiveCall)
})
case .channelFull:
break

View File

@ -251,6 +251,8 @@ extension Api.Update {
switch self {
case let .updateChannel(channelId):
return [PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)]
case let .updateChat(chatId):
return [PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId)]
case let .updateChannelTooLong(_, channelId, _):
return [PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)]
case let .updateChatParticipantAdd(chatId, userId, inviterId, _, _):

View File

@ -405,13 +405,14 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
}
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
case let .groupPhoneCall(_, _, duration):
let titleString: String
if let duration = duration {
titleString = strings.Notification_VoiceChatEnded(callDurationString(strings: strings, value: duration)).0
let titleString = strings.Notification_VoiceChatEnded(callDurationString(strings: strings, value: duration)).0
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
} else {
titleString = strings.Notification_VoiceChatStarted
var attributePeerIds: [(Int, PeerId?)] = [(0, message.author?.id)]
let titleString = strings.Notification_VoiceChatStarted(authorName)
attributedString = addAttributesToStringWithRanges(titleString, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
}
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
case let .customText(text, entities):
attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false)
case let .botDomainAccessGranted(domain):

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_call_headphones.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -318,7 +318,7 @@ public final class AccountContextImpl: AccountContext {
}
let presentationData = strongSelf.sharedContext.currentPresentationData.with { $0 }
if let current = current {
if current is TelegramChannel {
if current is TelegramChannel || current is TelegramGroup {
strongSelf.sharedContext.mainWindow?.present(textAlertController(context: strongSelf, title: presentationData.strings.Call_VoiceChatInProgressTitle, text: presentationData.strings.Call_VoiceChatInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
guard let strongSelf = self else {
return
@ -364,7 +364,7 @@ public final class AccountContextImpl: AccountContext {
}
let presentationData = strongSelf.sharedContext.currentPresentationData.with { $0 }
if let current = current {
if current is TelegramChannel {
if current is TelegramChannel || current is TelegramGroup {
strongSelf.sharedContext.mainWindow?.present(textAlertController(context: strongSelf, title: presentationData.strings.Call_VoiceChatInProgressTitle, text: presentationData.strings.Call_VoiceChatInProgressCallMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
guard let strongSelf = self else {
return

View File

@ -3753,6 +3753,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
pinnedMessageId = cachedData.pinnedMessageId
} else if let cachedData = combinedInitialData.cachedData as? CachedGroupData {
pinnedMessageId = cachedData.pinnedMessageId
if let activeCall = cachedData.activeCall {
activeGroupCallInfo = ChatActiveGroupCallInfo(activeCall: activeCall)
}
} else if let _ = combinedInitialData.cachedData as? CachedSecretChatData {
}
@ -3908,6 +3911,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
pinnedMessageId = cachedData.pinnedMessageId
} else if let cachedData = cachedData as? CachedGroupData {
pinnedMessageId = cachedData.pinnedMessageId
if let activeCall = cachedData.activeCall {
activeGroupCallInfo = ChatActiveGroupCallInfo(activeCall: activeCall)
}
} else if let _ = cachedData as? CachedSecretChatData {
}
@ -6221,9 +6227,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
let shouldBeActive = combineLatest(self.context.sharedContext.mediaManager.audioSession.isPlaybackActive() |> deliverOnMainQueue, self.chatDisplayNode.historyNode.hasVisiblePlayableItemNodes)
|> mapToSignal { [weak self] isPlaybackActive, hasVisiblePlayableItemNodes -> Signal<Bool, NoError> in
if hasVisiblePlayableItemNodes && !isPlaybackActive {
let hasActiveCalls: Signal<Bool, NoError>
if let callManager = self.context.sharedContext.callManager as? PresentationCallManagerImpl {
hasActiveCalls = callManager.hasActiveCalls
} else {
hasActiveCalls = .single(false)
}
let shouldBeActive = combineLatest(self.context.sharedContext.mediaManager.audioSession.isPlaybackActive() |> deliverOnMainQueue, self.chatDisplayNode.historyNode.hasVisiblePlayableItemNodes, hasActiveCalls)
|> mapToSignal { [weak self] isPlaybackActive, hasVisiblePlayableItemNodes, hasActiveCalls -> Signal<Bool, NoError> in
if hasVisiblePlayableItemNodes && !isPlaybackActive && !hasActiveCalls {
return Signal<Bool, NoError> { [weak self] subscriber in
guard let strongSelf = self else {
subscriber.putCompletion()
@ -7060,7 +7073,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.saveInterfaceState(includeScrollState: false)
}
if let navigationController = self.navigationController as? NavigationController {
if let navigationController = self.navigationController as? NavigationController, self.traceVisibility() && isTopmostChatController(self) {
var voiceChatOverlayController: VoiceChatOverlayController?
for controller in navigationController.globalOverlayControllers {
if let controller = controller as? VoiceChatOverlayController {
@ -7070,11 +7083,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
if let controller = voiceChatOverlayController {
var hidden = false
if self.presentationInterfaceState.interfaceState.editMessage != nil || self.presentationInterfaceState.interfaceState.forwardMessageIds != nil || self.presentationInterfaceState.interfaceState.composeInputState.inputText.string.count > 0 {
hidden = true
}
controller.update(hidden: hidden, slide: false, animated: true)
controller.update(hidden: self.isSendButtonVisible, slide: false, animated: true)
}
}
}
@ -7669,11 +7678,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
}, openCamera: { [weak self] cameraView, menuController in
if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
if let callManager = strongSelf.context.sharedContext.callManager as? PresentationCallManagerImpl, callManager.hasActiveGroupCall {
return
var photoOnly = false
if let callManager = strongSelf.context.sharedContext.callManager as? PresentationCallManagerImpl, callManager.hasActiveCall {
photoOnly = true
}
presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: settings.storeEditedPhotos, mediaGrouping: true, initialCaption: inputText.string, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: settings.storeEditedPhotos, mediaGrouping: true, initialCaption: inputText.string, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, photoOnly: photoOnly, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
if let strongSelf = self {
if editMediaOptions != nil {
strongSelf.editMessageMediaWithLegacySignals(signals!)
@ -11307,6 +11317,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
//return self.chatDisplayNode.acceptEmbeddedTitlePeekContent(content: content)
return false
}
public var isSendButtonVisible: Bool {
if self.presentationInterfaceState.interfaceState.editMessage != nil || self.presentationInterfaceState.interfaceState.forwardMessageIds != nil || self.presentationInterfaceState.interfaceState.composeInputState.inputText.string.count > 0 {
return true
} else {
return false
}
}
}
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {

View File

@ -1236,7 +1236,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
let searchLayoutClearButtonSize = CGSize(width: 44.0, height: minimalHeight)
var textFieldInsets = self.textFieldInsets(metrics: metrics)
if additionalSideInsets.right > 0.0 && self.text.isEmpty {
if additionalSideInsets.right > 0.0 {
textFieldInsets.right += additionalSideInsets.right / 3.0
}
self.actionButtons.micButton.isHidden = additionalSideInsets.right > 0.0

View File

@ -11,7 +11,7 @@ import ShareController
import LegacyUI
import LegacyMediaPickerUI
func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocation: ChatLocation, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: String, hasSchedule: Bool, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?) {
func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocation: ChatLocation, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: String, hasSchedule: Bool, photoOnly: Bool, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?) {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)
@ -23,7 +23,7 @@ func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocation: Ch
let controller: TGCameraController
if let cameraView = cameraView, let previewView = cameraView.previewView() {
controller = TGCameraController(context: legacyController.context, saveEditedPhotos: saveCapturedPhotos && !isSecretChat, saveCapturedMedia: saveCapturedPhotos && !isSecretChat, camera: previewView.camera, previewView: previewView, intent: TGCameraControllerGenericIntent)
controller = TGCameraController(context: legacyController.context, saveEditedPhotos: saveCapturedPhotos && !isSecretChat, saveCapturedMedia: saveCapturedPhotos && !isSecretChat, camera: previewView.camera, previewView: previewView, intent: photoOnly ? TGCameraControllerGenericPhotoOnlyIntent : TGCameraControllerGenericIntent)
controller.inhibitMultipleCapture = editingMedia
} else {
controller = TGCameraController()

View File

@ -109,6 +109,7 @@ public final class NotificationContainerController: ViewController {
let toAlpha: CGFloat = value ? 0.0 : 1.0
self.controllerNode.alpha = toAlpha
self.controllerNode.layer.animateAlpha(from: fromAlpha, to: toAlpha, duration: 0.2)
self.controllerNode.isUserInteractionEnabled = !value
}
}
}

View File

@ -3036,7 +3036,15 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
}
} else if let group = peer as? TelegramGroup {
var canManageGroupCalls = false
if case .creator = group.role {
canManageGroupCalls = true
} else if case let .admin(rights, _) = group.role {
if rights.flags.contains(.canManageCalls) {
canManageGroupCalls = true
}
}
if canManageGroupCalls {
items.append(ActionSheetButtonItem(title: presentationData.strings.ChannelInfo_CreateVoiceChat, color: .accent, action: { [weak self] in
dismissAction()
@ -3044,18 +3052,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
return
}
strongSelf.activeActionDisposable.set((convertGroupToSupergroup(account: strongSelf.context.account, peerId: group.id)
|> deliverOnMainQueue).start(next: { peerId in
guard let strongSelf = self else {
return
}
if let controller = strongSelf.controller, let navigationController = controller.navigationController as? NavigationController {
rebuildControllerStackAfterSupergroupUpgrade(controller: controller, navigationController: navigationController)
strongSelf.createAndJoinGroupCall(peerId: peerId)
}
}))
strongSelf.createAndJoinGroupCall(peerId: group.id)
}))
}
@ -3212,6 +3209,17 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.createAndJoinGroupCall(peerId: peer.id)
}
return
} else if let peer = self.data?.peer as? TelegramGroup {
guard let cachedGroupData = self.data?.cachedData as? CachedGroupData else {
return
}
if let activeCall = cachedGroupData.activeCall {
self.context.joinGroupCall(peerId: peer.id, activeCall: activeCall)
} else {
self.createAndJoinGroupCall(peerId: peer.id)
}
return
}
guard let peer = self.data?.peer as? TelegramUser, let cachedUserData = self.data?.cachedData as? CachedUserData else {
@ -3226,7 +3234,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
private func createAndJoinGroupCall(peerId: PeerId) {
if let callManager = self.context.sharedContext.callManager {
if let _ = self.context.sharedContext.callManager {
let startCall: (Bool) -> Void = { [weak self] endCurrentIfAny in
guard let strongSelf = self else {
return
@ -3273,34 +3281,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}))
}
let _ = (callManager.currentGroupCallSignal
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] activeCall in
guard let strongSelf = self else {
return
}
if let activeCall = activeCall {
let currentPeerId = activeCall.peerId
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let _ = (strongSelf.context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
return (transaction.getPeer(peerId), transaction.getPeer(currentPeerId))
} |> deliverOnMainQueue).start(next: { [weak self] peer, current in
if let peer = peer {
if let strongSelf = self, let current = current {
strongSelf.controller?.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
startCall(true)
})]), in: .window(.root))
} else {
strongSelf.controller?.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_ExternalCallInProgressMessage, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
})]), in: .window(.root))
}
}
})
} else {
startCall(false)
}
})
startCall(true)
}
}