mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Story improvements
This commit is contained in:
parent
060bc61011
commit
1c2f989fbd
@ -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>
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
22
submodules/ICloudResources/BUILD
Normal file
22
submodules/ICloudResources/BUILD
Normal 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",
|
||||
],
|
||||
)
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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?)
|
||||
|
@ -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) }
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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() {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
@ -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! {
|
||||
|
@ -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] {
|
||||
|
@ -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))
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
@ -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")
|
||||
}
|
||||
|
28
submodules/TelegramUI/Components/LegacyCamera/BUILD
Normal file
28
submodules/TelegramUI/Components/LegacyCamera/BUILD
Normal 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",
|
||||
],
|
||||
)
|
@ -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)
|
@ -14,6 +14,7 @@ swift_library(
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/AppBundle",
|
||||
"//submodules/TelegramUI/Components/TextFieldComponent",
|
||||
"//submodules/Components/BundleIconComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -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(
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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 }
|
||||
|
@ -343,7 +343,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
self.deactivateSearch()
|
||||
}
|
||||
|
||||
public var mediaPickerContext: AttachmentMediaPickerContext {
|
||||
public var mediaPickerContext: AttachmentMediaPickerContext? {
|
||||
return ContactsPickerContext(controller: self)
|
||||
}
|
||||
|
||||
|
@ -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) })
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -17,6 +17,7 @@ import DebugSettingsUI
|
||||
import TabBarUI
|
||||
import WallpaperBackgroundNode
|
||||
import ChatPresentationInterfaceState
|
||||
import LegacyCamera
|
||||
|
||||
private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceholderNode {
|
||||
private var presentationData: PresentationData
|
||||
|
Loading…
x
Reference in New Issue
Block a user