Story improvements

This commit is contained in:
Ali 2023-04-25 18:04:51 +04:00
parent 060bc61011
commit 1c2f989fbd
44 changed files with 2340 additions and 345 deletions

View File

@ -738,6 +738,9 @@ public protocol AppLockContext: AnyObject {
public protocol RecentSessionsController: AnyObject {
}
public protocol AttachmentFileController: AnyObject {
}
public protocol SharedAccountContext: AnyObject {
var sharedContainerPath: String { get }
var basePath: String { get }
@ -805,6 +808,8 @@ public protocol SharedAccountContext: AnyObject {
func makePrivacyAndSecurityController(context: AccountContext) -> ViewController
func makeSetupTwoFactorAuthController(context: AccountContext) -> ViewController
func makeStorageManagementController(context: AccountContext) -> ViewController
func makeAttachmentFileController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, bannedSendMedia: (Int32, Bool)?, presentGallery: @escaping () -> Void, presentFiles: @escaping () -> Void, send: @escaping (AnyMediaReference) -> Void) -> AttachmentFileController
func makeGalleryCaptionPanelView(context: AccountContext, chatLocation: ChatLocation, customEmojiAvailable: Bool, present: @escaping (ViewController) -> Void, presentInGlobalOverlay: @escaping (ViewController) -> Void) -> NSObject?
func navigateToChatController(_ params: NavigateToChatControllerParams)
func navigateToForumChannel(context: AccountContext, peerId: EnginePeer.Id, navigationController: NavigationController)
func navigateToForumThread(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, messageId: EngineMessage.Id?, navigationController: NavigationController, activateInput: ChatControllerActivateInput?, keepStack: NavigateToChatKeepStack) -> Signal<Never, NoError>

View File

@ -6,6 +6,7 @@ public protocol ContactSelectionController: ViewController {
var result: Signal<([ContactListPeer], ContactListAction, Bool, Int32?, NSAttributedString?)?, NoError> { get }
var displayProgress: Bool { get set }
var dismissed: (() -> Void)? { get set }
var presentScheduleTimePicker: (@escaping (Int32) -> Void) -> Void { get set }
func dismissSearch()
}

View File

@ -463,6 +463,7 @@ public protocol PresentationGroupCall: AnyObject {
public protocol PresentationCallManager: AnyObject {
var currentCallSignal: Signal<PresentationCall?, NoError> { get }
var currentGroupCallSignal: Signal<PresentationGroupCall?, NoError> { get }
var hasActiveCall: Bool { get }
func requestCall(context: AccountContext, peerId: EnginePeer.Id, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult
func joinGroupCall(context: AccountContext, peerId: EnginePeer.Id, invite: String?, requestJoinAsPeerId: ((@escaping (EnginePeer.Id?) -> Void) -> Void)?, initialCall: EngineGroupCallDescription, endCurrentIfAny: Bool) -> JoinGroupCallManagerResult

View File

@ -84,6 +84,7 @@ public protocol AttachmentContainable: ViewController {
var cancelPanGesture: () -> Void { get set }
var isContainerPanning: () -> Bool { get set }
var isContainerExpanded: () -> Bool { get set }
var mediaPickerContext: AttachmentMediaPickerContext? { get }
func isContainerPanningUpdated(_ panning: Bool)

View File

@ -611,6 +611,8 @@ private final class ChatListMediaPreviewNode: ASDisplayNode {
}
}
private let loginCodeRegex = try? NSRegularExpression(pattern: "[\\d\\-]{5,7}", options: [])
class ChatListItemNode: ItemListRevealOptionsItemNode {
final class TopicItemNode: ASDisplayNode {
let topicTitleNode: TextNode
@ -1841,7 +1843,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
authorAttributedString = NSAttributedString(string: peerText, font: textFont, textColor: theme.authorNameColor)
}
let entities = (message._asMessage().textEntitiesAttribute?.entities ?? []).filter { entity in
var entities = (message._asMessage().textEntitiesAttribute?.entities ?? []).filter { entity in
switch entity.type {
case .Spoiler, .CustomEmoji:
return true
@ -1851,6 +1853,18 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
return false
}
}
if message.author?.id.id == PeerId.Id._internalFromInt64Value(777000) {
if let loginCodeRegex {
let results = loginCodeRegex.matches(in: message.text, range: NSRange(message.text.startIndex..., in: message.text))
for result in results {
let spoilerRange: Range<Int> = result.range.location ..< (result.range.location + result.range.length)
if !entities.contains(where: { $0.range.overlaps(spoilerRange) }) {
entities.append(MessageTextEntity(range: spoilerRange, type: .Spoiler))
}
}
}
}
let messageString: NSAttributedString
if !message.text.isEmpty && entities.count > 0 {
var messageText = message.text

View File

@ -0,0 +1,22 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ICloudResources",
module_name = "ICloudResources",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/TelegramCore",
"//submodules/Postbox",
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/Display",
"//submodules/Pdf",
],
visibility = [
"//visibility:public",
],
)

View File

@ -63,17 +63,17 @@ public class ICloudFileResource: TelegramMediaResource {
}
}
struct ICloudFileDescription {
struct AudioMetadata {
let title: String?
let performer: String?
let duration: Int
public struct ICloudFileDescription {
public struct AudioMetadata {
public let title: String?
public let performer: String?
public let duration: Int
}
let urlData: String
let fileName: String
let fileSize: Int
let audioMetadata: AudioMetadata?
public let urlData: String
public let fileName: String
public let fileSize: Int
public let audioMetadata: AudioMetadata?
}
private func descriptionWithUrl(_ url: URL) -> ICloudFileDescription? {
@ -115,7 +115,7 @@ private func descriptionWithUrl(_ url: URL) -> ICloudFileDescription? {
}
}
func iCloudFileDescription(_ url: URL) -> Signal<ICloudFileDescription?, NoError> {
public func iCloudFileDescription(_ url: URL) -> Signal<ICloudFileDescription?, NoError> {
return Signal { subscriber in
var isRemote = false
var isCurrent = true
@ -191,7 +191,7 @@ private final class ICloudFileResourceCopyItem: MediaResourceDataFetchCopyLocalI
}
}
func fetchICloudFileResource(resource: ICloudFileResource) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
public func fetchICloudFileResource(resource: ICloudFileResource) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
return Signal { subscriber in
subscriber.putNext(.reset)

View File

@ -438,6 +438,10 @@ open class LegacyController: ViewController, PresentableController, AttachmentCo
open var isContainerPanning: () -> Bool = { return false }
open var isContainerExpanded: () -> Bool = { return false }
public var mediaPickerContext: AttachmentMediaPickerContext? {
return nil
}
public init(presentation: LegacyControllerPresentation, theme: PresentationTheme? = nil, strings: PresentationStrings? = nil, initialLayout: ContainerViewLayout? = nil) {
self.sizeClass.set(SSignal.single(UIUserInterfaceSizeClass.compact.rawValue as NSNumber))
self.presentation = presentation

View File

@ -524,13 +524,13 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[784356159] = { return Api.MessageMedia.parse_messageMediaVenue($0) }
dict[-1557277184] = { return Api.MessageMedia.parse_messageMediaWebPage($0) }
dict[-1938180548] = { return Api.MessagePeerReaction.parse_messagePeerReaction($0) }
dict[-1228133028] = { return Api.MessagePeerVote.parse_messagePeerVote($0) }
dict[1959634180] = { return Api.MessagePeerVote.parse_messagePeerVoteInputOption($0) }
dict[1177089766] = { return Api.MessagePeerVote.parse_messagePeerVoteMultiple($0) }
dict[182649427] = { return Api.MessageRange.parse_messageRange($0) }
dict[1328256121] = { return Api.MessageReactions.parse_messageReactions($0) }
dict[-2083123262] = { return Api.MessageReplies.parse_messageReplies($0) }
dict[-1495959709] = { return Api.MessageReplyHeader.parse_messageReplyHeader($0) }
dict[886196148] = { return Api.MessageUserVote.parse_messageUserVote($0) }
dict[1017491692] = { return Api.MessageUserVote.parse_messageUserVoteInputOption($0) }
dict[-1973033641] = { return Api.MessageUserVote.parse_messageUserVoteMultiple($0) }
dict[1163625789] = { return Api.MessageViews.parse_messageViews($0) }
dict[975236280] = { return Api.MessagesFilter.parse_inputMessagesFilterChatPhotos($0) }
dict[-530392189] = { return Api.MessagesFilter.parse_inputMessagesFilterContacts($0) }
@ -635,7 +635,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-2032041631] = { return Api.Poll.parse_poll($0) }
dict[1823064809] = { return Api.PollAnswer.parse_pollAnswer($0) }
dict[997055186] = { return Api.PollAnswerVoters.parse_pollAnswerVoters($0) }
dict[-591909213] = { return Api.PollResults.parse_pollResults($0) }
dict[2061444128] = { return Api.PollResults.parse_pollResults($0) }
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
dict[512535275] = { return Api.PostAddress.parse_postAddress($0) }
dict[1958953753] = { return Api.PremiumGiftOption.parse_premiumGiftOption($0) }
@ -856,7 +856,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1517529484] = { return Api.Update.parse_updateMessageExtendedMedia($0) }
dict[1318109142] = { return Api.Update.parse_updateMessageID($0) }
dict[-1398708869] = { return Api.Update.parse_updateMessagePoll($0) }
dict[274961865] = { return Api.Update.parse_updateMessagePollVote($0) }
dict[619974263] = { return Api.Update.parse_updateMessagePollVote($0) }
dict[1578843320] = { return Api.Update.parse_updateMessageReactions($0) }
dict[-2030252155] = { return Api.Update.parse_updateMoveStickerSetToTop($0) }
dict[1656358105] = { return Api.Update.parse_updateNewChannelMessage($0) }
@ -1108,7 +1108,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-244016606] = { return Api.messages.Stickers.parse_stickersNotModified($0) }
dict[-1821037486] = { return Api.messages.TranscribedAudio.parse_transcribedAudio($0) }
dict[870003448] = { return Api.messages.TranslatedText.parse_translateResult($0) }
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
dict[1218005070] = { return Api.messages.VotesList.parse_votesList($0) }
dict[1042605427] = { return Api.payments.BankCardData.parse_bankCardData($0) }
dict[-1362048039] = { return Api.payments.ExportedInvoice.parse_exportedInvoice($0) }
dict[-1610250415] = { return Api.payments.PaymentForm.parse_paymentForm($0) }
@ -1522,6 +1522,8 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.MessagePeerReaction:
_1.serialize(buffer, boxed)
case let _1 as Api.MessagePeerVote:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageRange:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageReactions:
@ -1530,8 +1532,6 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.MessageReplyHeader:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageUserVote:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageViews:
_1.serialize(buffer, boxed)
case let _1 as Api.MessagesFilter:

View File

@ -50,6 +50,114 @@ public extension Api {
}
}
public extension Api {
enum MessagePeerVote: TypeConstructorDescription {
case messagePeerVote(peer: Api.Peer, option: Buffer, date: Int32)
case messagePeerVoteInputOption(peer: Api.Peer, date: Int32)
case messagePeerVoteMultiple(peer: Api.Peer, options: [Buffer], date: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .messagePeerVote(let peer, let option, let date):
if boxed {
buffer.appendInt32(-1228133028)
}
peer.serialize(buffer, true)
serializeBytes(option, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
break
case .messagePeerVoteInputOption(let peer, let date):
if boxed {
buffer.appendInt32(1959634180)
}
peer.serialize(buffer, true)
serializeInt32(date, buffer: buffer, boxed: false)
break
case .messagePeerVoteMultiple(let peer, let options, let date):
if boxed {
buffer.appendInt32(1177089766)
}
peer.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(options.count))
for item in options {
serializeBytes(item, buffer: buffer, boxed: false)
}
serializeInt32(date, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .messagePeerVote(let peer, let option, let date):
return ("messagePeerVote", [("peer", peer as Any), ("option", option as Any), ("date", date as Any)])
case .messagePeerVoteInputOption(let peer, let date):
return ("messagePeerVoteInputOption", [("peer", peer as Any), ("date", date as Any)])
case .messagePeerVoteMultiple(let peer, let options, let date):
return ("messagePeerVoteMultiple", [("peer", peer as Any), ("options", options as Any), ("date", date as Any)])
}
}
public static func parse_messagePeerVote(_ reader: BufferReader) -> MessagePeerVote? {
var _1: Api.Peer?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.Peer
}
var _2: Buffer?
_2 = parseBytes(reader)
var _3: Int32?
_3 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.MessagePeerVote.messagePeerVote(peer: _1!, option: _2!, date: _3!)
}
else {
return nil
}
}
public static func parse_messagePeerVoteInputOption(_ reader: BufferReader) -> MessagePeerVote? {
var _1: Api.Peer?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.Peer
}
var _2: Int32?
_2 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.MessagePeerVote.messagePeerVoteInputOption(peer: _1!, date: _2!)
}
else {
return nil
}
}
public static func parse_messagePeerVoteMultiple(_ reader: BufferReader) -> MessagePeerVote? {
var _1: Api.Peer?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.Peer
}
var _2: [Buffer]?
if let _ = reader.readInt32() {
_2 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self)
}
var _3: Int32?
_3 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.MessagePeerVote.messagePeerVoteMultiple(peer: _1!, options: _2!, date: _3!)
}
else {
return nil
}
}
}
}
public extension Api {
enum MessageRange: TypeConstructorDescription {
case messageRange(minId: Int32, maxId: Int32)
@ -262,108 +370,6 @@ public extension Api {
}
}
public extension Api {
enum MessageUserVote: TypeConstructorDescription {
case messageUserVote(userId: Int64, option: Buffer, date: Int32)
case messageUserVoteInputOption(userId: Int64, date: Int32)
case messageUserVoteMultiple(userId: Int64, options: [Buffer], date: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .messageUserVote(let userId, let option, let date):
if boxed {
buffer.appendInt32(886196148)
}
serializeInt64(userId, buffer: buffer, boxed: false)
serializeBytes(option, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
break
case .messageUserVoteInputOption(let userId, let date):
if boxed {
buffer.appendInt32(1017491692)
}
serializeInt64(userId, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
break
case .messageUserVoteMultiple(let userId, let options, let date):
if boxed {
buffer.appendInt32(-1973033641)
}
serializeInt64(userId, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(options.count))
for item in options {
serializeBytes(item, buffer: buffer, boxed: false)
}
serializeInt32(date, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .messageUserVote(let userId, let option, let date):
return ("messageUserVote", [("userId", userId as Any), ("option", option as Any), ("date", date as Any)])
case .messageUserVoteInputOption(let userId, let date):
return ("messageUserVoteInputOption", [("userId", userId as Any), ("date", date as Any)])
case .messageUserVoteMultiple(let userId, let options, let date):
return ("messageUserVoteMultiple", [("userId", userId as Any), ("options", options as Any), ("date", date as Any)])
}
}
public static func parse_messageUserVote(_ reader: BufferReader) -> MessageUserVote? {
var _1: Int64?
_1 = reader.readInt64()
var _2: Buffer?
_2 = parseBytes(reader)
var _3: Int32?
_3 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.MessageUserVote.messageUserVote(userId: _1!, option: _2!, date: _3!)
}
else {
return nil
}
}
public static func parse_messageUserVoteInputOption(_ reader: BufferReader) -> MessageUserVote? {
var _1: Int64?
_1 = reader.readInt64()
var _2: Int32?
_2 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.MessageUserVote.messageUserVoteInputOption(userId: _1!, date: _2!)
}
else {
return nil
}
}
public static func parse_messageUserVoteMultiple(_ reader: BufferReader) -> MessageUserVote? {
var _1: Int64?
_1 = reader.readInt64()
var _2: [Buffer]?
if let _ = reader.readInt32() {
_2 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self)
}
var _3: Int32?
_3 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.MessageUserVote.messageUserVoteMultiple(userId: _1!, options: _2!, date: _3!)
}
else {
return nil
}
}
}
}
public extension Api {
enum MessageViews: TypeConstructorDescription {
case messageViews(flags: Int32, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?)

View File

@ -428,13 +428,13 @@ public extension Api {
}
public extension Api {
enum PollResults: TypeConstructorDescription {
case pollResults(flags: Int32, results: [Api.PollAnswerVoters]?, totalVoters: Int32?, recentVoters: [Int64]?, solution: String?, solutionEntities: [Api.MessageEntity]?)
case pollResults(flags: Int32, results: [Api.PollAnswerVoters]?, totalVoters: Int32?, recentVoters: [Api.Peer]?, solution: String?, solutionEntities: [Api.MessageEntity]?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .pollResults(let flags, let results, let totalVoters, let recentVoters, let solution, let solutionEntities):
if boxed {
buffer.appendInt32(-591909213)
buffer.appendInt32(2061444128)
}
serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
@ -446,7 +446,7 @@ public extension Api {
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(recentVoters!.count))
for item in recentVoters! {
serializeInt64(item, buffer: buffer, boxed: false)
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 4) != 0 {serializeString(solution!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261)
@ -474,9 +474,9 @@ public extension Api {
} }
var _3: Int32?
if Int(_1!) & Int(1 << 2) != 0 {_3 = reader.readInt32() }
var _4: [Int64]?
var _4: [Api.Peer]?
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
_4 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self)
} }
var _5: String?
if Int(_1!) & Int(1 << 4) != 0 {_5 = parseString(reader) }

View File

@ -785,7 +785,7 @@ public extension Api {
case updateMessageExtendedMedia(peer: Api.Peer, msgId: Int32, extendedMedia: Api.MessageExtendedMedia)
case updateMessageID(id: Int32, randomId: Int64)
case updateMessagePoll(flags: Int32, pollId: Int64, poll: Api.Poll?, results: Api.PollResults)
case updateMessagePollVote(pollId: Int64, userId: Int64, options: [Buffer], qts: Int32)
case updateMessagePollVote(pollId: Int64, peer: Api.Peer, options: [Buffer], qts: Int32)
case updateMessageReactions(flags: Int32, peer: Api.Peer, msgId: Int32, topMsgId: Int32?, reactions: Api.MessageReactions)
case updateMoveStickerSetToTop(flags: Int32, stickerset: Int64)
case updateNewChannelMessage(message: Api.Message, pts: Int32, ptsCount: Int32)
@ -1385,12 +1385,12 @@ public extension Api {
if Int(flags) & Int(1 << 0) != 0 {poll!.serialize(buffer, true)}
results.serialize(buffer, true)
break
case .updateMessagePollVote(let pollId, let userId, let options, let qts):
case .updateMessagePollVote(let pollId, let peer, let options, let qts):
if boxed {
buffer.appendInt32(274961865)
buffer.appendInt32(619974263)
}
serializeInt64(pollId, buffer: buffer, boxed: false)
serializeInt64(userId, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(options.count))
for item in options {
@ -1923,8 +1923,8 @@ public extension Api {
return ("updateMessageID", [("id", id as Any), ("randomId", randomId as Any)])
case .updateMessagePoll(let flags, let pollId, let poll, let results):
return ("updateMessagePoll", [("flags", flags as Any), ("pollId", pollId as Any), ("poll", poll as Any), ("results", results as Any)])
case .updateMessagePollVote(let pollId, let userId, let options, let qts):
return ("updateMessagePollVote", [("pollId", pollId as Any), ("userId", userId as Any), ("options", options as Any), ("qts", qts as Any)])
case .updateMessagePollVote(let pollId, let peer, let options, let qts):
return ("updateMessagePollVote", [("pollId", pollId as Any), ("peer", peer as Any), ("options", options as Any), ("qts", qts as Any)])
case .updateMessageReactions(let flags, let peer, let msgId, let topMsgId, let reactions):
return ("updateMessageReactions", [("flags", flags as Any), ("peer", peer as Any), ("msgId", msgId as Any), ("topMsgId", topMsgId as Any), ("reactions", reactions as Any)])
case .updateMoveStickerSetToTop(let flags, let stickerset):
@ -3171,8 +3171,10 @@ public extension Api {
public static func parse_updateMessagePollVote(_ reader: BufferReader) -> Update? {
var _1: Int64?
_1 = reader.readInt64()
var _2: Int64?
_2 = reader.readInt64()
var _2: Api.Peer?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.Peer
}
var _3: [Buffer]?
if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self)
@ -3184,7 +3186,7 @@ public extension Api {
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.Update.updateMessagePollVote(pollId: _1!, userId: _2!, options: _3!, qts: _4!)
return Api.Update.updateMessagePollVote(pollId: _1!, peer: _2!, options: _3!, qts: _4!)
}
else {
return nil

View File

@ -198,13 +198,13 @@ public extension Api.messages {
}
public extension Api.messages {
enum VotesList: TypeConstructorDescription {
case votesList(flags: Int32, count: Int32, votes: [Api.MessageUserVote], users: [Api.User], nextOffset: String?)
case votesList(flags: Int32, count: Int32, votes: [Api.MessagePeerVote], chats: [Api.Chat], users: [Api.User], nextOffset: String?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .votesList(let flags, let count, let votes, let users, let nextOffset):
case .votesList(let flags, let count, let votes, let chats, let users, let nextOffset):
if boxed {
buffer.appendInt32(136574537)
buffer.appendInt32(1218005070)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(count, buffer: buffer, boxed: false)
@ -214,6 +214,11 @@ public extension Api.messages {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(chats.count))
for item in chats {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
@ -225,8 +230,8 @@ public extension Api.messages {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .votesList(let flags, let count, let votes, let users, let nextOffset):
return ("votesList", [("flags", flags as Any), ("count", count as Any), ("votes", votes as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)])
case .votesList(let flags, let count, let votes, let chats, let users, let nextOffset):
return ("votesList", [("flags", flags as Any), ("count", count as Any), ("votes", votes as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)])
}
}
@ -235,23 +240,28 @@ public extension Api.messages {
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: [Api.MessageUserVote]?
var _3: [Api.MessagePeerVote]?
if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageUserVote.self)
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessagePeerVote.self)
}
var _4: [Api.User]?
var _4: [Api.Chat]?
if let _ = reader.readInt32() {
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
}
var _5: String?
if Int(_1!) & Int(1 << 0) != 0 {_5 = parseString(reader) }
var _5: [Api.User]?
if let _ = reader.readInt32() {
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
var _6: String?
if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.messages.VotesList.votesList(flags: _1!, count: _2!, votes: _3!, users: _4!, nextOffset: _5)
let _c5 = _5 != nil
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.messages.VotesList.votesList(flags: _1!, count: _2!, votes: _3!, chats: _4!, users: _5!, nextOffset: _6)
}
else {
return nil

View File

@ -4546,25 +4546,6 @@ public extension Api.functions.messages {
})
}
}
public extension Api.functions.messages {
static func getAllChats(exceptIds: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.Chats>) {
let buffer = Buffer()
buffer.appendInt32(-2023787330)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(exceptIds.count))
for item in exceptIds {
serializeInt64(item, buffer: buffer, boxed: false)
}
return (FunctionDescription(name: "messages.getAllChats", parameters: [("exceptIds", String(describing: exceptIds))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Chats? in
let reader = BufferReader(buffer)
var result: Api.messages.Chats?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.messages.Chats
}
return result
})
}
}
public extension Api.functions.messages {
static func getAllDrafts() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()

View File

@ -114,10 +114,14 @@ private func mergeReactions(reactions: [MessageReaction], recentPeers: [Reaction
pendingIndex += 1
}
if let index = recentPeers.firstIndex(where: { $0.value == pendingReaction.value && $0.peerId == accountPeerId }) {
let pendingReactionSendAsPeerId = pendingReaction.sendAsPeerId ?? accountPeerId
if let index = recentPeers.firstIndex(where: {
$0.value == pendingReaction.value && $0.peerId == pendingReactionSendAsPeerId
}) {
recentPeers.remove(at: index)
}
recentPeers.append(ReactionsMessageAttribute.RecentPeer(value: pendingReaction.value, isLarge: false, isUnseen: false, peerId: accountPeerId, timestamp: Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)))
recentPeers.append(ReactionsMessageAttribute.RecentPeer(value: pendingReaction.value, isLarge: false, isUnseen: false, peerId: pendingReactionSendAsPeerId, timestamp: Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)))
}
for i in (0 ..< result.count).reversed() {

View File

@ -35,7 +35,7 @@ extension TelegramMediaPollResults {
}
self.init(voters: results.flatMap({ $0.map(TelegramMediaPollOptionVoters.init(apiVoters:)) }), totalVoters: totalVoters, recentVoters: recentVoters.flatMap { recentVoters in
return recentVoters.map { PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value($0)) }
return recentVoters.map { $0.peerId }
} ?? [], solution: parsedSolution)
}
}

View File

@ -20,6 +20,15 @@ public enum UpdateMessageReaction {
public func updateMessageReactionsInteractively(account: Account, messageId: MessageId, reactions: [UpdateMessageReaction], isLarge: Bool, storeAsRecentlyUsed: Bool) -> Signal<Never, NoError> {
return account.postbox.transaction { transaction -> Void in
var sendAsPeerId = account.peerId
if let cachedData = transaction.getPeerCachedData(peerId: messageId.peerId) {
if let cachedData = cachedData as? CachedChannelData {
if let sendAsPeerIdValue = cachedData.sendAsPeerId {
sendAsPeerId = sendAsPeerIdValue
}
}
}
let isPremium = (transaction.getPeer(account.peerId) as? TelegramUser)?.isPremium ?? false
let appConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? .defaultValue
let maxCount: Int
@ -34,12 +43,12 @@ public func updateMessageReactionsInteractively(account: Account, messageId: Mes
for reaction in reactions {
switch reaction {
case let .custom(fileId, file):
mappedReactions.append(PendingReactionsMessageAttribute.PendingReaction(value: .custom(fileId)))
mappedReactions.append(PendingReactionsMessageAttribute.PendingReaction(value: .custom(fileId), sendAsPeerId: sendAsPeerId))
if let file = file {
transaction.storeMediaIfNotPresent(media: file)
}
case let .builtin(value):
mappedReactions.append(PendingReactionsMessageAttribute.PendingReaction(value: .builtin(value)))
mappedReactions.append(PendingReactionsMessageAttribute.PendingReaction(value: .builtin(value), sendAsPeerId: sendAsPeerId))
}
}
@ -88,7 +97,7 @@ public func updateMessageReactionsInteractively(account: Account, messageId: Mes
if updatedOutgoingReactions.count > maxCount {
let sortedOutgoingReactions = updatedOutgoingReactions.sorted(by: { $0.chosenOrder! < $1.chosenOrder! })
mappedReactions = Array(sortedOutgoingReactions.suffix(maxCount).map { reaction -> PendingReactionsMessageAttribute.PendingReaction in
return PendingReactionsMessageAttribute.PendingReaction(value: reaction.value)
return PendingReactionsMessageAttribute.PendingReaction(value: reaction.value, sendAsPeerId: sendAsPeerId)
})
}

View File

@ -446,20 +446,77 @@ public final class PendingMessageManager {
Logger.shared.log("PendingMessageManager", "beginSendingMessages messagesToForward.count: \(messagesToForward.count)")
for (_, messages) in messagesToForward {
for (context, _, _) in messages {
context.state = .sending(groupId: nil)
let forwardGroupLimit = 100
for (_, ungroupedMessages) in messagesToForward {
var messageGroups: [[(PendingMessageContext, Message, ForwardSourceInfoAttribute)]] = []
for message in ungroupedMessages {
if messageGroups.isEmpty || messageGroups[messageGroups.count - 1].isEmpty {
messageGroups.append([message])
} else {
if messageGroups[messageGroups.count - 1][0].1.groupingKey == message.1.groupingKey {
messageGroups[messageGroups.count - 1].append(message)
} else {
messageGroups.append([message])
}
}
}
let sendMessage: Signal<PendingMessageResult, NoError> = strongSelf.sendGroupMessagesContent(network: strongSelf.network, postbox: strongSelf.postbox, stateManager: strongSelf.stateManager, accountPeerId: strongSelf.accountPeerId, group: messages.map { data in
let (_, message, forwardInfo) = data
return (message.id, PendingMessageUploadedContentAndReuploadInfo(content: .forward(forwardInfo), reuploadInfo: nil, cacheReferenceKey: nil))
})
|> map { next -> PendingMessageResult in
return .progress(1.0)
var countedMessageGroups: [[(PendingMessageContext, Message, ForwardSourceInfoAttribute)]] = []
while !messageGroups.isEmpty {
guard let messageGroup = messageGroups.first else {
break
}
messageGroups.removeFirst()
if messageGroup.isEmpty {
continue
}
if countedMessageGroups.isEmpty {
countedMessageGroups.append([])
} else if countedMessageGroups[countedMessageGroups.count - 1].count >= forwardGroupLimit {
countedMessageGroups.append([])
}
if countedMessageGroups[countedMessageGroups.count - 1].isEmpty {
let fittingFreeMessageCount = min(forwardGroupLimit, messageGroup.count)
countedMessageGroups[countedMessageGroups.count - 1].append(contentsOf: messageGroup[0 ..< fittingFreeMessageCount])
if fittingFreeMessageCount < messageGroup.count {
messageGroups.insert(Array(messageGroup[fittingFreeMessageCount ..< messageGroup.count]), at: 0)
}
} else if countedMessageGroups[countedMessageGroups.count - 1].count + messageGroup.count <= forwardGroupLimit {
countedMessageGroups[countedMessageGroups.count - 1].append(contentsOf: messageGroup)
} else {
if countedMessageGroups[countedMessageGroups.count - 1][0].1.groupingKey == nil && messageGroup[0].1.groupingKey == nil {
let fittingFreeMessageCount = forwardGroupLimit - countedMessageGroups[countedMessageGroups.count - 1].count
countedMessageGroups[countedMessageGroups.count - 1].append(contentsOf: messageGroup[0 ..< fittingFreeMessageCount])
messageGroups.insert(Array(messageGroup[fittingFreeMessageCount ..< messageGroup.count]), at: 0)
} else {
countedMessageGroups.append([])
}
}
}
for messages in countedMessageGroups {
if messages.isEmpty {
continue
}
for (context, _, _) in messages {
context.state = .sending(groupId: nil)
}
let sendMessage: Signal<PendingMessageResult, NoError> = strongSelf.sendGroupMessagesContent(network: strongSelf.network, postbox: strongSelf.postbox, stateManager: strongSelf.stateManager, accountPeerId: strongSelf.accountPeerId, group: messages.map { data in
let (_, message, forwardInfo) = data
return (message.id, PendingMessageUploadedContentAndReuploadInfo(content: .forward(forwardInfo), reuploadInfo: nil, cacheReferenceKey: nil))
})
|> map { next -> PendingMessageResult in
return .progress(1.0)
}
messages[0].0.sendDisposable.set((sendMessage
|> deliverOn(strongSelf.queue)).start())
}
messages[0].0.sendDisposable.set((sendMessage
|> deliverOn(strongSelf.queue)).start())
}
}
}))

View File

@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
public class Serialization: NSObject, MTSerialization {
public func currentLayer() -> UInt {
return 158
return 159
}
public func parseMessage(_ data: Data!) -> Any! {

View File

@ -241,17 +241,25 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute {
public final class PendingReactionsMessageAttribute: MessageAttribute {
public struct PendingReaction: Equatable, PostboxCoding {
public var value: MessageReaction.Reaction
public var sendAsPeerId: PeerId?
public init(value: MessageReaction.Reaction) {
public init(value: MessageReaction.Reaction, sendAsPeerId: PeerId?) {
self.value = value
self.sendAsPeerId = sendAsPeerId
}
public init(decoder: PostboxDecoder) {
self.value = decoder.decodeObjectForKey("val", decoder: { MessageReaction.Reaction(decoder: $0) }) as! MessageReaction.Reaction
self.sendAsPeerId = decoder.decodeOptionalInt64ForKey("sa").flatMap(PeerId.init)
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeObject(self.value, forKey: "val")
if let sendAsPeerId = self.sendAsPeerId {
encoder.encodeInt64(sendAsPeerId.toInt64(), forKey: "sa")
} else {
encoder.encodeNil(forKey: "sa")
}
}
}
@ -261,11 +269,18 @@ public final class PendingReactionsMessageAttribute: MessageAttribute {
public let storeAsRecentlyUsed: Bool
public var associatedPeerIds: [PeerId] {
var peerIds: [PeerId] = []
if let accountPeerId = self.accountPeerId {
return [accountPeerId]
} else {
return []
peerIds.append(accountPeerId)
}
for reaction in self.reactions {
if let sendAsPeerId = reaction.sendAsPeerId {
if !peerIds.contains(sendAsPeerId) {
peerIds.append(sendAsPeerId)
}
}
}
return peerIds
}
public var associatedMediaIds: [MediaId] {

View File

@ -272,8 +272,13 @@ private final class PollResultsOptionContext {
return ([], 0, nil)
}
switch result {
case let .votesList(_, count, votes, users, nextOffset):
case let .votesList(_, count, votes, chats, users, nextOffset):
var peers: [Peer] = []
for apiChat in chats {
if let peer = parseTelegramGroupOrChannel(chat: apiChat) {
peers.append(peer)
}
}
for apiUser in users {
peers.append(TelegramUser(user: apiUser))
}
@ -284,12 +289,12 @@ private final class PollResultsOptionContext {
for vote in votes {
let peerId: PeerId
switch vote {
case let .messageUserVote(userId, _, _):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
case let .messageUserVoteInputOption(userId, _):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
case let .messageUserVoteMultiple(userId, _, _):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
case let .messagePeerVote(peerIdValue, _, _):
peerId = peerIdValue.peerId
case let .messagePeerVoteInputOption(peerIdValue, _):
peerId = peerIdValue.peerId
case let .messagePeerVoteMultiple(peerIdValue, _, _):
peerId = peerIdValue.peerId
}
if let peer = transaction.getPeer(peerId) {
resultPeers.append(RenderedPeer(peer: peer))

View File

@ -360,6 +360,9 @@ swift_library(
"//submodules/TelegramUI/Components/SliderContextItem:SliderContextItem",
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
"//submodules/TelegramUI/Components/Stories/StoryContentComponent",
"//submodules/TelegramUI/Components/ChatScheduleTimeController",
"//submodules/ICloudResources",
"//submodules/TelegramUI/Components/LegacyCamera",
] + select({
"@build_bazel_rules_apple//apple:ios_armv7": [],
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,

View File

@ -0,0 +1,28 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ChatScheduleTimeController",
module_name = "ChatScheduleTimeController",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/AsyncDisplayKit",
"//submodules/Display",
"//submodules/TelegramCore",
"//submodules/Postbox",
"//submodules/AccountContext",
"//submodules/TelegramPresentationData",
"//submodules/TelegramStringFormatting",
"//submodules/SolidRoundedButtonNode",
"//submodules/PresentationDataUtils",
"//submodules/UIKitRuntimeUtils",
],
visibility = [
"//visibility:public",
],
)

View File

@ -8,17 +8,17 @@ import SwiftSignalKit
import AccountContext
import TelegramPresentationData
enum ChatScheduleTimeControllerMode {
public enum ChatScheduleTimeControllerMode {
case scheduledMessages(sendWhenOnlineAvailable: Bool)
case reminders
}
enum ChatScheduleTimeControllerStyle {
public enum ChatScheduleTimeControllerStyle {
case `default`
case media
}
final class ChatScheduleTimeController: ViewController {
public final class ChatScheduleTimeController: ViewController {
private var controllerNode: ChatScheduleTimeControllerNode {
return self.displayNode as! ChatScheduleTimeControllerNode
}
@ -37,7 +37,7 @@ final class ChatScheduleTimeController: ViewController {
private var presentationData: PresentationData
private var presentationDataDisposable: Disposable?
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, mode: ChatScheduleTimeControllerMode, style: ChatScheduleTimeControllerStyle, currentTime: Int32? = nil, minimalTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) {
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, mode: ChatScheduleTimeControllerMode, style: ChatScheduleTimeControllerStyle, currentTime: Int32? = nil, minimalTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) {
self.context = context
self.peerId = peerId
self.mode = mode
@ -66,7 +66,7 @@ final class ChatScheduleTimeController: ViewController {
self.statusBar.statusBarStyle = .Ignore
}
required init(coder aDecoder: NSCoder) {
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

View File

@ -0,0 +1,28 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "LegacyCamera",
module_name = "LegacyCamera",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/LegacyComponents",
"//submodules/Display",
"//submodules/TelegramCore",
"//submodules/Postbox",
"//submodules/AccountContext",
"//submodules/ShareController",
"//submodules/LegacyUI",
"//submodules/LegacyMediaPickerUI",
],
visibility = [
"//visibility:public",
],
)

View File

@ -10,7 +10,7 @@ import ShareController
import LegacyUI
import LegacyMediaPickerUI
func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocation: ChatLocation, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, attachmentController: ViewController? = nil, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: NSAttributedString, hasSchedule: Bool, enablePhoto: Bool, enableVideo: Bool, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, dismissedWithResult: @escaping () -> Void = {}, finishedTransitionIn: @escaping () -> Void = {}) {
public func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocation: ChatLocation, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, attachmentController: ViewController? = nil, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: NSAttributedString, hasSchedule: Bool, enablePhoto: Bool, enableVideo: Bool, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, dismissedWithResult: @escaping () -> Void = {}, finishedTransitionIn: @escaping () -> Void = {}) {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)
@ -202,7 +202,7 @@ func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocation: Ch
parentController.present(legacyController, in: .window(.root))
}
func presentedLegacyShortcutCamera(context: AccountContext, saveCapturedMedia: Bool, saveEditedPhotos: Bool, mediaGrouping: Bool, parentController: ViewController) {
public func presentedLegacyShortcutCamera(context: AccountContext, saveCapturedMedia: Bool, saveEditedPhotos: Bool, mediaGrouping: Bool, parentController: ViewController) {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)

View File

@ -14,6 +14,7 @@ swift_library(
"//submodules/ComponentFlow",
"//submodules/AppBundle",
"//submodules/TelegramUI/Components/TextFieldComponent",
"//submodules/Components/BundleIconComponent",
],
visibility = [
"//visibility:public",

View File

@ -4,6 +4,7 @@ import Display
import ComponentFlow
import AppBundle
import TextFieldComponent
import BundleIconComponent
public final class MessageInputPanelComponent: Component {
public final class ExternalState {
@ -15,13 +16,16 @@ public final class MessageInputPanelComponent: Component {
public let externalState: ExternalState
public let sendMessageAction: () -> Void
public let attachmentAction: () -> Void
public init(
externalState: ExternalState,
sendMessageAction: @escaping () -> Void
sendMessageAction: @escaping () -> Void,
attachmentAction: @escaping () -> Void
) {
self.externalState = externalState
self.sendMessageAction = sendMessageAction
self.attachmentAction = attachmentAction
}
public static func ==(lhs: MessageInputPanelComponent, rhs: MessageInputPanelComponent) -> Bool {
@ -41,7 +45,7 @@ public final class MessageInputPanelComponent: Component {
private let textField = ComponentView<Empty>()
private let textFieldExternalState = TextFieldComponent.ExternalState()
private let attachmentIconView: UIImageView
private let attachmentButton = ComponentView<Empty>()
private let inputActionButton = ComponentView<Empty>()
private let stickerIconView: UIImageView
@ -52,7 +56,6 @@ public final class MessageInputPanelComponent: Component {
override init(frame: CGRect) {
self.fieldBackgroundView = UIImageView()
self.attachmentIconView = UIImageView()
self.stickerIconView = UIImageView()
super.init(frame: frame)
@ -60,7 +63,6 @@ public final class MessageInputPanelComponent: Component {
self.addSubview(self.fieldBackgroundView)
self.addSubview(self.fieldBackgroundView)
self.addSubview(self.attachmentIconView)
self.addSubview(self.stickerIconView)
}
@ -76,6 +78,13 @@ public final class MessageInputPanelComponent: Component {
return .text(textFieldView.getText())
}
public func getAttachmentButtonView() -> UIView? {
guard let attachmentButtonView = self.attachmentButton.view else {
return nil
}
return attachmentButtonView
}
public func clearSendMessageInput() {
if let textFieldView = self.textField.view as? TextFieldComponent.View {
textFieldView.setText(string: "")
@ -93,10 +102,6 @@ public final class MessageInputPanelComponent: Component {
if self.fieldBackgroundView.image == nil {
self.fieldBackgroundView.image = generateStretchableFilledCircleImage(diameter: fieldCornerRadius * 2.0, color: nil, strokeColor: UIColor(white: 1.0, alpha: 0.16), strokeWidth: 1.0, backgroundColor: nil)
}
if self.attachmentIconView.image == nil {
self.attachmentIconView.image = UIImage(bundleImageName: "Chat/Input/Text/IconAttachment")?.withRenderingMode(.alwaysTemplate)
self.attachmentIconView.tintColor = .white
}
if self.stickerIconView.image == nil {
self.stickerIconView.image = UIImage(bundleImageName: "Chat/Input/Text/AccessoryIconStickers")?.withRenderingMode(.alwaysTemplate)
self.stickerIconView.tintColor = .white
@ -129,8 +134,28 @@ public final class MessageInputPanelComponent: Component {
transition.setFrame(view: textFieldView, frame: CGRect(origin: CGPoint(x: fieldFrame.minX, y: fieldFrame.maxY - textFieldSize.height), size: textFieldSize))
}
if let image = self.attachmentIconView.image {
transition.setFrame(view: self.attachmentIconView, frame: CGRect(origin: CGPoint(x: floor((insets.left - image.size.width) * 0.5), y: size.height - baseHeight + floor((baseHeight - image.size.height) * 0.5)), size: image.size))
let attachmentButtonSize = self.attachmentButton.update(
transition: transition,
component: AnyComponent(Button(
content: AnyComponent(BundleIconComponent(
name: "Chat/Input/Text/IconAttachment",
tintColor: .white
)),
action: { [weak self] in
guard let self else {
return
}
self.component?.attachmentAction()
}
).minSize(CGSize(width: 41.0, height: baseHeight))),
environment: {},
containerSize: CGSize(width: 41.0, height: baseHeight)
)
if let attachmentButtonView = self.attachmentButton.view {
if attachmentButtonView.superview == nil {
self.addSubview(attachmentButtonView)
}
transition.setFrame(view: attachmentButtonView, frame: CGRect(origin: CGPoint(x: floor((insets.left - attachmentButtonSize.width) * 0.5), y: size.height - baseHeight + floor((baseHeight - attachmentButtonSize.height) * 0.5)), size: attachmentButtonSize))
}
let inputActionButtonSize = self.inputActionButton.update(

View File

@ -15,12 +15,32 @@ swift_library(
"//submodules/Components/ViewControllerComponent",
"//submodules/Components/ComponentDisplayAdapters",
"//submodules/TelegramUI/Components/MessageInputPanelComponent",
"//submodules/TelegramUI/Components/ChatEntityKeyboardInputNode",
"//submodules/AccountContext",
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/AppBundle",
"//submodules/TelegramCore",
"//submodules/ShareController",
"//submodules/UndoUI",
"//submodules/AttachmentUI",
"//submodules/TelegramUIPreferences",
"//submodules/MediaPickerUI",
"//submodules/LegacyMediaPickerUI",
"//submodules/LocationUI",
"//submodules/WebUI",
"//submodules/TelegramUI/Components/ChatScheduleTimeController",
"//submodules/TelegramUI/Components/ChatTimerScreen",
"//submodules/TextFormat",
"//submodules/PhoneNumberFormat",
"//submodules/ComposePollUI",
"//submodules/TelegramIntents",
"//submodules/LegacyUI",
"//submodules/WebSearchUI",
"//submodules/PremiumUI",
"//submodules/ICloudResources",
"//submodules/LegacyComponents",
"//submodules/TelegramUI/Components/LegacyCamera",
"//submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent",
],
visibility = [
"//visibility:public",

View File

@ -4,6 +4,21 @@ import Display
import ComponentFlow
final class MediaNavigationStripComponent: Component {
final class EnvironmentType: Equatable {
let currentProgress: Double
init(currentProgress: Double) {
self.currentProgress = currentProgress
}
static func ==(lhs: EnvironmentType, rhs: EnvironmentType) -> Bool {
if lhs.currentProgress != rhs.currentProgress {
return false
}
return true
}
}
let index: Int
let count: Int
@ -23,10 +38,17 @@ final class MediaNavigationStripComponent: Component {
}
private final class ItemLayer: SimpleLayer {
let foregroundLayer: SimpleLayer
override init() {
self.foregroundLayer = SimpleLayer()
super.init()
self.cornerRadius = 1.5
self.foregroundLayer.cornerRadius = 1.5
self.addSublayer(self.foregroundLayer)
}
required init?(coder: NSCoder) {
@ -34,6 +56,8 @@ final class MediaNavigationStripComponent: Component {
}
override init(layer: Any) {
self.foregroundLayer = SimpleLayer()
super.init(layer: layer)
}
}
@ -52,10 +76,10 @@ final class MediaNavigationStripComponent: Component {
fatalError("init(coder:) has not been implemented")
}
func update(component: MediaNavigationStripComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
func update(component: MediaNavigationStripComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
let spacing: CGFloat = 3.0
let itemHeight: CGFloat = 2.0
let minItemWidth: CGFloat = 3.0
let minItemWidth: CGFloat = 10.0
var validIndices: [Int] = []
if component.count != 0 {
@ -110,7 +134,19 @@ final class MediaNavigationStripComponent: Component {
transition.setFrame(layer: itemLayer, frame: itemFrame)
itemLayer.backgroundColor = UIColor(white: 1.0, alpha: i == component.index ? 1.0 : 0.5).cgColor
itemLayer.backgroundColor = UIColor(white: 1.0, alpha: 0.5).cgColor
itemLayer.foregroundLayer.backgroundColor = UIColor(white: 1.0, alpha: 1.0).cgColor
let itemProgress: CGFloat
if i < component.index {
itemProgress = 1.0
} else if i == component.index {
itemProgress = max(0.0, min(1.0, environment[EnvironmentType.self].value.currentProgress))
} else {
itemProgress = 0.0
}
transition.setFrame(layer: itemLayer.foregroundLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: itemProgress * itemFrame.width, height: itemFrame.height)))
}
}
@ -133,7 +169,7 @@ final class MediaNavigationStripComponent: Component {
return View(frame: CGRect())
}
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}

View File

@ -6,24 +6,51 @@ import SwiftSignalKit
import TelegramCore
public final class StoryContentItem {
public final class ExternalState {
public init() {
}
}
public final class Environment: Equatable {
public let externalState: ExternalState
public let presentationProgressUpdated: (Double) -> Void
public init(
externalState: ExternalState,
presentationProgressUpdated: @escaping (Double) -> Void
) {
self.externalState = externalState
self.presentationProgressUpdated = presentationProgressUpdated
}
public static func ==(lhs: Environment, rhs: Environment) -> Bool {
if lhs.externalState !== rhs.externalState {
return false
}
return true
}
}
public let id: AnyHashable
public let position: Int
public let component: AnyComponent<Empty>
public let component: AnyComponent<StoryContentItem.Environment>
public let centerInfoComponent: AnyComponent<Empty>?
public let rightInfoComponent: AnyComponent<Empty>?
public let targetMessageId: EngineMessage.Id?
public let preload: Signal<Never, NoError>?
public let hasLike: Bool
public let isMy: Bool
public init(
id: AnyHashable,
position: Int,
component: AnyComponent<Empty>,
component: AnyComponent<StoryContentItem.Environment>,
centerInfoComponent: AnyComponent<Empty>?,
rightInfoComponent: AnyComponent<Empty>?,
targetMessageId: EngineMessage.Id?,
preload: Signal<Never, NoError>?,
hasLike: Bool
hasLike: Bool,
isMy: Bool
) {
self.id = id
self.position = position
@ -33,6 +60,7 @@ public final class StoryContentItem {
self.targetMessageId = targetMessageId
self.preload = preload
self.hasLike = hasLike
self.isMy = isMy
}
}

View File

@ -65,7 +65,8 @@ public enum StoryChatContent {
},
targetMessageId: entry.message.id,
preload: preload,
hasLike: hasLike
hasLike: hasLike,
isMy: false//!entry.message.effectivelyIncoming(context.account.peerId)
))
}
return StoryContentItemSlice(

View File

@ -9,8 +9,11 @@ import PhotoResources
import SwiftSignalKit
import UniversalMediaPlayer
import TelegramUniversalVideoContent
import StoryContainerScreen
final class StoryMessageContentComponent: Component {
typealias EnvironmentType = StoryContentItem.Environment
let context: AccountContext
let message: EngineMessage
@ -88,6 +91,11 @@ final class StoryMessageContentComponent: Component {
private var component: StoryMessageContentComponent?
private weak var state: EmptyComponentState?
private var environment: StoryContentItem.Environment?
private var currentProgressStart: Double?
private var currentProgressTimer: SwiftSignalKit.Timer?
private var videoProgressDisposable: Disposable?
override init(frame: CGRect) {
self.imageNode = TransformImageNode()
@ -103,6 +111,8 @@ final class StoryMessageContentComponent: Component {
deinit {
self.fetchDisposable?.dispose()
self.currentProgressTimer?.invalidate()
self.videoProgressDisposable?.dispose()
}
private func performActionAfterImageContentLoaded(update: Bool) {
@ -149,9 +159,10 @@ final class StoryMessageContentComponent: Component {
}
}
func update(component: StoryMessageContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
func update(component: StoryMessageContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<StoryContentItem.Environment>, transition: Transition) -> CGSize {
self.component = component
self.state = state
self.environment = environment[StoryContentItem.Environment.self].value
var messageMedia: EngineMedia?
for media in component.message.media {
@ -266,6 +277,36 @@ final class StoryMessageContentComponent: Component {
self.imageNode.frame = CGRect(origin: CGPoint(), size: availableSize)
}
if let videoNode = self.videoNode {
if self.videoProgressDisposable == nil {
self.videoProgressDisposable = (videoNode.status
|> deliverOnMainQueue).start(next: { [weak self] status in
guard let self, let status, status.duration > 0.0 else {
return
}
let currentProgress = Double(status.timestamp / status.duration)
let clippedProgress = max(0.0, min(1.0, currentProgress))
self.environment?.presentationProgressUpdated(clippedProgress)
})
}
} else {
if self.currentProgressTimer == nil {
self.currentProgressStart = CFAbsoluteTimeGetCurrent()
self.currentProgressTimer = SwiftSignalKit.Timer(
timeout: 1.0 / 60.0,
repeat: true,
completion: { [weak self] in
guard let self, let currentProgressStart = self.currentProgressStart else {
return
}
let currentProgress = (CFAbsoluteTimeGetCurrent() - currentProgressStart) / 5.0
let clippedProgress = max(0.0, min(1.0, currentProgress))
self.environment?.presentationProgressUpdated(clippedProgress)
}, queue: .mainQueue())
self.currentProgressTimer?.start()
}
}
return availableSize
}
}
@ -274,7 +315,7 @@ final class StoryMessageContentComponent: Component {
return View(frame: CGRect())
}
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<StoryContentItem.Environment>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}

View File

@ -0,0 +1,21 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "StoryFooterPanelComponent",
module_name = "StoryFooterPanelComponent",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/Display",
"//submodules/ComponentFlow",
"//submodules/AppBundle",
"//submodules/Components/BundleIconComponent",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,48 @@
import Foundation
import UIKit
import Display
import ComponentFlow
import AppBundle
import BundleIconComponent
public final class StoryFooterPanelComponent: Component {
public init(
) {
}
public static func ==(lhs: StoryFooterPanelComponent, rhs: StoryFooterPanelComponent) -> Bool {
return true
}
public final class View: UIView {
private let viewStatsText = ComponentView<Empty>()
private let deleteButton = ComponentView<Empty>()
private let moreButton = ComponentView<Empty>()
private var component: StoryFooterPanelComponent?
private weak var state: EmptyComponentState?
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(component: StoryFooterPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
let baseHeight: CGFloat = 44.0
let size = CGSize(width: availableSize.width, height: baseHeight)
return size
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}

View File

@ -193,7 +193,7 @@ private final class AttachmentFileContext: AttachmentMediaPickerContext {
}
}
class AttachmentFileControllerImpl: ItemListController, AttachmentContainable {
class AttachmentFileControllerImpl: ItemListController, AttachmentFileController, AttachmentContainable {
public var requestAttachmentMenuExpansion: () -> Void = {}
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
@ -224,7 +224,7 @@ private struct AttachmentFileControllerState: Equatable {
var searching: Bool
}
func attachmentFileController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, bannedSendMedia: (Int32, Bool)?, presentGallery: @escaping () -> Void, presentFiles: @escaping () -> Void, send: @escaping (AnyMediaReference) -> Void) -> AttachmentFileControllerImpl {
func makeAttachmentFileControllerImpl(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, bannedSendMedia: (Int32, Bool)?, presentGallery: @escaping () -> Void, presentFiles: @escaping () -> Void, send: @escaping (AnyMediaReference) -> Void) -> AttachmentFileController {
let actionsDisposable = DisposableSet()
let statePromise = ValuePromise(AttachmentFileControllerState(searching: false), ignoreRepeated: true)

View File

@ -90,6 +90,9 @@ import FeaturedStickersScreen
import ChatEntityKeyboardInputNode
import StorageUsageScreen
import AvatarEditorScreen
import ChatScheduleTimeController
import ICloudResources
import LegacyCamera
#if DEBUG
import os.signpost
@ -12670,105 +12673,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
}
private func getCaptionPanelView() -> TGCaptionPanelView {
let presentationData = self.presentationData.withUpdated(theme: defaultDarkColorPresentationTheme)
var presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: presentationData.chatFontSize, bubbleCorners: presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: self.presentationInterfaceState.chatLocation, subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil)
var updateChatPresentationInterfaceStateImpl: (((ChatPresentationInterfaceState) -> ChatPresentationInterfaceState) -> Void)?
var ensureFocusedImpl: (() -> Void)?
let interfaceInteraction = ChatPanelInterfaceInteraction(updateTextInputStateAndMode: { f in
updateChatPresentationInterfaceStateImpl?({
let (updatedState, updatedMode) = f($0.interfaceState.effectiveInputState, $0.inputMode)
return $0.updatedInterfaceState { interfaceState in
return interfaceState.withUpdatedEffectiveInputState(updatedState)
}.updatedInputMode({ _ in updatedMode })
})
}, updateInputModeAndDismissedButtonKeyboardMessageId: { f in
updateChatPresentationInterfaceStateImpl?({
let (updatedInputMode, updatedClosedButtonKeyboardMessageId) = f($0)
return $0.updatedInputMode({ _ in return updatedInputMode }).updatedInterfaceState({
$0.withUpdatedMessageActionsState({ value in
var value = value
value.closedButtonKeyboardMessageId = updatedClosedButtonKeyboardMessageId
return value
})
})
})
}, openLinkEditing: { [weak self] in
if let strongSelf = self {
var selectionRange: Range<Int>?
var text: NSAttributedString?
var inputMode: ChatInputMode?
updateChatPresentationInterfaceStateImpl?({ state in
selectionRange = state.interfaceState.effectiveInputState.selectionRange
if let selectionRange = selectionRange {
text = state.interfaceState.effectiveInputState.inputText.attributedSubstring(from: NSRange(location: selectionRange.startIndex, length: selectionRange.count))
}
inputMode = state.inputMode
return state
})
var link: String?
if let text {
text.enumerateAttributes(in: NSMakeRange(0, text.length)) { attributes, _, _ in
if let linkAttribute = attributes[ChatTextInputAttributes.textUrl] as? ChatTextInputTextUrlAttribute {
link = linkAttribute.url
}
}
}
let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: (presentationData, .never()), account: strongSelf.context.account, text: text?.string ?? "", link: link, apply: { link in
if let inputMode = inputMode, let selectionRange = selectionRange {
if let link = link {
updateChatPresentationInterfaceStateImpl?({
return $0.updatedInterfaceState({
$0.withUpdatedEffectiveInputState(chatTextInputAddLinkAttribute($0.effectiveInputState, selectionRange: selectionRange, url: link))
})
})
}
ensureFocusedImpl?()
updateChatPresentationInterfaceStateImpl?({
return $0.updatedInputMode({ _ in return inputMode }).updatedInterfaceState({
$0.withUpdatedEffectiveInputState(ChatTextInputState(inputText: $0.effectiveInputState.inputText, selectionRange: selectionRange.endIndex ..< selectionRange.endIndex))
})
})
}
})
strongSelf.present(controller, in: .window(.root))
private func getCaptionPanelView() -> TGCaptionPanelView? {
return self.context.sharedContext.makeGalleryCaptionPanelView(context: self.context, chatLocation: self.presentationInterfaceState.chatLocation, customEmojiAvailable: self.presentationInterfaceState.customEmojiAvailable, present: { [weak self] c in
self?.present(c, in: .window(.root))
}, presentInGlobalOverlay: { [weak self] c in
guard let self else {
return
}
})
let inputPanelNode = AttachmentTextInputPanelNode(context: self.context, presentationInterfaceState: presentationInterfaceState, isCaption: true, presentController: { [weak self] c in
self?.presentInGlobalOverlay(c)
}, makeEntityInputView: { [weak self] in
guard let strongSelf = self else {
return nil
}
return EntityInputView(context: strongSelf.context, isDark: true, areCustomEmojiEnabled: strongSelf.presentationInterfaceState.customEmojiAvailable)
})
inputPanelNode.interfaceInteraction = interfaceInteraction
inputPanelNode.effectivePresentationInterfaceState = {
return presentationInterfaceState
}
updateChatPresentationInterfaceStateImpl = { [weak inputPanelNode] f in
let updatedPresentationInterfaceState = f(presentationInterfaceState)
let updateInputTextState = presentationInterfaceState.interfaceState.effectiveInputState != updatedPresentationInterfaceState.interfaceState.effectiveInputState
presentationInterfaceState = updatedPresentationInterfaceState
if let inputPanelNode = inputPanelNode, updateInputTextState {
inputPanelNode.updateInputTextState(updatedPresentationInterfaceState.interfaceState.effectiveInputState, animated: true)
}
}
ensureFocusedImpl = { [weak inputPanelNode] in
inputPanelNode?.ensureFocused()
}
return inputPanelNode
self.presentInGlobalOverlay(c)
}) as? TGCaptionPanelView
}
private func openCamera(cameraView: TGAttachmentCameraView? = nil) {
@ -13234,7 +13147,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
controller.prepareForReuse()
return
}
let controller = attachmentFileController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, bannedSendMedia: bannedSendFiles, presentGallery: { [weak self, weak attachmentController] in
let controller = strongSelf.context.sharedContext.makeAttachmentFileController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, bannedSendMedia: bannedSendFiles, presentGallery: { [weak self, weak attachmentController] in
attachmentController?.dismiss(animated: true)
self?.presentFileGallery()
}, presentFiles: { [weak self, weak attachmentController] in
@ -13252,8 +13165,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
})
let _ = currentFilesController.swap(controller)
completion(controller, controller.mediaPickerContext)
if let controller = controller as? AttachmentFileControllerImpl {
let _ = currentFilesController.swap(controller)
completion(controller, controller.mediaPickerContext)
}
case .location:
strongSelf.controllerNavigationDisposable.set(nil)
let existingController = currentLocationController.with { $0 }

View File

@ -343,7 +343,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
self.deactivateSearch()
}
public var mediaPickerContext: AttachmentMediaPickerContext {
public var mediaPickerContext: AttachmentMediaPickerContext? {
return ContactsPickerContext(controller: self)
}

View File

@ -12,6 +12,7 @@ import WallpaperResources
import MediaResources
import LocationUI
import ChatInterfaceState
import ICloudResources
private var telegramUIDeclaredEncodables: Void = {
declareEncodable(VideoLibraryMediaResource.self, f: { VideoLibraryMediaResource(decoder: $0) })

View File

@ -31,6 +31,10 @@ import ChatControllerInteraction
import ChatPresentationInterfaceState
import StorageUsageScreen
import DebugSettingsUI
import TextFormat
import ChatTextLinkEditUI
import AttachmentTextInputPanelNode
import ChatEntityKeyboardInputNode
private final class AccountUserInterfaceInUseContext {
let subscribers = Bag<(Bool) -> Void>()
@ -1610,6 +1614,107 @@ public final class SharedAccountContextImpl: SharedAccountContext {
})
}
public func makeAttachmentFileController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, bannedSendMedia: (Int32, Bool)?, presentGallery: @escaping () -> Void, presentFiles: @escaping () -> Void, send: @escaping (AnyMediaReference) -> Void) -> AttachmentFileController {
return makeAttachmentFileControllerImpl(context: context, updatedPresentationData: updatedPresentationData, bannedSendMedia: bannedSendMedia, presentGallery: presentGallery, presentFiles: presentFiles, send: send)
}
public func makeGalleryCaptionPanelView(context: AccountContext, chatLocation: ChatLocation, customEmojiAvailable: Bool, present: @escaping (ViewController) -> Void, presentInGlobalOverlay: @escaping (ViewController) -> Void) -> NSObject? {
var presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme)
var presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: presentationData.chatFontSize, bubbleCorners: presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: .standard(previewing: false), chatLocation: chatLocation, subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil)
var updateChatPresentationInterfaceStateImpl: (((ChatPresentationInterfaceState) -> ChatPresentationInterfaceState) -> Void)?
var ensureFocusedImpl: (() -> Void)?
let interfaceInteraction = ChatPanelInterfaceInteraction(updateTextInputStateAndMode: { f in
updateChatPresentationInterfaceStateImpl?({
let (updatedState, updatedMode) = f($0.interfaceState.effectiveInputState, $0.inputMode)
return $0.updatedInterfaceState { interfaceState in
return interfaceState.withUpdatedEffectiveInputState(updatedState)
}.updatedInputMode({ _ in updatedMode })
})
}, updateInputModeAndDismissedButtonKeyboardMessageId: { f in
updateChatPresentationInterfaceStateImpl?({
let (updatedInputMode, updatedClosedButtonKeyboardMessageId) = f($0)
return $0.updatedInputMode({ _ in return updatedInputMode }).updatedInterfaceState({
$0.withUpdatedMessageActionsState({ value in
var value = value
value.closedButtonKeyboardMessageId = updatedClosedButtonKeyboardMessageId
return value
})
})
})
}, openLinkEditing: {
var selectionRange: Range<Int>?
var text: NSAttributedString?
var inputMode: ChatInputMode?
updateChatPresentationInterfaceStateImpl?({ state in
selectionRange = state.interfaceState.effectiveInputState.selectionRange
if let selectionRange = selectionRange {
text = state.interfaceState.effectiveInputState.inputText.attributedSubstring(from: NSRange(location: selectionRange.startIndex, length: selectionRange.count))
}
inputMode = state.inputMode
return state
})
var link: String?
if let text {
text.enumerateAttributes(in: NSMakeRange(0, text.length)) { attributes, _, _ in
if let linkAttribute = attributes[ChatTextInputAttributes.textUrl] as? ChatTextInputTextUrlAttribute {
link = linkAttribute.url
}
}
}
let controller = chatTextLinkEditController(sharedContext: context.sharedContext, updatedPresentationData: (presentationData, .never()), account: context.account, text: text?.string ?? "", link: link, apply: { link in
if let inputMode = inputMode, let selectionRange = selectionRange {
if let link = link {
updateChatPresentationInterfaceStateImpl?({
return $0.updatedInterfaceState({
$0.withUpdatedEffectiveInputState(chatTextInputAddLinkAttribute($0.effectiveInputState, selectionRange: selectionRange, url: link))
})
})
}
ensureFocusedImpl?()
updateChatPresentationInterfaceStateImpl?({
return $0.updatedInputMode({ _ in return inputMode }).updatedInterfaceState({
$0.withUpdatedEffectiveInputState(ChatTextInputState(inputText: $0.effectiveInputState.inputText, selectionRange: selectionRange.endIndex ..< selectionRange.endIndex))
})
})
}
})
present(controller)
})
let inputPanelNode = AttachmentTextInputPanelNode(context: context, presentationInterfaceState: presentationInterfaceState, isCaption: true, presentController: { c in
presentInGlobalOverlay(c)
}, makeEntityInputView: {
return EntityInputView(context: context, isDark: true, areCustomEmojiEnabled: customEmojiAvailable)
})
inputPanelNode.interfaceInteraction = interfaceInteraction
inputPanelNode.effectivePresentationInterfaceState = {
return presentationInterfaceState
}
updateChatPresentationInterfaceStateImpl = { [weak inputPanelNode] f in
let updatedPresentationInterfaceState = f(presentationInterfaceState)
let updateInputTextState = presentationInterfaceState.interfaceState.effectiveInputState != updatedPresentationInterfaceState.interfaceState.effectiveInputState
presentationInterfaceState = updatedPresentationInterfaceState
if let inputPanelNode = inputPanelNode, updateInputTextState {
inputPanelNode.updateInputTextState(updatedPresentationInterfaceState.interfaceState.effectiveInputState, animated: true)
}
}
ensureFocusedImpl = { [weak inputPanelNode] in
inputPanelNode?.ensureFocused()
}
return inputPanelNode
}
public func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource) -> ViewController {
let mappedSource: PremiumSource
switch source {

View File

@ -11,6 +11,7 @@ import ChatInterfaceState
import WallpaperResources
import AppBundle
import SwiftSignalKit
import ICloudResources
func makeTelegramAccountAuxiliaryMethods(appDelegate: AppDelegate?) -> AccountAuxiliaryMethods {
return AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in

View File

@ -17,6 +17,7 @@ import DebugSettingsUI
import TabBarUI
import WallpaperBackgroundNode
import ChatPresentationInterfaceState
import LegacyCamera
private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceholderNode {
private var presentationData: PresentationData