mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Content reporting
This commit is contained in:
parent
762eb19cc1
commit
1a3f7dc42d
@ -12285,6 +12285,8 @@ Sorry for the inconvenience.";
|
||||
"Stars.Intro.Transaction.FragmentWithdrawal.Subtitle" = "via Fragment";
|
||||
"Stars.Intro.Transaction.TelegramAds.Title" = "Withdrawal";
|
||||
"Stars.Intro.Transaction.TelegramAds.Subtitle" = "via Telegram Ads";
|
||||
"Stars.Intro.Transaction.Gift" = "Gift";
|
||||
"Stars.Intro.Transaction.ConvertedGift" = "Converted Gift";
|
||||
"Stars.Intro.Transaction.Unsupported.Title" = "Unsupported";
|
||||
"Stars.Intro.Transaction.Refund" = "Refund";
|
||||
|
||||
|
@ -1020,6 +1020,8 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func makeStarsIntroScreen(context: AccountContext) -> ViewController
|
||||
func makeGiftViewScreen(context: AccountContext, message: EngineMessage) -> ViewController
|
||||
|
||||
func makeContentReportScreen(context: AccountContext, subject: ReportContentSubject, forceDark: Bool, present: @escaping (ViewController) -> Void, completion: @escaping () -> Void)
|
||||
|
||||
func makeMiniAppListScreenInitialData(context: AccountContext) -> Signal<MiniAppListScreenInitialData, NoError>
|
||||
func makeMiniAppListScreen(context: AccountContext, initialData: MiniAppListScreenInitialData) -> ViewController
|
||||
|
||||
|
@ -631,6 +631,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-2083123262] = { return Api.MessageReplies.parse_messageReplies($0) }
|
||||
dict[-1346631205] = { return Api.MessageReplyHeader.parse_messageReplyHeader($0) }
|
||||
dict[240843065] = { return Api.MessageReplyHeader.parse_messageReplyStoryHeader($0) }
|
||||
dict[2030298073] = { return Api.MessageReportOption.parse_messageReportOption($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) }
|
||||
@ -803,6 +804,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[777640226] = { return Api.ReportReason.parse_inputReportReasonPornography($0) }
|
||||
dict[1490799288] = { return Api.ReportReason.parse_inputReportReasonSpam($0) }
|
||||
dict[505595789] = { return Api.ReportReason.parse_inputReportReasonViolence($0) }
|
||||
dict[1862904881] = { return Api.ReportResult.parse_reportResultAddComment($0) }
|
||||
dict[-253435722] = { return Api.ReportResult.parse_reportResultChooseOption($0) }
|
||||
dict[-1917633461] = { return Api.ReportResult.parse_reportResultReported($0) }
|
||||
dict[865857388] = { return Api.RequestPeerType.parse_requestPeerTypeBroadcast($0) }
|
||||
dict[-906990053] = { return Api.RequestPeerType.parse_requestPeerTypeChat($0) }
|
||||
dict[1597737472] = { return Api.RequestPeerType.parse_requestPeerTypeUser($0) }
|
||||
@ -898,7 +902,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1401868056] = { return Api.StarsSubscription.parse_starsSubscription($0) }
|
||||
dict[88173912] = { return Api.StarsSubscriptionPricing.parse_starsSubscriptionPricing($0) }
|
||||
dict[198776256] = { return Api.StarsTopupOption.parse_starsTopupOption($0) }
|
||||
dict[-294313259] = { return Api.StarsTransaction.parse_starsTransaction($0) }
|
||||
dict[178185410] = { return Api.StarsTransaction.parse_starsTransaction($0) }
|
||||
dict[-670195363] = { return Api.StarsTransactionPeer.parse_starsTransactionPeer($0) }
|
||||
dict[1617438738] = { return Api.StarsTransactionPeer.parse_starsTransactionPeerAds($0) }
|
||||
dict[-1269320843] = { return Api.StarsTransactionPeer.parse_starsTransactionPeerAppStore($0) }
|
||||
@ -1853,6 +1857,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageReplyHeader:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageReportOption:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageViews:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessagesFilter:
|
||||
@ -1969,6 +1975,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ReportReason:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ReportResult:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.RequestPeerType:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.RequestedPeer:
|
||||
|
@ -482,6 +482,46 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum MessageReportOption: TypeConstructorDescription {
|
||||
case messageReportOption(text: String, option: Buffer)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .messageReportOption(let text, let option):
|
||||
if boxed {
|
||||
buffer.appendInt32(2030298073)
|
||||
}
|
||||
serializeString(text, buffer: buffer, boxed: false)
|
||||
serializeBytes(option, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .messageReportOption(let text, let option):
|
||||
return ("messageReportOption", [("text", text as Any), ("option", option as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_messageReportOption(_ reader: BufferReader) -> MessageReportOption? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: Buffer?
|
||||
_2 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.MessageReportOption.messageReportOption(text: _1!, option: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum MessageViews: TypeConstructorDescription {
|
||||
case messageViews(flags: Int32, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?)
|
||||
@ -902,87 +942,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum NotificationSound: TypeConstructorDescription {
|
||||
case notificationSoundDefault
|
||||
case notificationSoundLocal(title: String, data: String)
|
||||
case notificationSoundNone
|
||||
case notificationSoundRingtone(id: Int64)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .notificationSoundDefault:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1746354498)
|
||||
}
|
||||
|
||||
break
|
||||
case .notificationSoundLocal(let title, let data):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2096391452)
|
||||
}
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
serializeString(data, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .notificationSoundNone:
|
||||
if boxed {
|
||||
buffer.appendInt32(1863070943)
|
||||
}
|
||||
|
||||
break
|
||||
case .notificationSoundRingtone(let id):
|
||||
if boxed {
|
||||
buffer.appendInt32(-9666487)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .notificationSoundDefault:
|
||||
return ("notificationSoundDefault", [])
|
||||
case .notificationSoundLocal(let title, let data):
|
||||
return ("notificationSoundLocal", [("title", title as Any), ("data", data as Any)])
|
||||
case .notificationSoundNone:
|
||||
return ("notificationSoundNone", [])
|
||||
case .notificationSoundRingtone(let id):
|
||||
return ("notificationSoundRingtone", [("id", id as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_notificationSoundDefault(_ reader: BufferReader) -> NotificationSound? {
|
||||
return Api.NotificationSound.notificationSoundDefault
|
||||
}
|
||||
public static func parse_notificationSoundLocal(_ reader: BufferReader) -> NotificationSound? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.NotificationSound.notificationSoundLocal(title: _1!, data: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_notificationSoundNone(_ reader: BufferReader) -> NotificationSound? {
|
||||
return Api.NotificationSound.notificationSoundNone
|
||||
}
|
||||
public static func parse_notificationSoundRingtone(_ reader: BufferReader) -> NotificationSound? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.NotificationSound.notificationSoundRingtone(id: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,87 @@
|
||||
public extension Api {
|
||||
enum NotificationSound: TypeConstructorDescription {
|
||||
case notificationSoundDefault
|
||||
case notificationSoundLocal(title: String, data: String)
|
||||
case notificationSoundNone
|
||||
case notificationSoundRingtone(id: Int64)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .notificationSoundDefault:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1746354498)
|
||||
}
|
||||
|
||||
break
|
||||
case .notificationSoundLocal(let title, let data):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2096391452)
|
||||
}
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
serializeString(data, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .notificationSoundNone:
|
||||
if boxed {
|
||||
buffer.appendInt32(1863070943)
|
||||
}
|
||||
|
||||
break
|
||||
case .notificationSoundRingtone(let id):
|
||||
if boxed {
|
||||
buffer.appendInt32(-9666487)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .notificationSoundDefault:
|
||||
return ("notificationSoundDefault", [])
|
||||
case .notificationSoundLocal(let title, let data):
|
||||
return ("notificationSoundLocal", [("title", title as Any), ("data", data as Any)])
|
||||
case .notificationSoundNone:
|
||||
return ("notificationSoundNone", [])
|
||||
case .notificationSoundRingtone(let id):
|
||||
return ("notificationSoundRingtone", [("id", id as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_notificationSoundDefault(_ reader: BufferReader) -> NotificationSound? {
|
||||
return Api.NotificationSound.notificationSoundDefault
|
||||
}
|
||||
public static func parse_notificationSoundLocal(_ reader: BufferReader) -> NotificationSound? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.NotificationSound.notificationSoundLocal(title: _1!, data: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_notificationSoundNone(_ reader: BufferReader) -> NotificationSound? {
|
||||
return Api.NotificationSound.notificationSoundNone
|
||||
}
|
||||
public static func parse_notificationSoundRingtone(_ reader: BufferReader) -> NotificationSound? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.NotificationSound.notificationSoundRingtone(id: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum NotifyPeer: TypeConstructorDescription {
|
||||
case notifyBroadcasts
|
||||
|
@ -134,6 +134,88 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum ReportResult: TypeConstructorDescription {
|
||||
case reportResultAddComment(flags: Int32, option: Buffer)
|
||||
case reportResultChooseOption(title: String, options: [Api.MessageReportOption])
|
||||
case reportResultReported
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .reportResultAddComment(let flags, let option):
|
||||
if boxed {
|
||||
buffer.appendInt32(1862904881)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeBytes(option, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .reportResultChooseOption(let title, let options):
|
||||
if boxed {
|
||||
buffer.appendInt32(-253435722)
|
||||
}
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(options.count))
|
||||
for item in options {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .reportResultReported:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1917633461)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .reportResultAddComment(let flags, let option):
|
||||
return ("reportResultAddComment", [("flags", flags as Any), ("option", option as Any)])
|
||||
case .reportResultChooseOption(let title, let options):
|
||||
return ("reportResultChooseOption", [("title", title as Any), ("options", options as Any)])
|
||||
case .reportResultReported:
|
||||
return ("reportResultReported", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_reportResultAddComment(_ reader: BufferReader) -> ReportResult? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Buffer?
|
||||
_2 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.ReportResult.reportResultAddComment(flags: _1!, option: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_reportResultChooseOption(_ reader: BufferReader) -> ReportResult? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: [Api.MessageReportOption]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageReportOption.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.ReportResult.reportResultChooseOption(title: _1!, options: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_reportResultReported(_ reader: BufferReader) -> ReportResult? {
|
||||
return Api.ReportResult.reportResultReported
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum RequestPeerType: TypeConstructorDescription {
|
||||
case requestPeerTypeBroadcast(flags: Int32, hasUsername: Api.Bool?, userAdminRights: Api.ChatAdminRights?, botAdminRights: Api.ChatAdminRights?)
|
||||
|
@ -1002,13 +1002,13 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
enum StarsTransaction: TypeConstructorDescription {
|
||||
case starsTransaction(flags: Int32, id: String, stars: Int64, date: Int32, peer: Api.StarsTransactionPeer, title: String?, description: String?, photo: Api.WebDocument?, transactionDate: Int32?, transactionUrl: String?, botPayload: Buffer?, msgId: Int32?, extendedMedia: [Api.MessageMedia]?, subscriptionPeriod: Int32?, giveawayPostId: Int32?)
|
||||
case starsTransaction(flags: Int32, id: String, stars: Int64, date: Int32, peer: Api.StarsTransactionPeer, title: String?, description: String?, photo: Api.WebDocument?, transactionDate: Int32?, transactionUrl: String?, botPayload: Buffer?, msgId: Int32?, extendedMedia: [Api.MessageMedia]?, subscriptionPeriod: Int32?, giveawayPostId: Int32?, stargift: Api.StarGift?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo, let transactionDate, let transactionUrl, let botPayload, let msgId, let extendedMedia, let subscriptionPeriod, let giveawayPostId):
|
||||
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo, let transactionDate, let transactionUrl, let botPayload, let msgId, let extendedMedia, let subscriptionPeriod, let giveawayPostId, let stargift):
|
||||
if boxed {
|
||||
buffer.appendInt32(-294313259)
|
||||
buffer.appendInt32(178185410)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(id, buffer: buffer, boxed: false)
|
||||
@ -1029,14 +1029,15 @@ public extension Api {
|
||||
}}
|
||||
if Int(flags) & Int(1 << 12) != 0 {serializeInt32(subscriptionPeriod!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 13) != 0 {serializeInt32(giveawayPostId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 14) != 0 {stargift!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo, let transactionDate, let transactionUrl, let botPayload, let msgId, let extendedMedia, let subscriptionPeriod, let giveawayPostId):
|
||||
return ("starsTransaction", [("flags", flags as Any), ("id", id as Any), ("stars", stars as Any), ("date", date as Any), ("peer", peer as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("transactionDate", transactionDate as Any), ("transactionUrl", transactionUrl as Any), ("botPayload", botPayload as Any), ("msgId", msgId as Any), ("extendedMedia", extendedMedia as Any), ("subscriptionPeriod", subscriptionPeriod as Any), ("giveawayPostId", giveawayPostId as Any)])
|
||||
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo, let transactionDate, let transactionUrl, let botPayload, let msgId, let extendedMedia, let subscriptionPeriod, let giveawayPostId, let stargift):
|
||||
return ("starsTransaction", [("flags", flags as Any), ("id", id as Any), ("stars", stars as Any), ("date", date as Any), ("peer", peer as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("transactionDate", transactionDate as Any), ("transactionUrl", transactionUrl as Any), ("botPayload", botPayload as Any), ("msgId", msgId as Any), ("extendedMedia", extendedMedia as Any), ("subscriptionPeriod", subscriptionPeriod as Any), ("giveawayPostId", giveawayPostId as Any), ("stargift", stargift as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -1077,6 +1078,10 @@ public extension Api {
|
||||
if Int(_1!) & Int(1 << 12) != 0 {_14 = reader.readInt32() }
|
||||
var _15: Int32?
|
||||
if Int(_1!) & Int(1 << 13) != 0 {_15 = reader.readInt32() }
|
||||
var _16: Api.StarGift?
|
||||
if Int(_1!) & Int(1 << 14) != 0 {if let signature = reader.readInt32() {
|
||||
_16 = Api.parse(reader, signature: signature) as? Api.StarGift
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
@ -1092,8 +1097,9 @@ public extension Api {
|
||||
let _c13 = (Int(_1!) & Int(1 << 9) == 0) || _13 != nil
|
||||
let _c14 = (Int(_1!) & Int(1 << 12) == 0) || _14 != nil
|
||||
let _c15 = (Int(_1!) & Int(1 << 13) == 0) || _15 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 {
|
||||
return Api.StarsTransaction.starsTransaction(flags: _1!, id: _2!, stars: _3!, date: _4!, peer: _5!, title: _6, description: _7, photo: _8, transactionDate: _9, transactionUrl: _10, botPayload: _11, msgId: _12, extendedMedia: _13, subscriptionPeriod: _14, giveawayPostId: _15)
|
||||
let _c16 = (Int(_1!) & Int(1 << 14) == 0) || _16 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 {
|
||||
return Api.StarsTransaction.starsTransaction(flags: _1!, id: _2!, stars: _3!, date: _4!, peer: _5!, title: _6, description: _7, photo: _8, transactionDate: _9, transactionUrl: _10, botPayload: _11, msgId: _12, extendedMedia: _13, subscriptionPeriod: _14, giveawayPostId: _15, stargift: _16)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -7380,22 +7380,22 @@ public extension Api.functions.messages {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func report(peer: Api.InputPeer, id: [Int32], reason: Api.ReportReason, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
static func report(peer: Api.InputPeer, id: [Int32], option: Buffer, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.ReportResult>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1991005362)
|
||||
buffer.appendInt32(-59199589)
|
||||
peer.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(id.count))
|
||||
for item in id {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
reason.serialize(buffer, true)
|
||||
serializeBytes(option, buffer: buffer, boxed: false)
|
||||
serializeString(message, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.report", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("reason", String(describing: reason)), ("message", String(describing: message))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
return (FunctionDescription(name: "messages.report", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("option", String(describing: option)), ("message", String(describing: message))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ReportResult? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
var result: Api.ReportResult?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
result = Api.parse(reader, signature: signature) as? Api.ReportResult
|
||||
}
|
||||
return result
|
||||
})
|
||||
@ -10787,22 +10787,22 @@ public extension Api.functions.stories {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.stories {
|
||||
static func report(peer: Api.InputPeer, id: [Int32], reason: Api.ReportReason, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
static func report(peer: Api.InputPeer, id: [Int32], option: Buffer, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.ReportResult>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(421788300)
|
||||
buffer.appendInt32(433646405)
|
||||
peer.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(id.count))
|
||||
for item in id {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
reason.serialize(buffer, true)
|
||||
serializeBytes(option, buffer: buffer, boxed: false)
|
||||
serializeString(message, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "stories.report", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("reason", String(describing: reason)), ("message", String(describing: message))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
return (FunctionDescription(name: "stories.report", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("option", String(describing: option)), ("message", String(describing: message))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ReportResult? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
var result: Api.ReportResult?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
result = Api.parse(reader, signature: signature) as? Api.ReportResult
|
||||
}
|
||||
return result
|
||||
})
|
||||
|
@ -0,0 +1,82 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import TelegramApi
|
||||
import MtProtoKit
|
||||
|
||||
public enum ReportContentResult {
|
||||
public struct Option: Equatable {
|
||||
public let text: String
|
||||
public let option: Data
|
||||
}
|
||||
|
||||
case options(title: String, options: [Option])
|
||||
case addComment(optional: Bool, option: Data)
|
||||
case reported
|
||||
}
|
||||
|
||||
public enum ReportContentError {
|
||||
case generic
|
||||
case messageIdRequired
|
||||
}
|
||||
|
||||
public enum ReportContentSubject: Equatable {
|
||||
case peer(EnginePeer.Id)
|
||||
case messages([EngineMessage.Id])
|
||||
case stories(EnginePeer.Id, [Int32])
|
||||
|
||||
var peerId: EnginePeer.Id {
|
||||
switch self {
|
||||
case let .peer(peerId):
|
||||
return peerId
|
||||
case let .messages(messageIds):
|
||||
return messageIds.first!.peerId
|
||||
case let .stories(peerId, _):
|
||||
return peerId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_reportContent(account: Account, subject: ReportContentSubject, option: Data?, message: String?) -> Signal<ReportContentResult, ReportContentError> {
|
||||
return account.postbox.transaction { transaction -> Signal<ReportContentResult, ReportContentError> in
|
||||
guard let peer = transaction.getPeer(subject.peerId), let inputPeer = apiInputPeer(peer) else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
let request: Signal<Api.ReportResult, MTRpcError>
|
||||
if case let .stories(_, ids) = subject {
|
||||
request = account.network.request(Api.functions.stories.report(peer: inputPeer, id: ids, option: Buffer(data: option), message: message ?? ""))
|
||||
} else {
|
||||
var ids: [Int32] = []
|
||||
if case let .messages(messageIds) = subject {
|
||||
ids = messageIds.map { $0.id }
|
||||
}
|
||||
request = account.network.request(Api.functions.messages.report(peer: inputPeer, id: ids, option: Buffer(data: option), message: message ?? ""))
|
||||
}
|
||||
|
||||
return request
|
||||
|> mapError { error -> ReportContentError in
|
||||
if error.errorDescription == "MESSAGE_ID_REQUIRED" {
|
||||
return .messageIdRequired
|
||||
}
|
||||
return .generic
|
||||
}
|
||||
|> map { result -> ReportContentResult in
|
||||
switch result {
|
||||
case let .reportResultChooseOption(title, options):
|
||||
return .options(title: title, options: options.map {
|
||||
switch $0 {
|
||||
case let .messageReportOption(text, option):
|
||||
return ReportContentResult.Option(text: text, option: option.makeData())
|
||||
}
|
||||
})
|
||||
case let .reportResultAddComment(flags, option):
|
||||
return .addComment(optional: (flags & (1 << 0)) != 0, option: option.makeData())
|
||||
case .reportResultReported:
|
||||
return .reported
|
||||
}
|
||||
}
|
||||
}
|
||||
|> castError(ReportContentError.self)
|
||||
|> switchToLatest
|
||||
}
|
@ -1463,6 +1463,10 @@ public extension TelegramEngine {
|
||||
return _internal_reportAdMessage(account: self.account, peerId: peerId, opaqueId: opaqueId, option: option)
|
||||
}
|
||||
|
||||
public func reportContent(subject: ReportContentSubject, option: Data?, message: String?) -> Signal<ReportContentResult, ReportContentError> {
|
||||
return _internal_reportContent(account: self.account, subject: subject, option: option, message: message)
|
||||
}
|
||||
|
||||
public func updateExtendedMedia(messageIds: [EngineMessage.Id]) -> Signal<Never, NoError> {
|
||||
return _internal_updateExtendedMedia(account: self.account, messageIds: messageIds)
|
||||
}
|
||||
|
@ -468,7 +468,7 @@ private final class StarsContextImpl {
|
||||
}
|
||||
var transactions = state.transactions
|
||||
if addTransaction {
|
||||
transactions.insert(.init(flags: [.isLocal], id: "\(arc4random())", count: balance, date: Int32(Date().timeIntervalSince1970), peer: .appStore, title: nil, description: nil, photo: nil, transactionDate: nil, transactionUrl: nil, paidMessageId: nil, giveawayMessageId: nil, media: [], subscriptionPeriod: nil), at: 0)
|
||||
transactions.insert(.init(flags: [.isLocal], id: "\(arc4random())", count: balance, date: Int32(Date().timeIntervalSince1970), peer: .appStore, title: nil, description: nil, photo: nil, transactionDate: nil, transactionUrl: nil, paidMessageId: nil, giveawayMessageId: nil, media: [], subscriptionPeriod: nil, starGift: nil), at: 0)
|
||||
}
|
||||
|
||||
self.updateState(StarsContext.State(flags: [.isPendingBalance], balance: max(0, state.balance + balance), subscriptions: state.subscriptions, canLoadMoreSubscriptions: state.canLoadMoreSubscriptions, transactions: transactions, canLoadMoreTransactions: state.canLoadMoreTransactions, isLoading: state.isLoading))
|
||||
@ -490,7 +490,7 @@ private final class StarsContextImpl {
|
||||
private extension StarsContext.State.Transaction {
|
||||
init?(apiTransaction: Api.StarsTransaction, peerId: EnginePeer.Id?, transaction: Transaction) {
|
||||
switch apiTransaction {
|
||||
case let .starsTransaction(apiFlags, id, stars, date, transactionPeer, title, description, photo, transactionDate, transactionUrl, _, messageId, extendedMedia, subscriptionPeriod, giveawayPostId):
|
||||
case let .starsTransaction(apiFlags, id, stars, date, transactionPeer, title, description, photo, transactionDate, transactionUrl, _, messageId, extendedMedia, subscriptionPeriod, giveawayPostId, starGift):
|
||||
let parsedPeer: StarsContext.State.Transaction.Peer
|
||||
var paidMessageId: MessageId?
|
||||
var giveawayMessageId: MessageId?
|
||||
@ -544,7 +544,7 @@ private extension StarsContext.State.Transaction {
|
||||
|
||||
let media = extendedMedia.flatMap({ $0.compactMap { textMediaAndExpirationTimerFromApiMedia($0, PeerId(0)).media } }) ?? []
|
||||
let _ = subscriptionPeriod
|
||||
self.init(flags: flags, id: id, count: stars, date: date, peer: parsedPeer, title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), transactionDate: transactionDate, transactionUrl: transactionUrl, paidMessageId: paidMessageId, giveawayMessageId: giveawayMessageId, media: media, subscriptionPeriod: subscriptionPeriod)
|
||||
self.init(flags: flags, id: id, count: stars, date: date, peer: parsedPeer, title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), transactionDate: transactionDate, transactionUrl: transactionUrl, paidMessageId: paidMessageId, giveawayMessageId: giveawayMessageId, media: media, subscriptionPeriod: subscriptionPeriod, starGift: starGift.flatMap { StarGift(apiStarGift: $0) })
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -613,6 +613,7 @@ public final class StarsContext {
|
||||
public let giveawayMessageId: MessageId?
|
||||
public let media: [Media]
|
||||
public let subscriptionPeriod: Int32?
|
||||
public let starGift: StarGift?
|
||||
|
||||
public init(
|
||||
flags: Flags,
|
||||
@ -628,7 +629,8 @@ public final class StarsContext {
|
||||
paidMessageId: MessageId?,
|
||||
giveawayMessageId: MessageId?,
|
||||
media: [Media],
|
||||
subscriptionPeriod: Int32?
|
||||
subscriptionPeriod: Int32?,
|
||||
starGift: StarGift?
|
||||
) {
|
||||
self.flags = flags
|
||||
self.id = id
|
||||
@ -644,6 +646,7 @@ public final class StarsContext {
|
||||
self.giveawayMessageId = giveawayMessageId
|
||||
self.media = media
|
||||
self.subscriptionPeriod = subscriptionPeriod
|
||||
self.starGift = starGift
|
||||
}
|
||||
|
||||
public static func == (lhs: Transaction, rhs: Transaction) -> Bool {
|
||||
@ -689,6 +692,9 @@ public final class StarsContext {
|
||||
if lhs.subscriptionPeriod != rhs.subscriptionPeriod {
|
||||
return false
|
||||
}
|
||||
if lhs.starGift != rhs.starGift {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -148,42 +148,44 @@ func _internal_reportPeerPhoto(account: Account, peerId: PeerId, reason: ReportR
|
||||
}
|
||||
|
||||
func _internal_reportPeerMessages(account: Account, messageIds: [MessageId], reason: ReportReason, message: String) -> Signal<Void, NoError> {
|
||||
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
let groupedIds = messagesIdsGroupedByPeerId(messageIds)
|
||||
let signals = groupedIds.values.compactMap { ids -> Signal<Void, NoError>? in
|
||||
guard let peerId = ids.first?.peerId, let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) else {
|
||||
return nil
|
||||
}
|
||||
return account.network.request(Api.functions.messages.report(peer: inputPeer, id: ids.map { $0.id }, reason: reason.apiReason, message: message))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|
||||
return combineLatest(signals)
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
} |> switchToLatest
|
||||
return .complete()
|
||||
// return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
// let groupedIds = messagesIdsGroupedByPeerId(messageIds)
|
||||
// let signals = groupedIds.values.compactMap { ids -> Signal<Void, NoError>? in
|
||||
// guard let peerId = ids.first?.peerId, let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) else {
|
||||
// return nil
|
||||
// }
|
||||
// return account.network.request(Api.functions.messages.report(peer: inputPeer, id: ids.map { $0.id }, reason: reason.apiReason, message: message))
|
||||
// |> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
// return .single(.boolFalse)
|
||||
// }
|
||||
// |> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
// return .complete()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return combineLatest(signals)
|
||||
// |> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
// return .complete()
|
||||
// }
|
||||
// } |> switchToLatest
|
||||
}
|
||||
|
||||
func _internal_reportPeerStory(account: Account, peerId: PeerId, storyId: Int32, reason: ReportReason, message: String) -> Signal<Void, NoError> {
|
||||
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
return account.network.request(Api.functions.stories.report(peer: inputPeer, id: [storyId], reason: reason.apiReason, message: message))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
} |> switchToLatest
|
||||
return .complete()
|
||||
// return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
// if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
// return account.network.request(Api.functions.stories.report(peer: inputPeer, id: [storyId], reason: reason.apiReason, message: message))
|
||||
// |> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
// return .single(.boolFalse)
|
||||
// }
|
||||
// |> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
// return .complete()
|
||||
// }
|
||||
// } else {
|
||||
// return .complete()
|
||||
// }
|
||||
// } |> switchToLatest
|
||||
}
|
||||
|
||||
func _internal_reportPeerReaction(account: Account, authorId: PeerId, messageId: MessageId) -> Signal<Never, NoError> {
|
||||
|
@ -461,6 +461,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/MiniAppListScreen",
|
||||
"//submodules/TelegramUI/Components/Stars/StarsIntroScreen",
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftOptionsScreen",
|
||||
"//submodules/TelegramUI/Components/ContentReportScreen",
|
||||
] + select({
|
||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||
"//build-system:ios_sim_arm64": [],
|
||||
|
@ -396,6 +396,7 @@ private final class SheetContent: CombinedComponent {
|
||||
let navigation = navigation.update(
|
||||
component: NavigationStackComponent(
|
||||
items: items,
|
||||
clipContent: false,
|
||||
requestPop: { [weak state] in
|
||||
state?.pushedOptions.removeLast()
|
||||
update(.spring(duration: 0.45))
|
||||
|
41
submodules/TelegramUI/Components/ContentReportScreen/BUILD
Normal file
41
submodules/TelegramUI/Components/ContentReportScreen/BUILD
Normal file
@ -0,0 +1,41 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "ContentReportScreen",
|
||||
module_name = "ContentReportScreen",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/Postbox",
|
||||
"//submodules/TelegramCore",
|
||||
"//submodules/SSignalKit/SwiftSignalKit",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/Components/ViewControllerComponent",
|
||||
"//submodules/Components/ComponentDisplayAdapters",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
"//submodules/Components/BalancedTextComponent",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/AppBundle",
|
||||
"//submodules/ItemListUI",
|
||||
"//submodules/TelegramStringFormatting",
|
||||
"//submodules/PresentationDataUtils",
|
||||
"//submodules/Components/SheetComponent",
|
||||
"//submodules/UndoUI",
|
||||
"//submodules/TelegramUI/Components/ListSectionComponent",
|
||||
"//submodules/TelegramUI/Components/ListActionItemComponent",
|
||||
"//submodules/TelegramUI/Components/NavigationStackComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
"//submodules/TelegramUI/Components/ListMultilineTextFieldItemComponent",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -0,0 +1,726 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import Markdown
|
||||
import TextFormat
|
||||
import TelegramPresentationData
|
||||
import ViewControllerComponent
|
||||
import SheetComponent
|
||||
import BalancedTextComponent
|
||||
import MultilineTextComponent
|
||||
import ListSectionComponent
|
||||
import ListActionItemComponent
|
||||
import NavigationStackComponent
|
||||
import ItemListUI
|
||||
import UndoUI
|
||||
import AccountContext
|
||||
import LottieComponent
|
||||
import TextFieldComponent
|
||||
import ListMultilineTextFieldItemComponent
|
||||
import ButtonComponent
|
||||
|
||||
private enum ReportResult {
|
||||
case reported
|
||||
}
|
||||
|
||||
private final class SheetPageContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
enum Content: Equatable {
|
||||
struct Item: Equatable {
|
||||
let title: String
|
||||
let option: Data
|
||||
}
|
||||
|
||||
case options(items: [Item])
|
||||
case comment(isOptional: Bool, option: Data)
|
||||
}
|
||||
|
||||
let context: AccountContext
|
||||
let isFirst: Bool
|
||||
let title: String?
|
||||
let subtitle: String
|
||||
let content: Content
|
||||
let action: (Content.Item, String?) -> Void
|
||||
let pop: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
isFirst: Bool,
|
||||
title: String?,
|
||||
subtitle: String,
|
||||
content: Content,
|
||||
action: @escaping (Content.Item, String?) -> Void,
|
||||
pop: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.isFirst = isFirst
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
self.content = content
|
||||
self.action = action
|
||||
self.pop = pop
|
||||
}
|
||||
|
||||
static func ==(lhs: SheetPageContent, rhs: SheetPageContent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.subtitle != rhs.subtitle {
|
||||
return false
|
||||
}
|
||||
if lhs.content != rhs.content {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
var backArrowImage: (UIImage, PresentationTheme)?
|
||||
|
||||
let playOnce = ActionSlot<Void>()
|
||||
private var didPlayAnimation = false
|
||||
|
||||
let textInputState = ListMultilineTextFieldItemComponent.ExternalState()
|
||||
|
||||
func playAnimationIfNeeded() {
|
||||
guard !self.didPlayAnimation else {
|
||||
return
|
||||
}
|
||||
self.didPlayAnimation = true
|
||||
self.playOnce.invoke(Void())
|
||||
}
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State()
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let background = Child(RoundedRectangle.self)
|
||||
let back = Child(Button.self)
|
||||
let title = Child(Text.self)
|
||||
let animation = Child(LottieComponent.self)
|
||||
let section = Child(ListSectionComponent.self)
|
||||
let button = Child(ButtonComponent.self)
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
let component = context.component
|
||||
let state = context.state
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let theme = environment.theme
|
||||
let strings = environment.strings
|
||||
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
|
||||
var contentSize = CGSize(width: context.availableSize.width, height: 18.0)
|
||||
|
||||
let background = background.update(
|
||||
component: RoundedRectangle(color: theme.list.modalBlocksBackgroundColor, cornerRadius: 8.0),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: 1000.0),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(background
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: background.size.height / 2.0))
|
||||
)
|
||||
|
||||
let backArrowImage: UIImage
|
||||
if let (cached, cachedTheme) = state.backArrowImage, cachedTheme === theme {
|
||||
backArrowImage = cached
|
||||
} else {
|
||||
backArrowImage = NavigationBarTheme.generateBackArrowImage(color: theme.list.itemAccentColor)!
|
||||
state.backArrowImage = (backArrowImage, theme)
|
||||
}
|
||||
|
||||
let backContents: AnyComponent<Empty>
|
||||
if component.isFirst {
|
||||
backContents = AnyComponent(Text(text: strings.Common_Cancel, font: Font.regular(17.0), color: theme.list.itemAccentColor))
|
||||
} else {
|
||||
backContents = AnyComponent(
|
||||
HStack([
|
||||
AnyComponentWithIdentity(id: "arrow", component: AnyComponent(Image(image: backArrowImage, contentMode: .center))),
|
||||
AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: strings.Common_Back, font: Font.regular(17.0), color: theme.list.itemAccentColor)))
|
||||
], spacing: 6.0)
|
||||
)
|
||||
}
|
||||
let back = back.update(
|
||||
component: Button(
|
||||
content: backContents,
|
||||
action: {
|
||||
component.pop()
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(back
|
||||
.position(CGPoint(x: sideInset + back.size.width / 2.0 - (component.title != nil ? 8.0 : 0.0), y: contentSize.height + back.size.height / 2.0))
|
||||
)
|
||||
|
||||
let constrainedTitleWidth = context.availableSize.width - (back.size.width + 16.0) * 2.0
|
||||
|
||||
let titleString: String
|
||||
if let title = component.title {
|
||||
titleString = title
|
||||
} else {
|
||||
titleString = ""
|
||||
}
|
||||
|
||||
let title = title.update(
|
||||
component: Text(text: titleString, font: Font.semibold(17.0), color: theme.list.itemPrimaryTextColor),
|
||||
availableSize: CGSize(width: constrainedTitleWidth, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(title
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + title.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += title.size.height
|
||||
contentSize.height += 24.0
|
||||
|
||||
var items: [AnyComponentWithIdentity<Empty>] = []
|
||||
var footer: AnyComponent<Empty>?
|
||||
|
||||
switch component.content {
|
||||
case let .options(options):
|
||||
for item in options {
|
||||
items.append(AnyComponentWithIdentity(id: item.title, component: AnyComponent(ListActionItemComponent(
|
||||
theme: theme,
|
||||
title: AnyComponent(VStack([
|
||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: item.title,
|
||||
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||
textColor: theme.list.itemPrimaryTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 1
|
||||
))),
|
||||
], alignment: .left, spacing: 2.0)),
|
||||
accessory: .arrow,
|
||||
action: { _ in
|
||||
component.action(item, nil)
|
||||
}
|
||||
))))
|
||||
}
|
||||
case let .comment(isOptional, _):
|
||||
contentSize.height -= 11.0
|
||||
|
||||
let animationHeight: CGFloat = 120.0
|
||||
let animation = animation.update(
|
||||
component: LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(name: "Cop"),
|
||||
startingPosition: .begin,
|
||||
playOnce: state.playOnce
|
||||
),
|
||||
environment: {},
|
||||
availableSize: CGSize(width: animationHeight, height: animationHeight),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(animation
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + animation.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += animation.size.height
|
||||
contentSize.height += 18.0
|
||||
|
||||
items.append(
|
||||
AnyComponentWithIdentity(id: items.count, component: AnyComponent(ListMultilineTextFieldItemComponent(
|
||||
externalState: state.textInputState,
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
initialText: "",
|
||||
resetText: nil,
|
||||
placeholder: isOptional ? "Add Comment (Optional)" : "Add Comment",
|
||||
autocapitalizationType: .none,
|
||||
autocorrectionType: .no,
|
||||
returnKeyType: .done,
|
||||
characterLimit: 140,
|
||||
displayCharacterLimit: true,
|
||||
emptyLineHandling: .notAllowed,
|
||||
updated: { [weak state] _ in
|
||||
state?.updated()
|
||||
},
|
||||
returnKeyAction: {
|
||||
// guard let self else {
|
||||
// return
|
||||
// }
|
||||
// if let titleView = self.introSection.findTaggedView(tag: self.textInputTag) as? ListMultilineTextFieldItemComponent.View {
|
||||
// titleView.endEditing(true)
|
||||
// }
|
||||
},
|
||||
textUpdateTransition: .spring(duration: 0.4),
|
||||
tag: nil
|
||||
)))
|
||||
)
|
||||
|
||||
footer = AnyComponent(MultilineTextComponent(
|
||||
text: .plain(
|
||||
NSAttributedString(string: "Please help us by telling what is wrong with the message you have selected.", font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: theme.list.freeTextColor)
|
||||
),
|
||||
maximumNumberOfLines: 0
|
||||
))
|
||||
}
|
||||
|
||||
let section = section.update(
|
||||
component: ListSectionComponent(
|
||||
theme: theme,
|
||||
header: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: component.subtitle.uppercased(),
|
||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||
textColor: theme.list.freeTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 0
|
||||
)),
|
||||
footer: footer,
|
||||
items: items
|
||||
),
|
||||
environment: {},
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(section
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + section.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += section.size.height
|
||||
contentSize.height += 54.0
|
||||
|
||||
if case let .comment(isOptional, option) = component.content {
|
||||
contentSize.height -= 16.0
|
||||
|
||||
let action = component.action
|
||||
let button = button.update(
|
||||
component: ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
color: theme.list.itemCheckColors.fillColor,
|
||||
foreground: theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8)
|
||||
),
|
||||
content: AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent(Text(text: "Send Report", font: Font.semibold(17.0), color: theme.list.itemCheckColors.foregroundColor))),
|
||||
isEnabled: isOptional || state.textInputState.hasText,
|
||||
allowActionWhenDisabled: false,
|
||||
displaysProgress: false,
|
||||
action: {
|
||||
action(SheetPageContent.Content.Item(title: "", option: option), state.textInputState.text.string)
|
||||
}
|
||||
),
|
||||
environment: {},
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(button
|
||||
.clipsToBounds(true)
|
||||
.cornerRadius(10.0)
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + button.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += button.size.height
|
||||
contentSize.height += 16.0
|
||||
|
||||
if environment.inputHeight.isZero && environment.safeInsets.bottom > 0.0 {
|
||||
contentSize.height += environment.safeInsets.bottom
|
||||
}
|
||||
}
|
||||
|
||||
contentSize.height += environment.inputHeight
|
||||
|
||||
state.playAnimationIfNeeded()
|
||||
|
||||
return contentSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class SheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let subject: ReportContentSubject
|
||||
let title: String
|
||||
let options: [ReportContentResult.Option]
|
||||
let pts: Int
|
||||
let openMore: () -> Void
|
||||
let complete: (ReportResult) -> Void
|
||||
let dismiss: () -> Void
|
||||
let update: (ComponentTransition) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
subject: ReportContentSubject,
|
||||
title: String,
|
||||
options: [ReportContentResult.Option],
|
||||
pts: Int,
|
||||
openMore: @escaping () -> Void,
|
||||
complete: @escaping (ReportResult) -> Void,
|
||||
dismiss: @escaping () -> Void,
|
||||
update: @escaping (ComponentTransition) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.subject = subject
|
||||
self.title = title
|
||||
self.options = options
|
||||
self.pts = pts
|
||||
self.openMore = openMore
|
||||
self.complete = complete
|
||||
self.dismiss = dismiss
|
||||
self.update = update
|
||||
}
|
||||
|
||||
static func ==(lhs: SheetContent, rhs: SheetContent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.subject != rhs.subject {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.options != rhs.options {
|
||||
return false
|
||||
}
|
||||
if lhs.pts != rhs.pts {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
var pushedOptions: [(title: String, subtitle: String, content: SheetPageContent.Content)] = []
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
deinit {
|
||||
self.disposable.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State()
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let navigation = Child(NavigationStackComponent<EnvironmentType>.self)
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
let component = context.component
|
||||
let state = context.state
|
||||
let update = component.update
|
||||
|
||||
let accountContext = component.context
|
||||
let subject = component.subject
|
||||
let complete = component.complete
|
||||
let action: (SheetPageContent.Content.Item, String?) -> Void = { [weak state] item, message in
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
state.disposable.set(
|
||||
(accountContext.engine.messages.reportContent(subject: subject, option: item.option, message: message)
|
||||
|> deliverOnMainQueue).start(next: { [weak state] result in
|
||||
switch result {
|
||||
case let .options(title, options):
|
||||
state?.pushedOptions.append((item.title, title, .options(items: options.map { SheetPageContent.Content.Item(title: $0.text, option: $0.option) })))
|
||||
state?.updated(transition: .spring(duration: 0.45))
|
||||
case let .addComment(isOptional, option):
|
||||
state?.pushedOptions.append((item.title, "", .comment(isOptional: isOptional, option: option)))
|
||||
state?.updated(transition: .spring(duration: 0.45))
|
||||
case .reported:
|
||||
complete(.reported)
|
||||
}
|
||||
}, error: { error in
|
||||
// if case .premiumRequired = error {
|
||||
// complete(.premiumRequired)
|
||||
// }
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
let mainTitle: String
|
||||
switch component.subject {
|
||||
case .peer:
|
||||
mainTitle = "Report Peer"
|
||||
case .messages:
|
||||
mainTitle = "Report Message"
|
||||
case .stories:
|
||||
mainTitle = "Report Story"
|
||||
}
|
||||
|
||||
var items: [AnyComponentWithIdentity<EnvironmentType>] = []
|
||||
items.append(AnyComponentWithIdentity(id: items.count, component: AnyComponent(
|
||||
SheetPageContent(
|
||||
context: component.context,
|
||||
isFirst: true,
|
||||
title: mainTitle,
|
||||
subtitle: component.title,
|
||||
content: .options(items: component.options.map {
|
||||
SheetPageContent.Content.Item(title: $0.text, option: $0.option)
|
||||
}),
|
||||
action: { item, message in
|
||||
action(item, message)
|
||||
},
|
||||
pop: {
|
||||
component.dismiss()
|
||||
}
|
||||
)
|
||||
)))
|
||||
for pushedOption in state.pushedOptions {
|
||||
items.append(AnyComponentWithIdentity(id: items.count, component: AnyComponent(
|
||||
SheetPageContent(
|
||||
context: component.context,
|
||||
isFirst: false,
|
||||
title: pushedOption.title,
|
||||
subtitle: pushedOption.subtitle,
|
||||
content: pushedOption.content,
|
||||
action: { item, message in
|
||||
action(item, message)
|
||||
},
|
||||
pop: { [weak state] in
|
||||
state?.pushedOptions.removeLast()
|
||||
update(.spring(duration: 0.45))
|
||||
}
|
||||
)
|
||||
)))
|
||||
}
|
||||
|
||||
var contentSize = CGSize(width: context.availableSize.width, height: 0.0)
|
||||
let navigation = navigation.update(
|
||||
component: NavigationStackComponent(
|
||||
items: items,
|
||||
clipContent: false,
|
||||
requestPop: { [weak state] in
|
||||
state?.pushedOptions.removeLast()
|
||||
update(.spring(duration: 0.45))
|
||||
}
|
||||
),
|
||||
environment: { environment },
|
||||
availableSize: CGSize(width: context.availableSize.width, height: context.availableSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(navigation
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: navigation.size.height / 2.0))
|
||||
.clipsToBounds(true)
|
||||
.cornerRadius(8.0)
|
||||
)
|
||||
contentSize.height += navigation.size.height
|
||||
|
||||
return contentSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class SheetContainerComponent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let subject: ReportContentSubject
|
||||
let title: String
|
||||
let options: [ReportContentResult.Option]
|
||||
let openMore: () -> Void
|
||||
let complete: (ReportResult) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
subject: ReportContentSubject,
|
||||
title: String,
|
||||
options: [ReportContentResult.Option],
|
||||
openMore: @escaping () -> Void,
|
||||
complete: @escaping (ReportResult) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.subject = subject
|
||||
self.title = title
|
||||
self.options = options
|
||||
self.openMore = openMore
|
||||
self.complete = complete
|
||||
}
|
||||
|
||||
static func ==(lhs: SheetContainerComponent, rhs: SheetContainerComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.subject != rhs.subject {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.options != rhs.options {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
var pts: Int = 0
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State()
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let sheet = Child(SheetComponent<EnvironmentType>.self)
|
||||
let animateOut = StoredActionSlot(Action<Void>.self)
|
||||
|
||||
let sheetExternalState = SheetComponent<EnvironmentType>.ExternalState()
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
let state = context.state
|
||||
let controller = environment.controller
|
||||
|
||||
let sheet = sheet.update(
|
||||
component: SheetComponent<EnvironmentType>(
|
||||
content: AnyComponent<EnvironmentType>(SheetContent(
|
||||
context: context.component.context,
|
||||
subject: context.component.subject,
|
||||
title: context.component.title,
|
||||
options: context.component.options,
|
||||
pts: state.pts,
|
||||
openMore: context.component.openMore,
|
||||
complete: context.component.complete,
|
||||
dismiss: {
|
||||
animateOut.invoke(Action { _ in
|
||||
if let controller = controller() {
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
})
|
||||
},
|
||||
update: { [weak state] transition in
|
||||
state?.pts += 1
|
||||
state?.updated(transition: transition)
|
||||
}
|
||||
)),
|
||||
backgroundColor: .color(environment.theme.list.modalBlocksBackgroundColor),
|
||||
followContentSizeChanges: true,
|
||||
externalState: sheetExternalState,
|
||||
animateOut: animateOut
|
||||
),
|
||||
environment: {
|
||||
environment
|
||||
SheetComponentEnvironment(
|
||||
isDisplaying: environment.value.isVisible,
|
||||
isCentered: environment.metrics.widthClass == .regular,
|
||||
hasInputHeight: !environment.inputHeight.isZero,
|
||||
regularMetricsSize: CGSize(width: 430.0, height: 900.0),
|
||||
dismiss: { animated in
|
||||
if animated {
|
||||
animateOut.invoke(Action { _ in
|
||||
if let controller = controller() {
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if let controller = controller() {
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
context.add(sheet
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||
)
|
||||
|
||||
if let controller = controller(), !controller.automaticallyControlPresentationContextLayout {
|
||||
let layout = ContainerViewLayout(
|
||||
size: context.availableSize,
|
||||
metrics: environment.metrics,
|
||||
deviceMetrics: environment.deviceMetrics,
|
||||
intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: max(environment.safeInsets.bottom, sheetExternalState.contentHeight), right: 0.0),
|
||||
safeInsets: UIEdgeInsets(top: 0.0, left: environment.safeInsets.left, bottom: 0.0, right: environment.safeInsets.right),
|
||||
additionalInsets: .zero,
|
||||
statusBarHeight: environment.statusBarHeight,
|
||||
inputHeight: nil,
|
||||
inputHeightIsInteractivellyChanging: false,
|
||||
inVoiceOver: false
|
||||
)
|
||||
controller.presentationContext.containerLayoutUpdated(layout, transition: context.transition.containedViewLayoutTransition)
|
||||
}
|
||||
|
||||
return context.availableSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final class ContentReportScreen: ViewControllerComponentContainer {
|
||||
private let context: AccountContext
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
subject: ReportContentSubject,
|
||||
title: String,
|
||||
options: [ReportContentResult.Option],
|
||||
forceDark: Bool = false,
|
||||
completed: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
|
||||
var completeImpl: ((ReportResult) -> Void)?
|
||||
super.init(
|
||||
context: context,
|
||||
component: SheetContainerComponent(
|
||||
context: context,
|
||||
subject: subject,
|
||||
title: title,
|
||||
options: options,
|
||||
openMore: {},
|
||||
complete: { hidden in
|
||||
completeImpl?(hidden)
|
||||
}
|
||||
),
|
||||
navigationBarAppearance: .none,
|
||||
statusBarStyle: .ignore,
|
||||
theme: forceDark ? .dark : .default
|
||||
)
|
||||
|
||||
self.navigationPresentation = .flatModal
|
||||
|
||||
completeImpl = { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let navigationController = self.navigationController
|
||||
self.dismissAnimated()
|
||||
|
||||
switch result {
|
||||
case .reported:
|
||||
Queue.mainQueue().after(0.1) {
|
||||
completed()
|
||||
}
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
Queue.mainQueue().after(0.4, {
|
||||
(navigationController?.viewControllers.last as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .emoji(name: "PoliceCar", text: presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return true }), in: .current)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.view.disablesInteractiveModalDismiss = true
|
||||
}
|
||||
|
||||
public func dismissAnimated() {
|
||||
if let view = self.node.hostView.findTaggedView(tag: SheetComponent<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
|
||||
view.dismissAnimated()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "GiftAnimationComponent",
|
||||
module_name = "GiftAnimationComponent",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/Postbox",
|
||||
"//submodules/TelegramCore",
|
||||
"//submodules/SSignalKit/SwiftSignalKit",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/Components/ComponentDisplayAdapters",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/AppBundle",
|
||||
"//submodules/TelegramStringFormatting",
|
||||
"//submodules/PresentationDataUtils",
|
||||
"//submodules/TextFormat",
|
||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -0,0 +1,98 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import AppBundle
|
||||
import AccountContext
|
||||
import EmojiTextAttachmentView
|
||||
import TextFormat
|
||||
|
||||
public final class GiftAnimationComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let file: TelegramMediaFile?
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
file: TelegramMediaFile?
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.file = file
|
||||
}
|
||||
|
||||
public static func ==(lhs: GiftAnimationComponent, rhs: GiftAnimationComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.file != rhs.file {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private var component: GiftAnimationComponent?
|
||||
private weak var componentState: EmptyComponentState?
|
||||
|
||||
private var animationLayer: InlineStickerItemLayer?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: GiftAnimationComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.componentState = state
|
||||
|
||||
let emoji = ChatTextInputTextCustomEmojiAttribute(
|
||||
interactivelySelectedFromPackId: nil,
|
||||
fileId: component.file?.fileId.id ?? 0,
|
||||
file: component.file
|
||||
)
|
||||
|
||||
let iconSize = availableSize
|
||||
if self.animationLayer == nil {
|
||||
let animationLayer = InlineStickerItemLayer(
|
||||
context: .account(component.context),
|
||||
userLocation: .other,
|
||||
attemptSynchronousLoad: false,
|
||||
emoji: emoji,
|
||||
file: component.file,
|
||||
cache: component.context.animationCache,
|
||||
renderer: component.context.animationRenderer,
|
||||
unique: true,
|
||||
placeholderColor: component.theme.list.mediaPlaceholderColor,
|
||||
pointSize: CGSize(width: iconSize.width * 1.2, height: iconSize.height * 1.2),
|
||||
loopCount: 1
|
||||
)
|
||||
animationLayer.isVisibleForAnimations = true
|
||||
self.animationLayer = animationLayer
|
||||
self.layer.addSublayer(animationLayer)
|
||||
}
|
||||
if let animationLayer = self.animationLayer {
|
||||
transition.setFrame(layer: animationLayer, frame: CGRect(origin: .zero, size: iconSize))
|
||||
}
|
||||
|
||||
return iconSize
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
@ -37,6 +37,7 @@ swift_library(
|
||||
"//submodules/Components/SolidRoundedButtonComponent",
|
||||
"//submodules/TelegramUI/Components/Stars/StarsAvatarComponent",
|
||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView",
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftAnimationComponent",
|
||||
"//submodules/UndoUI",
|
||||
],
|
||||
visibility = [
|
||||
|
@ -23,6 +23,7 @@ import TelegramStringFormatting
|
||||
import StarsAvatarComponent
|
||||
import EmojiTextAttachmentView
|
||||
import UndoUI
|
||||
import GiftAnimationComponent
|
||||
|
||||
private final class GiftViewSheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
@ -695,14 +696,14 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
case message(EngineMessage)
|
||||
case profileGift(EnginePeer.Id, ProfileGiftsContext.State.StarGift)
|
||||
|
||||
var arguments: (peerId: EnginePeer.Id, messageId: EngineMessage.Id?, incoming: Bool, gift: StarGift, convertStars: Int64, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool)? {
|
||||
var arguments: (peerId: EnginePeer.Id, fromPeerName: String?, messageId: EngineMessage.Id?, incoming: Bool, gift: StarGift, convertStars: Int64, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool)? {
|
||||
switch self {
|
||||
case let .message(message):
|
||||
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted) = action.action {
|
||||
return (message.id.peerId, message.id, message.flags.contains(.Incoming), gift, convertStars, text, entities, nameHidden, savedToProfile, converted)
|
||||
return (message.id.peerId, message.author?.compactDisplayTitle, message.id, message.flags.contains(.Incoming), gift, convertStars, text, entities, nameHidden, savedToProfile, converted)
|
||||
}
|
||||
case let .profileGift(peerId, gift):
|
||||
return (peerId, gift.messageId, false, gift.gift, gift.convertStars ?? 0, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, false)
|
||||
return (peerId, gift.fromPeer?.compactDisplayTitle, gift.messageId, false, gift.gift, gift.convertStars ?? 0, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -789,9 +790,14 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
if let lastController = navigationController.viewControllers.last as? ViewController {
|
||||
let resultController = UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .sticker(context: context, file: arguments.gift.file, loop: false, title: added ? "Gift Saved to Profile" : "Gift Removed from Profile", text: added ? "The gift is now displayed in your profile." : "The gift is no longer displayed in your profile.", undoText: nil, customAction: nil),
|
||||
content: .sticker(context: context, file: arguments.gift.file, loop: false, title: added ? "Gift Saved to Profile" : "Gift Removed from Profile", text: added ? "The gift is now displayed in [your profile]()." : "The gift is no longer displayed in [your profile]().", undoText: nil, customAction: nil),
|
||||
elevatedLayout: lastController is ChatController,
|
||||
action: { _ in return true}
|
||||
action: { action in
|
||||
if case .info = action {
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
)
|
||||
lastController.present(resultController, in: .window(.root))
|
||||
}
|
||||
@ -800,13 +806,13 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
}
|
||||
|
||||
convertToStarsImpl = { [weak self] in
|
||||
guard let self, case let .message(message) = subject, let arguments = subject.arguments, let messageId = arguments.messageId, let navigationController = self.navigationController as? NavigationController else {
|
||||
guard let self, let arguments = subject.arguments, let messageId = arguments.messageId, let fromPeerName = arguments.fromPeerName, let navigationController = self.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
let controller = textAlertController(
|
||||
context: self.context,
|
||||
title: "Convert Gift to Stars",
|
||||
text: "Do you want to convert this gift from **\(message.author?.compactDisplayTitle ?? "")** to **\(arguments.convertStars) Stars**?\n\nThis action cannot be undone.",
|
||||
text: "Do you want to convert this gift from **\(fromPeerName)** to **\(arguments.convertStars) Stars**?\n\nThis action cannot be undone.",
|
||||
actions: [
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}),
|
||||
TextAlertAction(type: .defaultAction, title: "Convert", action: { [weak self, weak navigationController] in
|
||||
@ -1253,91 +1259,3 @@ private func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor:
|
||||
context.strokePath()
|
||||
})
|
||||
}
|
||||
|
||||
private final class GiftAnimationComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let file: TelegramMediaFile?
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
file: TelegramMediaFile?
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.file = file
|
||||
}
|
||||
|
||||
public static func ==(lhs: GiftAnimationComponent, rhs: GiftAnimationComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.file != rhs.file {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private var component: GiftAnimationComponent?
|
||||
private weak var componentState: EmptyComponentState?
|
||||
|
||||
private var animationLayer: InlineStickerItemLayer?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: GiftAnimationComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.componentState = state
|
||||
|
||||
let emoji = ChatTextInputTextCustomEmojiAttribute(
|
||||
interactivelySelectedFromPackId: nil,
|
||||
fileId: component.file?.fileId.id ?? 0,
|
||||
file: component.file
|
||||
)
|
||||
|
||||
let iconSize = availableSize
|
||||
if self.animationLayer == nil {
|
||||
let animationLayer = InlineStickerItemLayer(
|
||||
context: .account(component.context),
|
||||
userLocation: .other,
|
||||
attemptSynchronousLoad: false,
|
||||
emoji: emoji,
|
||||
file: component.file,
|
||||
cache: component.context.animationCache,
|
||||
renderer: component.context.animationRenderer,
|
||||
unique: true,
|
||||
placeholderColor: component.theme.list.mediaPlaceholderColor,
|
||||
pointSize: CGSize(width: iconSize.width * 1.2, height: iconSize.height * 1.2),
|
||||
loopCount: 1
|
||||
)
|
||||
animationLayer.isVisibleForAnimations = true
|
||||
self.animationLayer = animationLayer
|
||||
self.layer.addSublayer(animationLayer)
|
||||
}
|
||||
if let animationLayer = self.animationLayer {
|
||||
transition.setFrame(layer: animationLayer, frame: CGRect(origin: .zero, size: iconSize))
|
||||
}
|
||||
|
||||
return iconSize
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
@ -85,13 +85,16 @@ public final class NavigationStackComponent<ChildEnvironment: Equatable>: Compon
|
||||
}
|
||||
|
||||
public let items: [AnyComponentWithIdentity<ChildEnvironment>]
|
||||
public let clipContent: Bool
|
||||
public let requestPop: () -> Void
|
||||
|
||||
public init(
|
||||
items: [AnyComponentWithIdentity<ChildEnvironment>],
|
||||
clipContent: Bool = true,
|
||||
requestPop: @escaping () -> Void
|
||||
) {
|
||||
self.items = items
|
||||
self.clipContent = clipContent
|
||||
self.requestPop = requestPop
|
||||
}
|
||||
|
||||
@ -99,6 +102,9 @@ public final class NavigationStackComponent<ChildEnvironment: Equatable>: Compon
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
if lhs.clipContent != rhs.clipContent {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -198,7 +204,7 @@ public final class NavigationStackComponent<ChildEnvironment: Equatable>: Compon
|
||||
} else {
|
||||
itemTransition = itemTransition.withAnimation(.none)
|
||||
itemView = ItemView()
|
||||
itemView.clipsToBounds = true
|
||||
itemView.clipsToBounds = component.clipContent
|
||||
self.itemViews[itemId] = itemView
|
||||
itemView.contents.parentState = state
|
||||
}
|
||||
|
@ -999,7 +999,8 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
|
||||
items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 103, label: .text(""), additionalBadgeLabel: presentationData.strings.Settings_New, text: presentationData.strings.Settings_Business, icon: PresentationResourcesSettings.business, action: {
|
||||
interaction.openSettings(.businessSetup)
|
||||
}))
|
||||
items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 104, label: .text(""), text: presentationData.strings.Settings_PremiumGift, icon: PresentationResourcesSettings.premiumGift, action: {
|
||||
//TODO:localize
|
||||
items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 104, label: .text(""), text: "Send a Gift", icon: PresentationResourcesSettings.premiumGift, action: {
|
||||
interaction.openSettings(.premiumGift)
|
||||
}))
|
||||
}
|
||||
@ -6098,8 +6099,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
})))
|
||||
}
|
||||
|
||||
if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser, !user.isDeleted && user.botInfo == nil && !user.flags.contains(.isSupport), let cachedData = data.cachedData as? CachedUserData, !cachedData.premiumGiftOptions.isEmpty {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeerInfo_GiftPremium, icon: { theme in
|
||||
if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser, !user.isDeleted && user.botInfo == nil && !user.flags.contains(.isSupport) {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Send a Gift", icon: { theme in
|
||||
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Gift"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.dismissWithoutContent)
|
||||
@ -11642,11 +11644,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
return
|
||||
}
|
||||
strongSelf.view.endEditing(true)
|
||||
|
||||
strongSelf.controller?.present(peerReportOptionsController(context: strongSelf.context, subject: .messages(Array(messageIds).sorted()), passthrough: false, present: { c, a in
|
||||
self?.controller?.present(c, in: .window(.root), with: a)
|
||||
}, push: { c in
|
||||
self?.controller?.push(c)
|
||||
}, completion: { _, _ in }), in: .window(.root))
|
||||
|
||||
|
||||
}, displayCopyProtectionTip: { [weak self] node, save in
|
||||
if let strongSelf = self, let peer = strongSelf.data?.peer, let messageIds = strongSelf.state.selectedMessageIds, !messageIds.isEmpty {
|
||||
let _ = (strongSelf.context.engine.data.get(EngineDataMap(
|
||||
|
@ -36,6 +36,7 @@ swift_library(
|
||||
"//submodules/GalleryUI",
|
||||
"//submodules/TelegramUI/Components/MiniAppListScreen",
|
||||
"//submodules/TelegramUI/Components/Premium/PremiumStarComponent",
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftAnimationComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -25,6 +25,7 @@ import GalleryUI
|
||||
import StarsAvatarComponent
|
||||
import MiniAppListScreen
|
||||
import PremiumStarComponent
|
||||
import GiftAnimationComponent
|
||||
|
||||
private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
@ -145,6 +146,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let star = Child(StarsImageComponent.self)
|
||||
let activeStar = Child(PremiumStarComponent.self)
|
||||
let gift = Child(GiftAnimationComponent.self)
|
||||
let amountBackground = Child(RoundedRectangle.self)
|
||||
let amount = Child(BalancedTextComponent.self)
|
||||
let amountStar = Child(BundleIconComponent.self)
|
||||
@ -225,6 +227,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
var isReaction = false
|
||||
var giveawayMessageId: MessageId?
|
||||
var isBoost = false
|
||||
var giftAnimation: TelegramMediaFile?
|
||||
|
||||
var delayedCloseOnOpenPeer = true
|
||||
switch subject {
|
||||
@ -322,7 +325,18 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
}
|
||||
}
|
||||
case let .transaction(transaction, parentPeer):
|
||||
if let giveawayMessageIdValue = transaction.giveawayMessageId {
|
||||
if let starGift = transaction.starGift {
|
||||
titleText = "Gift"
|
||||
descriptionText = ""
|
||||
count = transaction.count
|
||||
transactionId = transaction.id
|
||||
date = transaction.date
|
||||
if case let .peer(peer) = transaction.peer {
|
||||
toPeer = peer
|
||||
}
|
||||
transactionPeer = transaction.peer
|
||||
giftAnimation = starGift.file
|
||||
} else if let giveawayMessageIdValue = transaction.giveawayMessageId {
|
||||
titleText = strings.Stars_Transaction_Giveaway_Title
|
||||
descriptionText = ""
|
||||
count = transaction.count
|
||||
@ -572,7 +586,17 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
imageIcon = nil
|
||||
}
|
||||
var starChild: _UpdatedChildComponent
|
||||
if isBoost {
|
||||
if let giftAnimation {
|
||||
starChild = gift.update(
|
||||
component: GiftAnimationComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
file: giftAnimation
|
||||
),
|
||||
availableSize: CGSize(width: 128.0, height: 128.0),
|
||||
transition: .immediate
|
||||
)
|
||||
} else if isBoost {
|
||||
starChild = activeStar.update(
|
||||
component: PremiumStarComponent(
|
||||
theme: theme,
|
||||
@ -877,7 +901,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
)
|
||||
|
||||
context.add(starChild
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: starChild.size.height / 2.0 - 19.0))
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 200.0 / 2.0 - 19.0))
|
||||
)
|
||||
|
||||
context.add(title
|
||||
@ -885,7 +909,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
)
|
||||
|
||||
var originY: CGFloat = 0.0
|
||||
originY += starChild.size.height - 23.0
|
||||
originY += 200.0 - 23.0
|
||||
|
||||
var descriptionSize: CGSize = .zero
|
||||
if !descriptionText.isEmpty {
|
||||
|
@ -209,7 +209,10 @@ final class StarsTransactionsListPanelComponent: Component {
|
||||
var itemPeer = item.peer
|
||||
switch item.peer {
|
||||
case let .peer(peer):
|
||||
if let _ = item.giveawayMessageId {
|
||||
if let _ = item.starGift {
|
||||
itemTitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)
|
||||
itemSubtitle = item.count > 0 ? environment.strings.Stars_Intro_Transaction_ConvertedGift : environment.strings.Stars_Intro_Transaction_Gift
|
||||
} else if let _ = item.giveawayMessageId {
|
||||
itemTitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)
|
||||
itemSubtitle = environment.strings.Stars_Intro_Transaction_GiveawayPrize
|
||||
} else if !item.media.isEmpty {
|
||||
|
@ -6961,48 +6961,69 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
if !component.slice.effectivePeer.isService {
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_Report, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] c, a in
|
||||
}, action: { [weak self] _, f in
|
||||
guard let self, let component = self.component, let controller = component.controller() else {
|
||||
return
|
||||
}
|
||||
|
||||
let options: [PeerReportOption] = [.spam, .violence, .pornography, .childAbuse, .copyright, .illegalDrugs, .personalDetails, .other]
|
||||
presentPeerReportOptions(
|
||||
f(.default)
|
||||
|
||||
self.isReporting = true
|
||||
self.updateIsProgressPaused()
|
||||
|
||||
component.context.sharedContext.makeContentReportScreen(
|
||||
context: component.context,
|
||||
parent: controller,
|
||||
contextController: c,
|
||||
backAction: { _ in },
|
||||
subject: .story(component.slice.effectivePeer.id, component.slice.item.storyItem.id),
|
||||
options: options,
|
||||
passthrough: true,
|
||||
forceTheme: defaultDarkPresentationTheme,
|
||||
isDetailedReportingVisible: { [weak self] isReporting in
|
||||
subject: .stories(component.slice.effectivePeer.id, [component.slice.item.storyItem.id]),
|
||||
forceDark: true,
|
||||
present: { c in
|
||||
controller.push(c)
|
||||
},
|
||||
completion: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.isReporting = isReporting
|
||||
self.isReporting = false
|
||||
self.updateIsProgressPaused()
|
||||
},
|
||||
completion: { [weak self] reason, _ in
|
||||
guard let self, let component = self.component, let controller = component.controller(), let reason else {
|
||||
return
|
||||
}
|
||||
let _ = component.context.engine.peers.reportPeerStory(peerId: component.slice.effectivePeer.id, storyId: component.slice.item.storyItem.id, reason: reason, message: "").startStandalone()
|
||||
controller.present(
|
||||
UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .emoji(
|
||||
name: "PoliceCar",
|
||||
text: presentationData.strings.Report_Succeed
|
||||
),
|
||||
elevatedLayout: false,
|
||||
blurred: true,
|
||||
action: { _ in return false }
|
||||
)
|
||||
, in: .current
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
// let options: [PeerReportOption] = [.spam, .violence, .pornography, .childAbuse, .copyright, .illegalDrugs, .personalDetails, .other]
|
||||
// presentPeerReportOptions(
|
||||
// context: component.context,
|
||||
// parent: controller,
|
||||
// contextController: c,
|
||||
// backAction: { _ in },
|
||||
// subject: .story(component.slice.effectivePeer.id, component.slice.item.storyItem.id),
|
||||
// options: options,
|
||||
// passthrough: true,
|
||||
// forceTheme: defaultDarkPresentationTheme,
|
||||
// isDetailedReportingVisible: { [weak self] isReporting in
|
||||
// guard let self else {
|
||||
// return
|
||||
// }
|
||||
// self.isReporting = isReporting
|
||||
// self.updateIsProgressPaused()
|
||||
// },
|
||||
// completion: { [weak self] reason, _ in
|
||||
// guard let self, let component = self.component, let controller = component.controller(), let reason else {
|
||||
// return
|
||||
// }
|
||||
// let _ = component.context.engine.peers.reportPeerStory(peerId: component.slice.effectivePeer.id, storyId: component.slice.item.storyItem.id, reason: reason, message: "").startStandalone()
|
||||
// controller.present(
|
||||
// UndoOverlayController(
|
||||
// presentationData: presentationData,
|
||||
// content: .emoji(
|
||||
// name: "PoliceCar",
|
||||
// text: presentationData.strings.Report_Succeed
|
||||
// ),
|
||||
// elevatedLayout: false,
|
||||
// blurred: true,
|
||||
// action: { _ in return false }
|
||||
// )
|
||||
// , in: .current
|
||||
// )
|
||||
// }
|
||||
// )
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
@ -1957,22 +1957,30 @@ extension ChatControllerImpl {
|
||||
])
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
} else {
|
||||
strongSelf.present(peerReportOptionsController(context: strongSelf.context, subject: .messages(Array(messageIds).sorted()), passthrough: false, present: { c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
}, push: { c in
|
||||
self?.push(c)
|
||||
}, completion: { _, done in
|
||||
if done {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } })
|
||||
}
|
||||
}), in: .window(.root))
|
||||
strongSelf.context.sharedContext.makeContentReportScreen(context: strongSelf.context, subject: .messages(Array(messageIds).sorted()), forceDark: false, present: { [weak self] controller in
|
||||
self?.push(controller)
|
||||
}, completion: { [weak self] in
|
||||
self?.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } })
|
||||
})
|
||||
}
|
||||
}
|
||||
}, reportMessages: { [weak self] messages, contextController in
|
||||
if let strongSelf = self, !messages.isEmpty {
|
||||
let options: [PeerReportOption] = [.spam, .violence, .pornography, .childAbuse, .copyright, .illegalDrugs, .personalDetails, .other]
|
||||
presentPeerReportOptions(context: strongSelf.context, parent: strongSelf, contextController: contextController, subject: .messages(messages.map({ $0.id }).sorted()), options: options, completion: { _, _ in })
|
||||
guard let self, !messages.isEmpty else {
|
||||
return
|
||||
}
|
||||
contextController?.dismiss()
|
||||
self.context.sharedContext.makeContentReportScreen(
|
||||
context: self.context,
|
||||
subject: .messages(messages.map({ $0.id }).sorted()),
|
||||
forceDark: false,
|
||||
present: { [weak self] controller in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.push(controller)
|
||||
},
|
||||
completion: {}
|
||||
)
|
||||
}, blockMessageAuthor: { [weak self] message, contextController in
|
||||
contextController?.dismiss(completion: {
|
||||
guard let strongSelf = self else {
|
||||
|
@ -73,6 +73,7 @@ import MiniAppListScreen
|
||||
import GiftOptionsScreen
|
||||
import GiftViewScreen
|
||||
import StarsIntroScreen
|
||||
import ContentReportScreen
|
||||
|
||||
private final class AccountUserInterfaceInUseContext {
|
||||
let subscribers = Bag<(Bool) -> Void>()
|
||||
@ -2819,6 +2820,15 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return GiftViewScreen(context: context, subject: .message(message))
|
||||
}
|
||||
|
||||
public func makeContentReportScreen(context: AccountContext, subject: ReportContentSubject, forceDark: Bool, present: @escaping (ViewController) -> Void, completion: @escaping () -> Void) {
|
||||
let _ = (context.engine.messages.reportContent(subject: subject, option: nil, message: nil)
|
||||
|> deliverOnMainQueue).startStandalone(next: { result in
|
||||
if case let .options(title, options) = result {
|
||||
present(ContentReportScreen(context: context, subject: subject, title: title, options: options, forceDark: forceDark, completed: completion))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func makeMiniAppListScreenInitialData(context: AccountContext) -> Signal<MiniAppListScreenInitialData, NoError> {
|
||||
return MiniAppListScreen.initialData(context: context)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user