mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
5978a5278d
commit
707d5137ca
@ -12876,3 +12876,7 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"BoostGift.Group.StarsDateInfo" = "Choose when %@ of your group will be randomly selected to receive Stars.";
|
"BoostGift.Group.StarsDateInfo" = "Choose when %@ of your group will be randomly selected to receive Stars.";
|
||||||
"BoostGift.StarsDateInfo" = "Choose when %@ of your channel will be randomly selected to receive Stars.";
|
"BoostGift.StarsDateInfo" = "Choose when %@ of your channel will be randomly selected to receive Stars.";
|
||||||
|
|
||||||
|
"BoostGift.PrepaidGiveawayStarsCount_1" = "%@ Telegram Premium";
|
||||||
|
"BoostGift.PrepaidGiveawayStarsCount_any" = "%@ Telegram Premium";
|
||||||
|
"BoostGift.PrepaidGiveawayStarsMonths" = "%@-month subscriptions";
|
||||||
|
@ -30,6 +30,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
|||||||
private let sourceLocation: InstantPageSourceLocation
|
private let sourceLocation: InstantPageSourceLocation
|
||||||
private let preloadedResouces: [Any]?
|
private let preloadedResouces: [Any]?
|
||||||
private var originalContent: BrowserContent?
|
private var originalContent: BrowserContent?
|
||||||
|
private let url: String
|
||||||
|
|
||||||
private var webPage: TelegramMediaWebpage?
|
private var webPage: TelegramMediaWebpage?
|
||||||
|
|
||||||
@ -102,6 +103,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
|||||||
self.sourceLocation = sourceLocation
|
self.sourceLocation = sourceLocation
|
||||||
self.preloadedResouces = preloadedResouces
|
self.preloadedResouces = preloadedResouces
|
||||||
self.originalContent = originalContent
|
self.originalContent = originalContent
|
||||||
|
self.url = url
|
||||||
|
|
||||||
self.uuid = UUID()
|
self.uuid = UUID()
|
||||||
|
|
||||||
@ -904,6 +906,12 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
|||||||
anchor = String(baseUrl[anchorRange.upperBound...]).removingPercentEncoding
|
anchor = String(baseUrl[anchorRange.upperBound...]).removingPercentEncoding
|
||||||
baseUrl = String(baseUrl[..<anchorRange.lowerBound])
|
baseUrl = String(baseUrl[..<anchorRange.lowerBound])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !baseUrl.hasPrefix("http://") && !baseUrl.hasPrefix("https://") {
|
||||||
|
if let updatedUrl = URL(string: baseUrl, relativeTo: URL(string: "/", relativeTo: URL(string: self.url))) {
|
||||||
|
baseUrl = updatedUrl.absoluteString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let webPage = self.webPage, case let .Loaded(content) = webPage.content, let page = content.instantPage, page.url == baseUrl || baseUrl.isEmpty, let anchor = anchor {
|
if let webPage = self.webPage, case let .Loaded(content) = webPage.content, let page = content.instantPage, page.url == baseUrl || baseUrl.isEmpty, let anchor = anchor {
|
||||||
self.scrollToAnchor(anchor)
|
self.scrollToAnchor(anchor)
|
||||||
@ -914,7 +922,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
|||||||
self.loadProgress.set(0.02)
|
self.loadProgress.set(0.02)
|
||||||
|
|
||||||
self.loadWebpageDisposable.set(nil)
|
self.loadWebpageDisposable.set(nil)
|
||||||
self.resolveUrlDisposable.set((self.context.sharedContext.resolveUrl(context: self.context, peerId: nil, url: url.url, skipUrlAuth: true)
|
self.resolveUrlDisposable.set((self.context.sharedContext.resolveUrl(context: self.context, peerId: nil, url: baseUrl, skipUrlAuth: true)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.loadProgress.set(0.07)
|
strongSelf.loadProgress.set(0.07)
|
||||||
@ -997,8 +1005,15 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func openUrlIn(_ url: InstantPageUrlItem) {
|
private func openUrlIn(_ url: InstantPageUrlItem) {
|
||||||
|
var baseUrl = url.url
|
||||||
|
if !baseUrl.hasPrefix("http://") && !baseUrl.hasPrefix("https://") {
|
||||||
|
if let updatedUrl = URL(string: baseUrl, relativeTo: URL(string: "/", relativeTo: URL(string: self.url))) {
|
||||||
|
baseUrl = updatedUrl.absoluteString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let actionSheet = OpenInActionSheetController(context: self.context, item: .url(url: url.url), openUrl: { [weak self] url in
|
let actionSheet = OpenInActionSheetController(context: self.context, item: .url(url: baseUrl), openUrl: { [weak self] url in
|
||||||
if let self {
|
if let self {
|
||||||
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||||
}
|
}
|
||||||
@ -1182,11 +1197,18 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
|||||||
}
|
}
|
||||||
case .longTap:
|
case .longTap:
|
||||||
if let url = self.urlForTapLocation(location) {
|
if let url = self.urlForTapLocation(location) {
|
||||||
let canOpenIn = availableOpenInOptions(context: self.context, item: .url(url: url.url)).count > 1
|
var baseUrl = url.url
|
||||||
|
if !baseUrl.hasPrefix("http://") && !baseUrl.hasPrefix("https://") {
|
||||||
|
if let updatedUrl = URL(string: baseUrl, relativeTo: URL(string: "/", relativeTo: URL(string: self.url))) {
|
||||||
|
baseUrl = updatedUrl.absoluteString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let canOpenIn = availableOpenInOptions(context: self.context, item: .url(url: baseUrl)).count > 1
|
||||||
let openText = canOpenIn ? self.presentationData.strings.Conversation_FileOpenIn : self.presentationData.strings.Conversation_LinkDialogOpen
|
let openText = canOpenIn ? self.presentationData.strings.Conversation_FileOpenIn : self.presentationData.strings.Conversation_LinkDialogOpen
|
||||||
let actionSheet = ActionSheetController(instantPageTheme: self.theme)
|
let actionSheet = ActionSheetController(instantPageTheme: self.theme)
|
||||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
||||||
ActionSheetTextItem(title: url.url),
|
ActionSheetTextItem(title: baseUrl),
|
||||||
ActionSheetButtonItem(title: openText, color: .accent, action: { [weak self, weak actionSheet] in
|
ActionSheetButtonItem(title: openText, color: .accent, action: { [weak self, weak actionSheet] in
|
||||||
actionSheet?.dismissAnimated()
|
actionSheet?.dismissAnimated()
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
@ -1199,11 +1221,11 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
|||||||
}),
|
}),
|
||||||
ActionSheetButtonItem(title: self.presentationData.strings.ShareMenu_CopyShareLink, color: .accent, action: { [weak actionSheet] in
|
ActionSheetButtonItem(title: self.presentationData.strings.ShareMenu_CopyShareLink, color: .accent, action: { [weak actionSheet] in
|
||||||
actionSheet?.dismissAnimated()
|
actionSheet?.dismissAnimated()
|
||||||
UIPasteboard.general.string = url.url
|
UIPasteboard.general.string = baseUrl
|
||||||
}),
|
}),
|
||||||
ActionSheetButtonItem(title: self.presentationData.strings.Conversation_AddToReadingList, color: .accent, action: { [weak actionSheet] in
|
ActionSheetButtonItem(title: self.presentationData.strings.Conversation_AddToReadingList, color: .accent, action: { [weak actionSheet] in
|
||||||
actionSheet?.dismissAnimated()
|
actionSheet?.dismissAnimated()
|
||||||
if let link = URL(string: url.url) {
|
if let link = URL(string: baseUrl) {
|
||||||
let _ = try? SSReadingList.default()?.addItem(with: link, title: nil, previewText: nil)
|
let _ = try? SSReadingList.default()?.addItem(with: link, title: nil, previewText: nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -211,6 +211,7 @@ private func parseRichText(_ input: [Any], _ media: inout [MediaId: Media]) -> R
|
|||||||
result.append(parseRichText(string))
|
result.append(parseRichText(string))
|
||||||
} else if let item = item as? [String: Any], let tag = item["tag"] as? String {
|
} else if let item = item as? [String: Any], let tag = item["tag"] as? String {
|
||||||
var text: RichText?
|
var text: RichText?
|
||||||
|
var addLineBreak = false
|
||||||
switch tag {
|
switch tag {
|
||||||
case "b", "strong":
|
case "b", "strong":
|
||||||
text = .bold(parseRichText(item, &media))
|
text = .bold(parseRichText(item, &media))
|
||||||
@ -273,6 +274,9 @@ private func parseRichText(_ input: [Any], _ media: inout [MediaId: Media]) -> R
|
|||||||
flags: []
|
flags: []
|
||||||
)
|
)
|
||||||
text = .image(id: id, dimensions: PixelDimensions(width: width, height: height))
|
text = .image(id: id, dimensions: PixelDimensions(width: width, height: height))
|
||||||
|
if width > 100 {
|
||||||
|
addLineBreak = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case "br":
|
case "br":
|
||||||
if let last = result.last {
|
if let last = result.last {
|
||||||
@ -284,6 +288,9 @@ private func parseRichText(_ input: [Any], _ media: inout [MediaId: Media]) -> R
|
|||||||
if var text {
|
if var text {
|
||||||
text = applyAnchor(text, item: item)
|
text = applyAnchor(text, item: item)
|
||||||
result.append(text)
|
result.append(text)
|
||||||
|
if addLineBreak {
|
||||||
|
result.append(.plain("\n"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -298,15 +305,130 @@ private func parseRichText(_ input: [Any], _ media: inout [MediaId: Media]) -> R
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func trimStart(_ input: RichText) -> RichText {
|
private func trimStart(_ input: RichText) -> RichText {
|
||||||
return input
|
var text = input
|
||||||
|
switch input {
|
||||||
|
case .empty:
|
||||||
|
text = .empty
|
||||||
|
case let .plain(string):
|
||||||
|
text = .plain(string.replacingOccurrences(of: "^[ \t\r\n]+", with: "", options: .regularExpression, range: nil))
|
||||||
|
case let .bold(richText):
|
||||||
|
text = .bold(trimStart(richText))
|
||||||
|
case let .italic(richText):
|
||||||
|
text = .italic(trimStart(richText))
|
||||||
|
case let .underline(richText):
|
||||||
|
text = .underline(trimStart(richText))
|
||||||
|
case let .strikethrough(richText):
|
||||||
|
text = .strikethrough(trimStart(richText))
|
||||||
|
case let .fixed(richText):
|
||||||
|
text = .fixed(trimStart(richText))
|
||||||
|
case let .url(richText, url, webpageId):
|
||||||
|
text = .url(text: trimStart(richText), url: url, webpageId: webpageId)
|
||||||
|
case let .email(richText, email):
|
||||||
|
text = .email(text: trimStart(richText), email: email)
|
||||||
|
case let .subscript(richText):
|
||||||
|
text = .subscript(trimStart(richText))
|
||||||
|
case let .superscript(richText):
|
||||||
|
text = .superscript(trimStart(richText))
|
||||||
|
case let .marked(richText):
|
||||||
|
text = .marked(trimStart(richText))
|
||||||
|
case let .phone(richText, phone):
|
||||||
|
text = .phone(text: trimStart(richText), phone: phone)
|
||||||
|
case let .anchor(richText, name):
|
||||||
|
text = .anchor(text: trimStart(richText), name: name)
|
||||||
|
case var .concat(array):
|
||||||
|
if !array.isEmpty {
|
||||||
|
array[0] = trimStart(array[0])
|
||||||
|
text = .concat(array)
|
||||||
|
}
|
||||||
|
case .image:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
private func trimEnd(_ input: RichText) -> RichText {
|
private func trimEnd(_ input: RichText) -> RichText {
|
||||||
return input
|
var text = input
|
||||||
|
switch input {
|
||||||
|
case .empty:
|
||||||
|
text = .empty
|
||||||
|
case let .plain(string):
|
||||||
|
text = .plain(string.replacingOccurrences(of: "[ \t\r\n]+$", with: "", options: .regularExpression, range: nil))
|
||||||
|
case let .bold(richText):
|
||||||
|
text = .bold(trimStart(richText))
|
||||||
|
case let .italic(richText):
|
||||||
|
text = .italic(trimStart(richText))
|
||||||
|
case let .underline(richText):
|
||||||
|
text = .underline(trimStart(richText))
|
||||||
|
case let .strikethrough(richText):
|
||||||
|
text = .strikethrough(trimStart(richText))
|
||||||
|
case let .fixed(richText):
|
||||||
|
text = .fixed(trimStart(richText))
|
||||||
|
case let .url(richText, url, webpageId):
|
||||||
|
text = .url(text: trimStart(richText), url: url, webpageId: webpageId)
|
||||||
|
case let .email(richText, email):
|
||||||
|
text = .email(text: trimStart(richText), email: email)
|
||||||
|
case let .subscript(richText):
|
||||||
|
text = .subscript(trimStart(richText))
|
||||||
|
case let .superscript(richText):
|
||||||
|
text = .superscript(trimStart(richText))
|
||||||
|
case let .marked(richText):
|
||||||
|
text = .marked(trimStart(richText))
|
||||||
|
case let .phone(richText, phone):
|
||||||
|
text = .phone(text: trimStart(richText), phone: phone)
|
||||||
|
case let .anchor(richText, name):
|
||||||
|
text = .anchor(text: trimStart(richText), name: name)
|
||||||
|
case var .concat(array):
|
||||||
|
if !array.isEmpty {
|
||||||
|
array[array.count - 1] = trimStart(array[array.count - 1])
|
||||||
|
text = .concat(array)
|
||||||
|
}
|
||||||
|
case .image:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
private func trim(_ input: RichText) -> RichText {
|
private func trim(_ input: RichText) -> RichText {
|
||||||
return input
|
var text = input
|
||||||
|
switch input {
|
||||||
|
case .empty:
|
||||||
|
text = .empty
|
||||||
|
case let .plain(string):
|
||||||
|
text = .plain(string.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||||
|
case let .bold(richText):
|
||||||
|
text = .bold(trimStart(richText))
|
||||||
|
case let .italic(richText):
|
||||||
|
text = .italic(trimStart(richText))
|
||||||
|
case let .underline(richText):
|
||||||
|
text = .underline(trimStart(richText))
|
||||||
|
case let .strikethrough(richText):
|
||||||
|
text = .strikethrough(trimStart(richText))
|
||||||
|
case let .fixed(richText):
|
||||||
|
text = .fixed(trimStart(richText))
|
||||||
|
case let .url(richText, url, webpageId):
|
||||||
|
text = .url(text: trimStart(richText), url: url, webpageId: webpageId)
|
||||||
|
case let .email(richText, email):
|
||||||
|
text = .email(text: trimStart(richText), email: email)
|
||||||
|
case let .subscript(richText):
|
||||||
|
text = .subscript(trimStart(richText))
|
||||||
|
case let .superscript(richText):
|
||||||
|
text = .superscript(trimStart(richText))
|
||||||
|
case let .marked(richText):
|
||||||
|
text = .marked(trimStart(richText))
|
||||||
|
case let .phone(richText, phone):
|
||||||
|
text = .phone(text: trimStart(richText), phone: phone)
|
||||||
|
case let .anchor(richText, name):
|
||||||
|
text = .anchor(text: trimStart(richText), name: name)
|
||||||
|
case var .concat(array):
|
||||||
|
if !array.isEmpty {
|
||||||
|
array[0] = trimStart(array[0])
|
||||||
|
array[array.count - 1] = trimEnd(array[array.count - 1])
|
||||||
|
text = .concat(array)
|
||||||
|
}
|
||||||
|
case .image:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addNewLine(_ input: RichText) -> RichText {
|
private func addNewLine(_ input: RichText) -> RichText {
|
||||||
@ -341,8 +463,10 @@ private func addNewLine(_ input: RichText) -> RichText {
|
|||||||
case let .anchor(richText, name):
|
case let .anchor(richText, name):
|
||||||
text = .anchor(text: addNewLine(richText), name: name)
|
text = .anchor(text: addNewLine(richText), name: name)
|
||||||
case var .concat(array):
|
case var .concat(array):
|
||||||
array[array.count - 1] = addNewLine(array[array.count - 1])
|
if !array.isEmpty {
|
||||||
text = .concat(array)
|
array[array.count - 1] = addNewLine(array[array.count - 1])
|
||||||
|
text = .concat(array)
|
||||||
|
}
|
||||||
case .image:
|
case .image:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -442,7 +566,7 @@ private func parseDetails(_ item: [String: Any], _ url: String, _ media: inout [
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func parseList(_ input: [String: Any], _ media: inout [MediaId: Media]) -> InstantPageBlock? {
|
private func parseList(_ input: [String: Any], _ url: String, _ media: inout [MediaId: Media]) -> InstantPageBlock? {
|
||||||
guard let content = input["content"] as? [Any], let tag = input["tag"] as? String else {
|
guard let content = input["content"] as? [Any], let tag = input["tag"] as? String else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -451,9 +575,40 @@ private func parseList(_ input: [String: Any], _ media: inout [MediaId: Media])
|
|||||||
guard let item = item as? [String: Any], let tag = item["tag"] as? String, tag == "li" else {
|
guard let item = item as? [String: Any], let tag = item["tag"] as? String, tag == "li" else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
items.append(.text(trim(parseRichText(item, &media)), nil))
|
var parseAsBlocks = false
|
||||||
|
if let subcontent = item["content"] as? [Any] {
|
||||||
|
for item in subcontent {
|
||||||
|
if let item = item as? [String: Any], let tag = item["tag"] as? String, ["ul", "ol"].contains(tag) {
|
||||||
|
parseAsBlocks = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if parseAsBlocks {
|
||||||
|
let blocks = parsePageBlocks(subcontent, url, &media)
|
||||||
|
if !blocks.isEmpty {
|
||||||
|
items.append(.blocks(blocks, nil))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
items.append(.text(trim(parseRichText(item, &media)), nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let ordered = tag == "ol"
|
let ordered = tag == "ol"
|
||||||
|
var allEmpty = true
|
||||||
|
for item in items {
|
||||||
|
if case let .text(text, _) = item {
|
||||||
|
if case .empty = text {
|
||||||
|
} else {
|
||||||
|
allEmpty = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
allEmpty = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
guard !allEmpty else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return .list(items: items, ordered: ordered)
|
return .list(items: items, ordered: ordered)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,6 +666,36 @@ private func parseImage(_ input: [String: Any], _ media: inout [MediaId: Media])
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func parseVideo(_ input: [String: Any], _ media: inout [MediaId: Media]) -> InstantPageBlock? {
|
||||||
|
guard let src = input["src"] as? String else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let width: Int32
|
||||||
|
if let value = input["width"] as? String, let intValue = Int32(value) {
|
||||||
|
width = intValue
|
||||||
|
} else {
|
||||||
|
width = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
let height: Int32
|
||||||
|
if let value = input["height"] as? String, let intValue = Int32(value) {
|
||||||
|
height = intValue
|
||||||
|
} else {
|
||||||
|
height = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return .webEmbed(
|
||||||
|
url: src,
|
||||||
|
html: nil,
|
||||||
|
dimensions: PixelDimensions(width: width, height: height),
|
||||||
|
caption: InstantPageCaption(text: .empty, credit: .empty),
|
||||||
|
stretchToWidth: true,
|
||||||
|
allowScrolling: false,
|
||||||
|
coverId: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private func parseFigure(_ input: [String: Any], _ media: inout [MediaId: Media]) -> InstantPageBlock? {
|
private func parseFigure(_ input: [String: Any], _ media: inout [MediaId: Media]) -> InstantPageBlock? {
|
||||||
guard let content = input["content"] as? [Any] else {
|
guard let content = input["content"] as? [Any] else {
|
||||||
return nil
|
return nil
|
||||||
@ -519,9 +704,19 @@ private func parseFigure(_ input: [String: Any], _ media: inout [MediaId: Media]
|
|||||||
var caption: RichText?
|
var caption: RichText?
|
||||||
for item in content {
|
for item in content {
|
||||||
if let item = item as? [String: Any], let tag = item["tag"] as? String {
|
if let item = item as? [String: Any], let tag = item["tag"] as? String {
|
||||||
if tag == "img" {
|
if tag == "p", let content = item["content"] as? [Any] {
|
||||||
|
for item in content {
|
||||||
|
if let item = item as? [String: Any], let tag = item["tag"] as? String {
|
||||||
|
if tag == "iframe" {
|
||||||
|
block = parseVideo(item, &media)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if tag == "iframe" {
|
||||||
|
block = parseVideo(item, &media)
|
||||||
|
} else if tag == "img" {
|
||||||
block = parseImage(item, &media)
|
block = parseImage(item, &media)
|
||||||
} else if tag == "figurecaption" {
|
} else if tag == "figcaption" {
|
||||||
caption = trim(parseRichText(item, &media))
|
caption = trim(parseRichText(item, &media))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -539,7 +734,7 @@ private func parsePageBlocks(_ input: [Any], _ url: String, _ media: inout [Medi
|
|||||||
var result: [InstantPageBlock] = []
|
var result: [InstantPageBlock] = []
|
||||||
for item in input {
|
for item in input {
|
||||||
if let string = item as? String {
|
if let string = item as? String {
|
||||||
result.append(.paragraph(parseRichText(string)))
|
result.append(.paragraph(trim(parseRichText(string))))
|
||||||
} else if let item = item as? [String: Any], let tag = item["tag"] as? String {
|
} else if let item = item as? [String: Any], let tag = item["tag"] as? String {
|
||||||
let content = item["content"] as? [Any]
|
let content = item["content"] as? [Any]
|
||||||
switch tag {
|
switch tag {
|
||||||
@ -557,7 +752,10 @@ private func parsePageBlocks(_ input: [Any], _ url: String, _ media: inout [Medi
|
|||||||
if let image = parseImage(item, &media) {
|
if let image = parseImage(item, &media) {
|
||||||
result.append(image)
|
result.append(image)
|
||||||
}
|
}
|
||||||
break
|
case "iframe":
|
||||||
|
if let video = parseVideo(item, &media) {
|
||||||
|
result.append(video)
|
||||||
|
}
|
||||||
case "figure":
|
case "figure":
|
||||||
if let figure = parseFigure(item, &media) {
|
if let figure = parseFigure(item, &media) {
|
||||||
result.append(figure)
|
result.append(figure)
|
||||||
@ -565,7 +763,7 @@ private func parsePageBlocks(_ input: [Any], _ url: String, _ media: inout [Medi
|
|||||||
case "table":
|
case "table":
|
||||||
result.append(parseTable(item, &media))
|
result.append(parseTable(item, &media))
|
||||||
case "ul", "ol":
|
case "ul", "ol":
|
||||||
if let list = parseList(item, &media) {
|
if let list = parseList(item, url, &media) {
|
||||||
result.append(list)
|
result.append(list)
|
||||||
}
|
}
|
||||||
case "hr":
|
case "hr":
|
||||||
|
@ -753,7 +753,7 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo
|
|||||||
let runRange = NSMakeRange(cfRunRange.location == kCFNotFound ? NSNotFound : cfRunRange.location, cfRunRange.length)
|
let runRange = NSMakeRange(cfRunRange.location == kCFNotFound ? NSNotFound : cfRunRange.location, cfRunRange.length)
|
||||||
string.enumerateAttributes(in: runRange, options: []) { attributes, range, _ in
|
string.enumerateAttributes(in: runRange, options: []) { attributes, range, _ in
|
||||||
if let id = attributes[NSAttributedString.Key.init(rawValue: InstantPageMediaIdAttribute)] as? Int64, let dimensions = attributes[NSAttributedString.Key.init(rawValue: InstantPageMediaDimensionsAttribute)] as? PixelDimensions {
|
if let id = attributes[NSAttributedString.Key.init(rawValue: InstantPageMediaIdAttribute)] as? Int64, let dimensions = attributes[NSAttributedString.Key.init(rawValue: InstantPageMediaDimensionsAttribute)] as? PixelDimensions {
|
||||||
var imageFrame = CGRect(origin: CGPoint(), size: dimensions.cgSize)
|
var imageFrame = CGRect(origin: CGPoint(), size: dimensions.cgSize.fitted(CGSize(width: boundingWidth, height: boundingWidth)))
|
||||||
|
|
||||||
let xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, nil)
|
let xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, nil)
|
||||||
let yOffset = fontLineHeight.isZero ? 0.0 : floorToScreenPixels((fontLineHeight - imageFrame.size.height) / 2.0)
|
let yOffset = fontLineHeight.isZero ? 0.0 : floorToScreenPixels((fontLineHeight - imageFrame.size.height) / 2.0)
|
||||||
|
@ -459,15 +459,26 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .prepaid(_, title, subtitle, prepaidGiveaway):
|
case let .prepaid(_, title, subtitle, prepaidGiveaway):
|
||||||
let color: GiftOptionItem.Icon.Color
|
let color: GiftOptionItem.Icon.Color
|
||||||
switch prepaidGiveaway.months {
|
switch prepaidGiveaway.prize {
|
||||||
case 3:
|
case let .premium(months):
|
||||||
color = .green
|
switch months {
|
||||||
case 6:
|
case 3:
|
||||||
color = .blue
|
color = .green
|
||||||
case 12:
|
case 6:
|
||||||
color = .red
|
color = .blue
|
||||||
default:
|
case 12:
|
||||||
color = .blue
|
color = .red
|
||||||
|
default:
|
||||||
|
color = .blue
|
||||||
|
}
|
||||||
|
case let .stars(amount):
|
||||||
|
if amount <= 1000 {
|
||||||
|
color = .green
|
||||||
|
} else if amount < 2500 {
|
||||||
|
color = .blue
|
||||||
|
} else {
|
||||||
|
color = .red
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: .image(color: color, name: "Premium/Giveaway"), title: title, titleFont: .bold, titleBadge: "\(prepaidGiveaway.quantity * 4)", subtitle: subtitle, sectionId: self.section, action: nil)
|
return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: .image(color: color, name: "Premium/Giveaway"), title: title, titleFont: .bold, titleBadge: "\(prepaidGiveaway.quantity * 4)", subtitle: subtitle, sectionId: self.section, action: nil)
|
||||||
case let .starsHeader(_, text, additionalText):
|
case let .starsHeader(_, text, additionalText):
|
||||||
@ -760,7 +771,18 @@ private func createGiveawayControllerEntries(
|
|||||||
entries.append(.giftStars(presentationData.theme, presentationData.strings.BoostGift_Prize_Stars, presentationData.strings.BoostGift_CreateGiveawayInfo, state.mode == .starsGiveaway))
|
entries.append(.giftStars(presentationData.theme, presentationData.strings.BoostGift_Prize_Stars, presentationData.strings.BoostGift_CreateGiveawayInfo, state.mode == .starsGiveaway))
|
||||||
case let .prepaid(prepaidGiveaway):
|
case let .prepaid(prepaidGiveaway):
|
||||||
entries.append(.prepaidHeader(presentationData.theme, presentationData.strings.BoostGift_PrepaidGiveawayTitle))
|
entries.append(.prepaidHeader(presentationData.theme, presentationData.strings.BoostGift_PrepaidGiveawayTitle))
|
||||||
entries.append(.prepaid(presentationData.theme, presentationData.strings.BoostGift_PrepaidGiveawayCount(prepaidGiveaway.quantity), presentationData.strings.BoostGift_PrepaidGiveawayMonths("\(prepaidGiveaway.months)").string, prepaidGiveaway))
|
let title: String
|
||||||
|
let text: String
|
||||||
|
switch prepaidGiveaway.prize {
|
||||||
|
case let .premium(months):
|
||||||
|
title = presentationData.strings.BoostGift_PrepaidGiveawayCount(prepaidGiveaway.quantity)
|
||||||
|
text = presentationData.strings.BoostGift_PrepaidGiveawayMonths("\(months)").string
|
||||||
|
case let .stars(stars):
|
||||||
|
//TODO:localize
|
||||||
|
title = "\(stars) Telegram Stars"
|
||||||
|
text = "among \(prepaidGiveaway.quantity) winners"
|
||||||
|
}
|
||||||
|
entries.append(.prepaid(presentationData.theme, title, text, prepaidGiveaway))
|
||||||
}
|
}
|
||||||
|
|
||||||
if case .starsGiveaway = state.mode, !starsGiveawayOptions.isEmpty {
|
if case .starsGiveaway = state.mode, !starsGiveawayOptions.isEmpty {
|
||||||
@ -988,10 +1010,20 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||||||
let actionsDisposable = DisposableSet()
|
let actionsDisposable = DisposableSet()
|
||||||
|
|
||||||
let initialSubscriptions: Int32
|
let initialSubscriptions: Int32
|
||||||
|
let initialStars: Int64
|
||||||
|
let initialWinners: Int32
|
||||||
if case let .prepaid(prepaidGiveaway) = subject {
|
if case let .prepaid(prepaidGiveaway) = subject {
|
||||||
|
if case let .stars(stars) = prepaidGiveaway.prize {
|
||||||
|
initialStars = stars
|
||||||
|
} else {
|
||||||
|
initialStars = 500
|
||||||
|
}
|
||||||
initialSubscriptions = prepaidGiveaway.quantity
|
initialSubscriptions = prepaidGiveaway.quantity
|
||||||
|
initialWinners = prepaidGiveaway.quantity
|
||||||
} else {
|
} else {
|
||||||
initialSubscriptions = 5
|
initialSubscriptions = 5
|
||||||
|
initialStars = 500
|
||||||
|
initialWinners = 5
|
||||||
}
|
}
|
||||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
|
||||||
@ -1009,7 +1041,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||||||
let minDate = currentTime + 60 * 1
|
let minDate = currentTime + 60 * 1
|
||||||
let maxDate = currentTime + context.userLimits.maxGiveawayPeriodSeconds
|
let maxDate = currentTime + context.userLimits.maxGiveawayPeriodSeconds
|
||||||
|
|
||||||
let initialState: CreateGiveawayControllerState = CreateGiveawayControllerState(mode: .giveaway, subscriptions: initialSubscriptions, stars: 500, winners: 5, time: expiryTime)
|
let initialState: CreateGiveawayControllerState = CreateGiveawayControllerState(mode: .giveaway, subscriptions: initialSubscriptions, stars: initialStars, winners: initialWinners, time: expiryTime)
|
||||||
|
|
||||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||||
let stateValue = Atomic(value: initialState)
|
let stateValue = Atomic(value: initialState)
|
||||||
|
@ -29,7 +29,7 @@ final class CreateGiveawayHeaderItem: ItemListControllerHeaderItem {
|
|||||||
|
|
||||||
func isEqual(to: ItemListControllerHeaderItem) -> Bool {
|
func isEqual(to: ItemListControllerHeaderItem) -> Bool {
|
||||||
if let item = to as? CreateGiveawayHeaderItem {
|
if let item = to as? CreateGiveawayHeaderItem {
|
||||||
return self.theme === item.theme && self.title == item.title && self.text == item.text
|
return self.theme === item.theme && self.title == item.title && self.text == item.text && self.isStars == item.isStars
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -198,16 +198,32 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
|
|||||||
|
|
||||||
self.backgroundNode.update(size: CGSize(width: layout.size.width, height: navigationBarHeight), transition: transition)
|
self.backgroundNode.update(size: CGSize(width: layout.size.width, height: navigationBarHeight), transition: transition)
|
||||||
|
|
||||||
|
let colors: [UIColor]
|
||||||
|
let particleColor: UIColor?
|
||||||
|
if self.item.isStars {
|
||||||
|
colors = [
|
||||||
|
UIColor(rgb: 0xe57d02),
|
||||||
|
UIColor(rgb: 0xf09903),
|
||||||
|
UIColor(rgb: 0xf9b004),
|
||||||
|
UIColor(rgb: 0xfdd219)
|
||||||
|
]
|
||||||
|
particleColor = UIColor(rgb: 0xf9b004)
|
||||||
|
} else {
|
||||||
|
colors = [
|
||||||
|
UIColor(rgb: 0x6a94ff),
|
||||||
|
UIColor(rgb: 0x9472fd),
|
||||||
|
UIColor(rgb: 0xe26bd3)
|
||||||
|
]
|
||||||
|
particleColor = nil
|
||||||
|
}
|
||||||
|
|
||||||
let component = AnyComponent(PremiumStarComponent(
|
let component = AnyComponent(PremiumStarComponent(
|
||||||
theme: self.item.theme,
|
theme: self.item.theme,
|
||||||
isIntro: true,
|
isIntro: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
hasIdleAnimations: true,
|
hasIdleAnimations: true,
|
||||||
colors: [
|
colors: colors,
|
||||||
UIColor(rgb: 0x6a94ff),
|
particleColor: particleColor
|
||||||
UIColor(rgb: 0x9472fd),
|
|
||||||
UIColor(rgb: 0xe26bd3)
|
|
||||||
]
|
|
||||||
))
|
))
|
||||||
let containerSize = CGSize(width: min(414.0, layout.size.width), height: 220.0)
|
let containerSize = CGSize(width: min(414.0, layout.size.width), height: 220.0)
|
||||||
|
|
||||||
|
@ -1038,15 +1038,26 @@ private enum StatsEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
case let .boostPrepaid(_, _, title, subtitle, prepaidGiveaway):
|
case let .boostPrepaid(_, _, title, subtitle, prepaidGiveaway):
|
||||||
let color: GiftOptionItem.Icon.Color
|
let color: GiftOptionItem.Icon.Color
|
||||||
switch prepaidGiveaway.months {
|
switch prepaidGiveaway.prize {
|
||||||
case 3:
|
case let .premium(months):
|
||||||
color = .green
|
switch months {
|
||||||
case 6:
|
case 3:
|
||||||
color = .blue
|
color = .green
|
||||||
case 12:
|
case 6:
|
||||||
color = .red
|
color = .blue
|
||||||
default:
|
case 12:
|
||||||
color = .blue
|
color = .red
|
||||||
|
default:
|
||||||
|
color = .blue
|
||||||
|
}
|
||||||
|
case let .stars(amount):
|
||||||
|
if amount <= 1000 {
|
||||||
|
color = .green
|
||||||
|
} else if amount < 2500 {
|
||||||
|
color = .blue
|
||||||
|
} else {
|
||||||
|
color = .red
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: .image(color: color, name: "Premium/Giveaway"), title: title, titleFont: .bold, titleBadge: "\(prepaidGiveaway.quantity * 4)", subtitle: subtitle, label: nil, sectionId: self.section, action: {
|
return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: .image(color: color, name: "Premium/Giveaway"), title: title, titleFont: .bold, titleBadge: "\(prepaidGiveaway.quantity * 4)", subtitle: subtitle, label: nil, sectionId: self.section, action: {
|
||||||
arguments.createPrepaidGiveaway(prepaidGiveaway)
|
arguments.createPrepaidGiveaway(prepaidGiveaway)
|
||||||
@ -1423,7 +1434,17 @@ private func boostsEntries(
|
|||||||
entries.append(.boostPrepaidTitle(presentationData.theme, presentationData.strings.Stats_Boosts_PrepaidGiveawaysTitle))
|
entries.append(.boostPrepaidTitle(presentationData.theme, presentationData.strings.Stats_Boosts_PrepaidGiveawaysTitle))
|
||||||
var i: Int32 = 0
|
var i: Int32 = 0
|
||||||
for giveaway in boostData.prepaidGiveaways {
|
for giveaway in boostData.prepaidGiveaways {
|
||||||
entries.append(.boostPrepaid(i, presentationData.theme, presentationData.strings.Stats_Boosts_PrepaidGiveawayCount(giveaway.quantity), presentationData.strings.Stats_Boosts_PrepaidGiveawayMonths("\(giveaway.months)").string, giveaway))
|
let title: String
|
||||||
|
let text: String
|
||||||
|
switch giveaway.prize {
|
||||||
|
case let .premium(months):
|
||||||
|
title = presentationData.strings.Stats_Boosts_PrepaidGiveawayCount(giveaway.quantity)
|
||||||
|
text = presentationData.strings.Stats_Boosts_PrepaidGiveawayMonths("\(months)").string
|
||||||
|
case let .stars(stars):
|
||||||
|
title = "\(stars) Telegram Stars"
|
||||||
|
text = "among \(giveaway.quantity) winners"
|
||||||
|
}
|
||||||
|
entries.append(.boostPrepaid(i, presentationData.theme, title, text, giveaway))
|
||||||
i += 1
|
i += 1
|
||||||
}
|
}
|
||||||
entries.append(.boostPrepaidInfo(presentationData.theme, presentationData.strings.Stats_Boosts_PrepaidGiveawaysInfo))
|
entries.append(.boostPrepaidInfo(presentationData.theme, presentationData.strings.Stats_Boosts_PrepaidGiveawaysInfo))
|
||||||
|
@ -80,7 +80,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[1821253126] = { return Api.Birthday.parse_birthday($0) }
|
dict[1821253126] = { return Api.Birthday.parse_birthday($0) }
|
||||||
dict[-1132882121] = { return Api.Bool.parse_boolFalse($0) }
|
dict[-1132882121] = { return Api.Bool.parse_boolFalse($0) }
|
||||||
dict[-1720552011] = { return Api.Bool.parse_boolTrue($0) }
|
dict[-1720552011] = { return Api.Bool.parse_boolTrue($0) }
|
||||||
dict[706514033] = { return Api.Boost.parse_boost($0) }
|
dict[1262359766] = { return Api.Boost.parse_boost($0) }
|
||||||
dict[-1778593322] = { return Api.BotApp.parse_botApp($0) }
|
dict[-1778593322] = { return Api.BotApp.parse_botApp($0) }
|
||||||
dict[1571189943] = { return Api.BotApp.parse_botAppNotModified($0) }
|
dict[1571189943] = { return Api.BotApp.parse_botAppNotModified($0) }
|
||||||
dict[-1989921868] = { return Api.BotBusinessConnection.parse_botBusinessConnection($0) }
|
dict[-1989921868] = { return Api.BotBusinessConnection.parse_botBusinessConnection($0) }
|
||||||
@ -553,7 +553,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[-935499028] = { return Api.MessageAction.parse_messageActionGiftPremium($0) }
|
dict[-935499028] = { return Api.MessageAction.parse_messageActionGiftPremium($0) }
|
||||||
dict[1171632161] = { return Api.MessageAction.parse_messageActionGiftStars($0) }
|
dict[1171632161] = { return Api.MessageAction.parse_messageActionGiftStars($0) }
|
||||||
dict[-1475391004] = { return Api.MessageAction.parse_messageActionGiveawayLaunch($0) }
|
dict[-1475391004] = { return Api.MessageAction.parse_messageActionGiveawayLaunch($0) }
|
||||||
dict[715107781] = { return Api.MessageAction.parse_messageActionGiveawayResults($0) }
|
dict[-2015170219] = { return Api.MessageAction.parse_messageActionGiveawayResults($0) }
|
||||||
dict[2047704898] = { return Api.MessageAction.parse_messageActionGroupCall($0) }
|
dict[2047704898] = { return Api.MessageAction.parse_messageActionGroupCall($0) }
|
||||||
dict[-1281329567] = { return Api.MessageAction.parse_messageActionGroupCallScheduled($0) }
|
dict[-1281329567] = { return Api.MessageAction.parse_messageActionGroupCallScheduled($0) }
|
||||||
dict[-1615153660] = { return Api.MessageAction.parse_messageActionHistoryClear($0) }
|
dict[-1615153660] = { return Api.MessageAction.parse_messageActionHistoryClear($0) }
|
||||||
@ -746,6 +746,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[1958953753] = { return Api.PremiumGiftOption.parse_premiumGiftOption($0) }
|
dict[1958953753] = { return Api.PremiumGiftOption.parse_premiumGiftOption($0) }
|
||||||
dict[1596792306] = { return Api.PremiumSubscriptionOption.parse_premiumSubscriptionOption($0) }
|
dict[1596792306] = { return Api.PremiumSubscriptionOption.parse_premiumSubscriptionOption($0) }
|
||||||
dict[-1303143084] = { return Api.PrepaidGiveaway.parse_prepaidGiveaway($0) }
|
dict[-1303143084] = { return Api.PrepaidGiveaway.parse_prepaidGiveaway($0) }
|
||||||
|
dict[-1973402371] = { return Api.PrepaidGiveaway.parse_prepaidStarsGiveaway($0) }
|
||||||
dict[-1534675103] = { return Api.PrivacyKey.parse_privacyKeyAbout($0) }
|
dict[-1534675103] = { return Api.PrivacyKey.parse_privacyKeyAbout($0) }
|
||||||
dict[1124062251] = { return Api.PrivacyKey.parse_privacyKeyAddedByPhone($0) }
|
dict[1124062251] = { return Api.PrivacyKey.parse_privacyKeyAddedByPhone($0) }
|
||||||
dict[536913176] = { return Api.PrivacyKey.parse_privacyKeyBirthday($0) }
|
dict[536913176] = { return Api.PrivacyKey.parse_privacyKeyBirthday($0) }
|
||||||
|
@ -962,13 +962,13 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
enum Boost: TypeConstructorDescription {
|
enum Boost: TypeConstructorDescription {
|
||||||
case boost(flags: Int32, id: String, userId: Int64?, giveawayMsgId: Int32?, date: Int32, expires: Int32, usedGiftSlug: String?, multiplier: Int32?)
|
case boost(flags: Int32, id: String, userId: Int64?, giveawayMsgId: Int32?, date: Int32, expires: Int32, usedGiftSlug: String?, multiplier: Int32?, stars: Int64?)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
case .boost(let flags, let id, let userId, let giveawayMsgId, let date, let expires, let usedGiftSlug, let multiplier):
|
case .boost(let flags, let id, let userId, let giveawayMsgId, let date, let expires, let usedGiftSlug, let multiplier, let stars):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(706514033)
|
buffer.appendInt32(1262359766)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeString(id, buffer: buffer, boxed: false)
|
serializeString(id, buffer: buffer, boxed: false)
|
||||||
@ -978,14 +978,15 @@ public extension Api {
|
|||||||
serializeInt32(expires, buffer: buffer, boxed: false)
|
serializeInt32(expires, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 4) != 0 {serializeString(usedGiftSlug!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 4) != 0 {serializeString(usedGiftSlug!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 5) != 0 {serializeInt32(multiplier!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 5) != 0 {serializeInt32(multiplier!, buffer: buffer, boxed: false)}
|
||||||
|
if Int(flags) & Int(1 << 6) != 0 {serializeInt64(stars!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
switch self {
|
||||||
case .boost(let flags, let id, let userId, let giveawayMsgId, let date, let expires, let usedGiftSlug, let multiplier):
|
case .boost(let flags, let id, let userId, let giveawayMsgId, let date, let expires, let usedGiftSlug, let multiplier, let stars):
|
||||||
return ("boost", [("flags", flags as Any), ("id", id as Any), ("userId", userId as Any), ("giveawayMsgId", giveawayMsgId as Any), ("date", date as Any), ("expires", expires as Any), ("usedGiftSlug", usedGiftSlug as Any), ("multiplier", multiplier as Any)])
|
return ("boost", [("flags", flags as Any), ("id", id as Any), ("userId", userId as Any), ("giveawayMsgId", giveawayMsgId as Any), ("date", date as Any), ("expires", expires as Any), ("usedGiftSlug", usedGiftSlug as Any), ("multiplier", multiplier as Any), ("stars", stars as Any)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1006,6 +1007,8 @@ public extension Api {
|
|||||||
if Int(_1!) & Int(1 << 4) != 0 {_7 = parseString(reader) }
|
if Int(_1!) & Int(1 << 4) != 0 {_7 = parseString(reader) }
|
||||||
var _8: Int32?
|
var _8: Int32?
|
||||||
if Int(_1!) & Int(1 << 5) != 0 {_8 = reader.readInt32() }
|
if Int(_1!) & Int(1 << 5) != 0 {_8 = reader.readInt32() }
|
||||||
|
var _9: Int64?
|
||||||
|
if Int(_1!) & Int(1 << 6) != 0 {_9 = reader.readInt64() }
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||||
@ -1014,8 +1017,9 @@ public extension Api {
|
|||||||
let _c6 = _6 != nil
|
let _c6 = _6 != nil
|
||||||
let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil
|
let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil
|
||||||
let _c8 = (Int(_1!) & Int(1 << 5) == 0) || _8 != nil
|
let _c8 = (Int(_1!) & Int(1 << 5) == 0) || _8 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
|
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil
|
||||||
return Api.Boost.boost(flags: _1!, id: _2!, userId: _3, giveawayMsgId: _4, date: _5!, expires: _6!, usedGiftSlug: _7, multiplier: _8)
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
||||||
|
return Api.Boost.boost(flags: _1!, id: _2!, userId: _3, giveawayMsgId: _4, date: _5!, expires: _6!, usedGiftSlug: _7, multiplier: _8, stars: _9)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -990,7 +990,7 @@ public extension Api {
|
|||||||
case messageActionGiftPremium(flags: Int32, currency: String, amount: Int64, months: Int32, cryptoCurrency: String?, cryptoAmount: Int64?)
|
case messageActionGiftPremium(flags: Int32, currency: String, amount: Int64, months: Int32, cryptoCurrency: String?, cryptoAmount: Int64?)
|
||||||
case messageActionGiftStars(flags: Int32, currency: String, amount: Int64, stars: Int64, cryptoCurrency: String?, cryptoAmount: Int64?, transactionId: String?)
|
case messageActionGiftStars(flags: Int32, currency: String, amount: Int64, stars: Int64, cryptoCurrency: String?, cryptoAmount: Int64?, transactionId: String?)
|
||||||
case messageActionGiveawayLaunch(flags: Int32, stars: Int64?)
|
case messageActionGiveawayLaunch(flags: Int32, stars: Int64?)
|
||||||
case messageActionGiveawayResults(winnersCount: Int32, unclaimedCount: Int32)
|
case messageActionGiveawayResults(flags: Int32, winnersCount: Int32, unclaimedCount: Int32)
|
||||||
case messageActionGroupCall(flags: Int32, call: Api.InputGroupCall, duration: Int32?)
|
case messageActionGroupCall(flags: Int32, call: Api.InputGroupCall, duration: Int32?)
|
||||||
case messageActionGroupCallScheduled(call: Api.InputGroupCall, scheduleDate: Int32)
|
case messageActionGroupCallScheduled(call: Api.InputGroupCall, scheduleDate: Int32)
|
||||||
case messageActionHistoryClear
|
case messageActionHistoryClear
|
||||||
@ -1183,10 +1183,11 @@ public extension Api {
|
|||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(stars!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(stars!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
case .messageActionGiveawayResults(let winnersCount, let unclaimedCount):
|
case .messageActionGiveawayResults(let flags, let winnersCount, let unclaimedCount):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(715107781)
|
buffer.appendInt32(-2015170219)
|
||||||
}
|
}
|
||||||
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt32(winnersCount, buffer: buffer, boxed: false)
|
serializeInt32(winnersCount, buffer: buffer, boxed: false)
|
||||||
serializeInt32(unclaimedCount, buffer: buffer, boxed: false)
|
serializeInt32(unclaimedCount, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
@ -1436,8 +1437,8 @@ public extension Api {
|
|||||||
return ("messageActionGiftStars", [("flags", flags as Any), ("currency", currency as Any), ("amount", amount as Any), ("stars", stars as Any), ("cryptoCurrency", cryptoCurrency as Any), ("cryptoAmount", cryptoAmount as Any), ("transactionId", transactionId as Any)])
|
return ("messageActionGiftStars", [("flags", flags as Any), ("currency", currency as Any), ("amount", amount as Any), ("stars", stars as Any), ("cryptoCurrency", cryptoCurrency as Any), ("cryptoAmount", cryptoAmount as Any), ("transactionId", transactionId as Any)])
|
||||||
case .messageActionGiveawayLaunch(let flags, let stars):
|
case .messageActionGiveawayLaunch(let flags, let stars):
|
||||||
return ("messageActionGiveawayLaunch", [("flags", flags as Any), ("stars", stars as Any)])
|
return ("messageActionGiveawayLaunch", [("flags", flags as Any), ("stars", stars as Any)])
|
||||||
case .messageActionGiveawayResults(let winnersCount, let unclaimedCount):
|
case .messageActionGiveawayResults(let flags, let winnersCount, let unclaimedCount):
|
||||||
return ("messageActionGiveawayResults", [("winnersCount", winnersCount as Any), ("unclaimedCount", unclaimedCount as Any)])
|
return ("messageActionGiveawayResults", [("flags", flags as Any), ("winnersCount", winnersCount as Any), ("unclaimedCount", unclaimedCount as Any)])
|
||||||
case .messageActionGroupCall(let flags, let call, let duration):
|
case .messageActionGroupCall(let flags, let call, let duration):
|
||||||
return ("messageActionGroupCall", [("flags", flags as Any), ("call", call as Any), ("duration", duration as Any)])
|
return ("messageActionGroupCall", [("flags", flags as Any), ("call", call as Any), ("duration", duration as Any)])
|
||||||
case .messageActionGroupCallScheduled(let call, let scheduleDate):
|
case .messageActionGroupCallScheduled(let call, let scheduleDate):
|
||||||
@ -1794,10 +1795,13 @@ public extension Api {
|
|||||||
_1 = reader.readInt32()
|
_1 = reader.readInt32()
|
||||||
var _2: Int32?
|
var _2: Int32?
|
||||||
_2 = reader.readInt32()
|
_2 = reader.readInt32()
|
||||||
|
var _3: Int32?
|
||||||
|
_3 = reader.readInt32()
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
if _c1 && _c2 {
|
let _c3 = _3 != nil
|
||||||
return Api.MessageAction.messageActionGiveawayResults(winnersCount: _1!, unclaimedCount: _2!)
|
if _c1 && _c2 && _c3 {
|
||||||
|
return Api.MessageAction.messageActionGiveawayResults(flags: _1!, winnersCount: _2!, unclaimedCount: _3!)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1013,6 +1013,7 @@ public extension Api {
|
|||||||
public extension Api {
|
public extension Api {
|
||||||
enum PrepaidGiveaway: TypeConstructorDescription {
|
enum PrepaidGiveaway: TypeConstructorDescription {
|
||||||
case prepaidGiveaway(id: Int64, months: Int32, quantity: Int32, date: Int32)
|
case prepaidGiveaway(id: Int64, months: Int32, quantity: Int32, date: Int32)
|
||||||
|
case prepaidStarsGiveaway(id: Int64, stars: Int64, quantity: Int32, date: Int32)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
@ -1025,6 +1026,15 @@ public extension Api {
|
|||||||
serializeInt32(quantity, buffer: buffer, boxed: false)
|
serializeInt32(quantity, buffer: buffer, boxed: false)
|
||||||
serializeInt32(date, buffer: buffer, boxed: false)
|
serializeInt32(date, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
|
case .prepaidStarsGiveaway(let id, let stars, let quantity, let date):
|
||||||
|
if boxed {
|
||||||
|
buffer.appendInt32(-1973402371)
|
||||||
|
}
|
||||||
|
serializeInt64(id, buffer: buffer, boxed: false)
|
||||||
|
serializeInt64(stars, buffer: buffer, boxed: false)
|
||||||
|
serializeInt32(quantity, buffer: buffer, boxed: false)
|
||||||
|
serializeInt32(date, buffer: buffer, boxed: false)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1032,6 +1042,8 @@ public extension Api {
|
|||||||
switch self {
|
switch self {
|
||||||
case .prepaidGiveaway(let id, let months, let quantity, let date):
|
case .prepaidGiveaway(let id, let months, let quantity, let date):
|
||||||
return ("prepaidGiveaway", [("id", id as Any), ("months", months as Any), ("quantity", quantity as Any), ("date", date as Any)])
|
return ("prepaidGiveaway", [("id", id as Any), ("months", months as Any), ("quantity", quantity as Any), ("date", date as Any)])
|
||||||
|
case .prepaidStarsGiveaway(let id, let stars, let quantity, let date):
|
||||||
|
return ("prepaidStarsGiveaway", [("id", id as Any), ("stars", stars as Any), ("quantity", quantity as Any), ("date", date as Any)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1055,6 +1067,26 @@ public extension Api {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static func parse_prepaidStarsGiveaway(_ reader: BufferReader) -> PrepaidGiveaway? {
|
||||||
|
var _1: Int64?
|
||||||
|
_1 = reader.readInt64()
|
||||||
|
var _2: Int64?
|
||||||
|
_2 = reader.readInt64()
|
||||||
|
var _3: Int32?
|
||||||
|
_3 = reader.readInt32()
|
||||||
|
var _4: Int32?
|
||||||
|
_4 = reader.readInt32()
|
||||||
|
let _c1 = _1 != nil
|
||||||
|
let _c2 = _2 != nil
|
||||||
|
let _c3 = _3 != nil
|
||||||
|
let _c4 = _4 != nil
|
||||||
|
if _c1 && _c2 && _c3 && _c4 {
|
||||||
|
return Api.PrepaidGiveaway.prepaidStarsGiveaway(id: _1!, stars: _2!, quantity: _3!, date: _4!)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,8 +137,8 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
|||||||
return TelegramMediaAction(action: .giftCode(slug: slug, fromGiveaway: (flags & (1 << 0)) != 0, isUnclaimed: (flags & (1 << 2)) != 0, boostPeerId: boostPeer?.peerId, months: months, currency: currency, amount: amount, cryptoCurrency: cryptoCurrency, cryptoAmount: cryptoAmount))
|
return TelegramMediaAction(action: .giftCode(slug: slug, fromGiveaway: (flags & (1 << 0)) != 0, isUnclaimed: (flags & (1 << 2)) != 0, boostPeerId: boostPeer?.peerId, months: months, currency: currency, amount: amount, cryptoCurrency: cryptoCurrency, cryptoAmount: cryptoAmount))
|
||||||
case let .messageActionGiveawayLaunch(_, stars):
|
case let .messageActionGiveawayLaunch(_, stars):
|
||||||
return TelegramMediaAction(action: .giveawayLaunched(stars: stars))
|
return TelegramMediaAction(action: .giveawayLaunched(stars: stars))
|
||||||
case let .messageActionGiveawayResults(winners, unclaimed):
|
case let .messageActionGiveawayResults(flags, winners, unclaimed):
|
||||||
return TelegramMediaAction(action: .giveawayResults(winners: winners, unclaimed: unclaimed))
|
return TelegramMediaAction(action: .giveawayResults(winners: winners, unclaimed: unclaimed, stars: (flags & (1 << 0)) != 0))
|
||||||
case let .messageActionBoostApply(boosts):
|
case let .messageActionBoostApply(boosts):
|
||||||
return TelegramMediaAction(action: .boostsApplied(boosts: boosts))
|
return TelegramMediaAction(action: .boostsApplied(boosts: boosts))
|
||||||
case let .messageActionPaymentRefunded(_, peer, currency, totalAmount, payload, charge):
|
case let .messageActionPaymentRefunded(_, peer, currency, totalAmount, payload, charge):
|
||||||
|
@ -279,7 +279,7 @@ private final class ChannelBoostersContextImpl {
|
|||||||
var resultBoosts: [ChannelBoostersContext.State.Boost] = []
|
var resultBoosts: [ChannelBoostersContext.State.Boost] = []
|
||||||
for boost in boosts {
|
for boost in boosts {
|
||||||
switch boost {
|
switch boost {
|
||||||
case let .boost(flags, id, userId, giveawayMessageId, date, expires, usedGiftSlug, multiplier):
|
case let .boost(flags, id, userId, giveawayMessageId, date, expires, usedGiftSlug, multiplier, stars):
|
||||||
var boostFlags: ChannelBoostersContext.State.Boost.Flags = []
|
var boostFlags: ChannelBoostersContext.State.Boost.Flags = []
|
||||||
var boostPeer: EnginePeer?
|
var boostPeer: EnginePeer?
|
||||||
if let userId = userId {
|
if let userId = userId {
|
||||||
@ -297,7 +297,7 @@ private final class ChannelBoostersContextImpl {
|
|||||||
if (flags & (1 << 3)) != 0 {
|
if (flags & (1 << 3)) != 0 {
|
||||||
boostFlags.insert(.isUnclaimed)
|
boostFlags.insert(.isUnclaimed)
|
||||||
}
|
}
|
||||||
resultBoosts.append(ChannelBoostersContext.State.Boost(flags: boostFlags, id: id, peer: boostPeer, date: date, expires: expires, multiplier: multiplier ?? 1, slug: usedGiftSlug, giveawayMessageId: giveawayMessageId.flatMap { EngineMessage.Id(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }))
|
resultBoosts.append(ChannelBoostersContext.State.Boost(flags: boostFlags, id: id, peer: boostPeer, date: date, expires: expires, multiplier: multiplier ?? 1, stars: stars, slug: usedGiftSlug, giveawayMessageId: giveawayMessageId.flatMap { EngineMessage.Id(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if populateCache {
|
if populateCache {
|
||||||
@ -388,6 +388,7 @@ public final class ChannelBoostersContext {
|
|||||||
public var date: Int32
|
public var date: Int32
|
||||||
public var expires: Int32
|
public var expires: Int32
|
||||||
public var multiplier: Int32
|
public var multiplier: Int32
|
||||||
|
public var stars: Int64?
|
||||||
public var slug: String?
|
public var slug: String?
|
||||||
public var giveawayMessageId: EngineMessage.Id?
|
public var giveawayMessageId: EngineMessage.Id?
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
case giftCode(slug: String, fromGiveaway: Bool, isUnclaimed: Bool, boostPeerId: PeerId?, months: Int32, currency: String?, amount: Int64?, cryptoCurrency: String?, cryptoAmount: Int64?)
|
case giftCode(slug: String, fromGiveaway: Bool, isUnclaimed: Bool, boostPeerId: PeerId?, months: Int32, currency: String?, amount: Int64?, cryptoCurrency: String?, cryptoAmount: Int64?)
|
||||||
case giveawayLaunched(stars: Int64?)
|
case giveawayLaunched(stars: Int64?)
|
||||||
case joinedChannel
|
case joinedChannel
|
||||||
case giveawayResults(winners: Int32, unclaimed: Int32)
|
case giveawayResults(winners: Int32, unclaimed: Int32, stars: Bool)
|
||||||
case boostsApplied(boosts: Int32)
|
case boostsApplied(boosts: Int32)
|
||||||
case paymentRefunded(peerId: PeerId, currency: String, totalAmount: Int64, payload: Data?, transactionId: String)
|
case paymentRefunded(peerId: PeerId, currency: String, totalAmount: Int64, payload: Data?, transactionId: String)
|
||||||
case giftStars(currency: String, amount: Int64, count: Int64, cryptoCurrency: String?, cryptoAmount: Int64?, transactionId: String?)
|
case giftStars(currency: String, amount: Int64, count: Int64, cryptoCurrency: String?, cryptoAmount: Int64?, transactionId: String?)
|
||||||
@ -235,7 +235,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
case 38:
|
case 38:
|
||||||
self = .joinedChannel
|
self = .joinedChannel
|
||||||
case 39:
|
case 39:
|
||||||
self = .giveawayResults(winners: decoder.decodeInt32ForKey("winners", orElse: 0), unclaimed: decoder.decodeInt32ForKey("unclaimed", orElse: 0))
|
self = .giveawayResults(winners: decoder.decodeInt32ForKey("winners", orElse: 0), unclaimed: decoder.decodeInt32ForKey("unclaimed", orElse: 0), stars: decoder.decodeBoolForKey("stars", orElse: false))
|
||||||
case 40:
|
case 40:
|
||||||
self = .boostsApplied(boosts: decoder.decodeInt32ForKey("boosts", orElse: 0))
|
self = .boostsApplied(boosts: decoder.decodeInt32ForKey("boosts", orElse: 0))
|
||||||
case 41:
|
case 41:
|
||||||
@ -470,10 +470,11 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
}
|
}
|
||||||
case .joinedChannel:
|
case .joinedChannel:
|
||||||
encoder.encodeInt32(38, forKey: "_rawValue")
|
encoder.encodeInt32(38, forKey: "_rawValue")
|
||||||
case let .giveawayResults(winners, unclaimed):
|
case let .giveawayResults(winners, unclaimed, stars):
|
||||||
encoder.encodeInt32(39, forKey: "_rawValue")
|
encoder.encodeInt32(39, forKey: "_rawValue")
|
||||||
encoder.encodeInt32(winners, forKey: "winners")
|
encoder.encodeInt32(winners, forKey: "winners")
|
||||||
encoder.encodeInt32(unclaimed, forKey: "unclaimed")
|
encoder.encodeInt32(unclaimed, forKey: "unclaimed")
|
||||||
|
encoder.encodeBool(stars, forKey: "stars")
|
||||||
case let .boostsApplied(boosts):
|
case let .boostsApplied(boosts):
|
||||||
encoder.encodeInt32(40, forKey: "_rawValue")
|
encoder.encodeInt32(40, forKey: "_rawValue")
|
||||||
encoder.encodeInt32(boosts, forKey: "boosts")
|
encoder.encodeInt32(boosts, forKey: "boosts")
|
||||||
|
@ -89,8 +89,13 @@ public enum PremiumGiveawayInfo: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public struct PrepaidGiveaway: Equatable {
|
public struct PrepaidGiveaway: Equatable {
|
||||||
|
public enum Prize: Equatable {
|
||||||
|
case premium(months: Int32)
|
||||||
|
case stars(stars: Int64)
|
||||||
|
}
|
||||||
|
|
||||||
public let id: Int64
|
public let id: Int64
|
||||||
public let months: Int32
|
public let prize: Prize
|
||||||
public let quantity: Int32
|
public let quantity: Int32
|
||||||
public let date: Int32
|
public let date: Int32
|
||||||
}
|
}
|
||||||
@ -304,7 +309,12 @@ extension PrepaidGiveaway {
|
|||||||
switch apiPrepaidGiveaway {
|
switch apiPrepaidGiveaway {
|
||||||
case let .prepaidGiveaway(id, months, quantity, date):
|
case let .prepaidGiveaway(id, months, quantity, date):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.months = months
|
self.prize = .premium(months: months)
|
||||||
|
self.quantity = quantity
|
||||||
|
self.date = date
|
||||||
|
case let .prepaidStarsGiveaway(id, stars, quantity, date):
|
||||||
|
self.id = id
|
||||||
|
self.prize = .stars(stars: stars)
|
||||||
self.quantity = quantity
|
self.quantity = quantity
|
||||||
self.date = date
|
self.date = date
|
||||||
}
|
}
|
||||||
|
@ -981,7 +981,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
|
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
|
||||||
case .joinedChannel:
|
case .joinedChannel:
|
||||||
attributedString = NSAttributedString(string: strings.Notification_ChannelJoinedByYou, font: titleBoldFont, textColor: primaryTextColor)
|
attributedString = NSAttributedString(string: strings.Notification_ChannelJoinedByYou, font: titleBoldFont, textColor: primaryTextColor)
|
||||||
case let .giveawayResults(winners, unclaimed):
|
case let .giveawayResults(winners, unclaimed, _):
|
||||||
if winners == 0 {
|
if winners == 0 {
|
||||||
attributedString = parseMarkdownIntoAttributedString(strings.Notification_GiveawayResultsNoWinners(unclaimed), attributes: MarkdownAttributes(body: bodyAttributes, bold: boldAttributes, link: bodyAttributes, linkAttribute: { _ in return nil }))
|
attributedString = parseMarkdownIntoAttributedString(strings.Notification_GiveawayResultsNoWinners(unclaimed), attributes: MarkdownAttributes(body: bodyAttributes, bold: boldAttributes, link: bodyAttributes, linkAttribute: { _ in return nil }))
|
||||||
} else if unclaimed > 0 {
|
} else if unclaimed > 0 {
|
||||||
|
@ -1728,7 +1728,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
|||||||
attributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedString.string))
|
attributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedString.string))
|
||||||
}
|
}
|
||||||
if let range = attributedString.string.range(of: "*") {
|
if let range = attributedString.string.range(of: "*") {
|
||||||
attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: attributedString.string))
|
attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 1, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: attributedString.string))
|
||||||
attributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedString.string))
|
attributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedString.string))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5967,7 +5967,17 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
}
|
}
|
||||||
|
|
||||||
var addedPrivacy = false
|
var addedPrivacy = false
|
||||||
if let privacyPolicyUrl = (data.cachedData as? CachedUserData)?.botInfo?.privacyPolicyUrl {
|
var privacyPolicyUrl: String?
|
||||||
|
if let cachedData = (data.cachedData as? CachedUserData), let botInfo = cachedData.botInfo {
|
||||||
|
if let url = botInfo.privacyPolicyUrl {
|
||||||
|
privacyPolicyUrl = url
|
||||||
|
} else if botInfo.commands.contains(where: { $0.text == "privacy" }) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
privacyPolicyUrl = presentationData.strings.WebApp_PrivacyPolicy_URL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let privacyPolicyUrl {
|
||||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_BotPrivacy, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_BotPrivacy, icon: { theme in
|
||||||
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor)
|
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, f in
|
}, action: { [weak self] _, f in
|
||||||
|
@ -290,6 +290,101 @@ public final class PremiumStarComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateColors(animated: Bool = false) {
|
||||||
|
guard let component = self.component, let colors = component.colors, let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "star", recursively: false) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if animated {
|
||||||
|
UIView.animate(withDuration: 0.25, animations: {
|
||||||
|
node.geometry?.materials.first?.diffuse.contents = generateDiffuseTexture(colors: colors)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
node.geometry?.materials.first?.diffuse.contents = generateDiffuseTexture(colors: colors)
|
||||||
|
}
|
||||||
|
|
||||||
|
let names: [String] = [
|
||||||
|
"particles_left",
|
||||||
|
"particles_right",
|
||||||
|
"particles_left_bottom",
|
||||||
|
"particles_right_bottom",
|
||||||
|
"particles_center"
|
||||||
|
]
|
||||||
|
|
||||||
|
let starNames: [String] = [
|
||||||
|
"coins_left",
|
||||||
|
"coins_right"
|
||||||
|
]
|
||||||
|
|
||||||
|
if let particleColor = component.particleColor {
|
||||||
|
for name in starNames {
|
||||||
|
if let node = scene.rootNode.childNode(withName: name, recursively: false), let particleSystem = node.particleSystems?.first {
|
||||||
|
if animated {
|
||||||
|
particleSystem.warmupDuration = 0.0
|
||||||
|
}
|
||||||
|
particleSystem.particleIntensity = 1.0
|
||||||
|
particleSystem.particleIntensityVariation = 0.05
|
||||||
|
particleSystem.particleColor = particleColor
|
||||||
|
particleSystem.particleColorVariation = SCNVector4Make(0.07, 0.0, 0.1, 0.0)
|
||||||
|
node.isHidden = false
|
||||||
|
|
||||||
|
if let propertyControllers = particleSystem.propertyControllers, let sizeController = propertyControllers[.size], let colorController = propertyControllers[.color] {
|
||||||
|
let animation = CAKeyframeAnimation()
|
||||||
|
if let existing = colorController.animation as? CAKeyframeAnimation {
|
||||||
|
animation.keyTimes = existing.keyTimes
|
||||||
|
animation.values = existing.values?.compactMap { ($0 as? UIColor)?.alpha } ?? []
|
||||||
|
} else {
|
||||||
|
animation.values = [ 0.0, 1.0, 1.0, 0.0 ]
|
||||||
|
}
|
||||||
|
let opacityController = SCNParticlePropertyController(animation: animation)
|
||||||
|
particleSystem.propertyControllers = [
|
||||||
|
.size: sizeController,
|
||||||
|
.opacity: opacityController
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if animated {
|
||||||
|
for name in starNames {
|
||||||
|
if let node = scene.rootNode.childNode(withName: name, recursively: false) {
|
||||||
|
node.isHidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name in names {
|
||||||
|
if let node = scene.rootNode.childNode(withName: name, recursively: false), let particleSystem = node.particleSystems?.first {
|
||||||
|
if let particleColor = component.particleColor {
|
||||||
|
particleSystem.particleIntensity = min(1.0, 2.0 * particleSystem.particleIntensity)
|
||||||
|
particleSystem.particleIntensityVariation = 0.05
|
||||||
|
particleSystem.particleColor = particleColor
|
||||||
|
particleSystem.particleColorVariation = SCNVector4Make(0.1, 0.0, 0.12, 0.0)
|
||||||
|
} else {
|
||||||
|
particleSystem.particleColorVariation = SCNVector4Make(0.12, 0.03, 0.035, 0.0)
|
||||||
|
if animated {
|
||||||
|
particleSystem.particleColor = UIColor(rgb: 0xaa69ea)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let propertyControllers = particleSystem.propertyControllers, let sizeController = propertyControllers[.size], let colorController = propertyControllers[.color] {
|
||||||
|
let animation = CAKeyframeAnimation()
|
||||||
|
if let existing = colorController.animation as? CAKeyframeAnimation {
|
||||||
|
animation.keyTimes = existing.keyTimes
|
||||||
|
animation.values = existing.values?.compactMap { ($0 as? UIColor)?.alpha } ?? []
|
||||||
|
} else {
|
||||||
|
animation.values = [ 0.0, 1.0, 1.0, 0.0 ]
|
||||||
|
}
|
||||||
|
let opacityController = SCNParticlePropertyController(animation: animation)
|
||||||
|
particleSystem.propertyControllers = [
|
||||||
|
.size: sizeController,
|
||||||
|
.opacity: opacityController
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var didSetup = false
|
private var didSetup = false
|
||||||
private func setup() {
|
private func setup() {
|
||||||
guard !self.didSetup, let scene = loadCompressedScene(name: "star2", version: sceneVersion) else {
|
guard !self.didSetup, let scene = loadCompressedScene(name: "star2", version: sceneVersion) else {
|
||||||
@ -300,78 +395,7 @@ public final class PremiumStarComponent: Component {
|
|||||||
self.sceneView.scene = scene
|
self.sceneView.scene = scene
|
||||||
self.sceneView.delegate = self
|
self.sceneView.delegate = self
|
||||||
|
|
||||||
if let component = self.component, let node = scene.rootNode.childNode(withName: "star", recursively: false), let colors =
|
self.updateColors()
|
||||||
component.colors {
|
|
||||||
node.geometry?.materials.first?.diffuse.contents = generateDiffuseTexture(colors: colors)
|
|
||||||
|
|
||||||
let names: [String] = [
|
|
||||||
"particles_left",
|
|
||||||
"particles_right",
|
|
||||||
"particles_left_bottom",
|
|
||||||
"particles_right_bottom",
|
|
||||||
"particles_center"
|
|
||||||
]
|
|
||||||
|
|
||||||
let starNames: [String] = [
|
|
||||||
"coins_left",
|
|
||||||
"coins_right"
|
|
||||||
]
|
|
||||||
|
|
||||||
if let particleColor = component.particleColor {
|
|
||||||
for name in starNames {
|
|
||||||
if let node = scene.rootNode.childNode(withName: name, recursively: false), let particleSystem = node.particleSystems?.first {
|
|
||||||
particleSystem.particleIntensity = 1.0
|
|
||||||
particleSystem.particleIntensityVariation = 0.05
|
|
||||||
particleSystem.particleColor = particleColor
|
|
||||||
particleSystem.particleColorVariation = SCNVector4Make(0.07, 0.0, 0.1, 0.0)
|
|
||||||
node.isHidden = false
|
|
||||||
|
|
||||||
if let propertyControllers = particleSystem.propertyControllers, let sizeController = propertyControllers[.size], let colorController = propertyControllers[.color] {
|
|
||||||
let animation = CAKeyframeAnimation()
|
|
||||||
if let existing = colorController.animation as? CAKeyframeAnimation {
|
|
||||||
animation.keyTimes = existing.keyTimes
|
|
||||||
animation.values = existing.values?.compactMap { ($0 as? UIColor)?.alpha } ?? []
|
|
||||||
} else {
|
|
||||||
animation.values = [ 0.0, 1.0, 1.0, 0.0 ]
|
|
||||||
}
|
|
||||||
let opacityController = SCNParticlePropertyController(animation: animation)
|
|
||||||
particleSystem.propertyControllers = [
|
|
||||||
.size: sizeController,
|
|
||||||
.opacity: opacityController
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for name in names {
|
|
||||||
if let node = scene.rootNode.childNode(withName: name, recursively: false), let particleSystem = node.particleSystems?.first {
|
|
||||||
if let particleColor = component.particleColor {
|
|
||||||
particleSystem.particleIntensity = min(1.0, 2.0 * particleSystem.particleIntensity)
|
|
||||||
particleSystem.particleIntensityVariation = 0.05
|
|
||||||
particleSystem.particleColor = particleColor
|
|
||||||
particleSystem.particleColorVariation = SCNVector4Make(0.1, 0.0, 0.12, 0.0)
|
|
||||||
} else {
|
|
||||||
particleSystem.particleColorVariation = SCNVector4Make(0.12, 0.03, 0.035, 0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let propertyControllers = particleSystem.propertyControllers, let sizeController = propertyControllers[.size], let colorController = propertyControllers[.color] {
|
|
||||||
let animation = CAKeyframeAnimation()
|
|
||||||
if let existing = colorController.animation as? CAKeyframeAnimation {
|
|
||||||
animation.keyTimes = existing.keyTimes
|
|
||||||
animation.values = existing.values?.compactMap { ($0 as? UIColor)?.alpha } ?? []
|
|
||||||
} else {
|
|
||||||
animation.values = [ 0.0, 1.0, 1.0, 0.0 ]
|
|
||||||
}
|
|
||||||
let opacityController = SCNParticlePropertyController(animation: animation)
|
|
||||||
particleSystem.propertyControllers = [
|
|
||||||
.size: sizeController,
|
|
||||||
.opacity: opacityController
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.animateFrom != nil {
|
if self.animateFrom != nil {
|
||||||
let _ = self.sceneView.snapshot()
|
let _ = self.sceneView.snapshot()
|
||||||
@ -515,10 +539,6 @@ public final class PremiumStarComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// if let material = node.geometry?.materials.first {
|
|
||||||
// material.metalness.intensity = 0.4
|
|
||||||
// }
|
|
||||||
|
|
||||||
let animation = CABasicAnimation(keyPath: "contentsTransform")
|
let animation = CABasicAnimation(keyPath: "contentsTransform")
|
||||||
animation.fillMode = .forwards
|
animation.fillMode = .forwards
|
||||||
animation.fromValue = NSValue(scnMatrix4: initial)
|
animation.fromValue = NSValue(scnMatrix4: initial)
|
||||||
@ -536,7 +556,13 @@ public final class PremiumStarComponent: Component {
|
|||||||
node.geometry?.materials.first?.emission.addAnimation(group, forKey: "shimmer")
|
node.geometry?.materials.first?.emission.addAnimation(group, forKey: "shimmer")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func playAppearanceAnimation(velocity: CGFloat? = nil, smallAngle: Bool = false, mirror: Bool = false, explode: Bool = false) {
|
private func playAppearanceAnimation(
|
||||||
|
velocity: CGFloat? = nil,
|
||||||
|
smallAngle: Bool = false,
|
||||||
|
mirror: Bool = false,
|
||||||
|
explode: Bool = false,
|
||||||
|
force: Bool = false
|
||||||
|
) {
|
||||||
guard let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "star", recursively: false) else {
|
guard let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "star", recursively: false) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -621,7 +647,7 @@ public final class PremiumStarComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var from = node.presentation.eulerAngles
|
var from = node.presentation.eulerAngles
|
||||||
if abs(from.y - .pi * 2.0) < 0.001 {
|
if abs(from.y) - .pi * 2.0 < 0.05 {
|
||||||
from.y = 0.0
|
from.y = 0.0
|
||||||
}
|
}
|
||||||
node.removeAnimation(forKey: "tapRotate")
|
node.removeAnimation(forKey: "tapRotate")
|
||||||
@ -633,6 +659,7 @@ public final class PremiumStarComponent: Component {
|
|||||||
if mirror {
|
if mirror {
|
||||||
toValue *= -1
|
toValue *= -1
|
||||||
}
|
}
|
||||||
|
|
||||||
let to = SCNVector3(x: 0.0, y: toValue, z: 0.0)
|
let to = SCNVector3(x: 0.0, y: toValue, z: 0.0)
|
||||||
let distance = rad2deg(to.y - from.y)
|
let distance = rad2deg(to.y - from.y)
|
||||||
|
|
||||||
@ -657,10 +684,16 @@ public final class PremiumStarComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func update(component: PremiumStarComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize {
|
func update(component: PremiumStarComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize {
|
||||||
|
let previousComponent = self.component
|
||||||
self.component = component
|
self.component = component
|
||||||
|
|
||||||
self.setup()
|
self.setup()
|
||||||
|
|
||||||
|
if let previousComponent, component.colors != previousComponent.colors {
|
||||||
|
self.updateColors(animated: true)
|
||||||
|
self.playAppearanceAnimation(velocity: nil, mirror: component.colors?.contains(UIColor(rgb: 0xe57d02)) == true, explode: true, force: true)
|
||||||
|
}
|
||||||
|
|
||||||
if let _ = component.particleColor {
|
if let _ = component.particleColor {
|
||||||
self.sceneView.backgroundColor = component.theme.list.blocksBackgroundColor
|
self.sceneView.backgroundColor = component.theme.list.blocksBackgroundColor
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,7 @@ private final class SheetContent: CombinedComponent {
|
|||||||
let titleString: String
|
let titleString: String
|
||||||
let amountTitle: String
|
let amountTitle: String
|
||||||
let amountPlaceholder: String
|
let amountPlaceholder: String
|
||||||
|
let amountLabel: String?
|
||||||
|
|
||||||
let minAmount: Int64?
|
let minAmount: Int64?
|
||||||
let maxAmount: Int64?
|
let maxAmount: Int64?
|
||||||
@ -124,6 +125,7 @@ private final class SheetContent: CombinedComponent {
|
|||||||
|
|
||||||
minAmount = configuration.minWithdrawAmount
|
minAmount = configuration.minWithdrawAmount
|
||||||
maxAmount = status.balances.availableBalance
|
maxAmount = status.balances.availableBalance
|
||||||
|
amountLabel = nil
|
||||||
case .paidMedia:
|
case .paidMedia:
|
||||||
titleString = environment.strings.Stars_PaidContent_Title
|
titleString = environment.strings.Stars_PaidContent_Title
|
||||||
amountTitle = environment.strings.Stars_PaidContent_AmountTitle
|
amountTitle = environment.strings.Stars_PaidContent_AmountTitle
|
||||||
@ -131,14 +133,22 @@ private final class SheetContent: CombinedComponent {
|
|||||||
|
|
||||||
minAmount = 1
|
minAmount = 1
|
||||||
maxAmount = configuration.maxPaidMediaAmount
|
maxAmount = configuration.maxPaidMediaAmount
|
||||||
|
|
||||||
|
var usdRate = 0.012
|
||||||
|
if let usdWithdrawRate = configuration.usdWithdrawRate, let amount = state.amount, amount > 0 {
|
||||||
|
usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
||||||
|
amountLabel = "≈\(formatTonUsdValue(amount, divide: false, rate: usdRate, dateTimeFormat: environment.dateTimeFormat))"
|
||||||
|
} else {
|
||||||
|
amountLabel = nil
|
||||||
|
}
|
||||||
case .reaction:
|
case .reaction:
|
||||||
titleString = environment.strings.Stars_SendStars_Title
|
titleString = environment.strings.Stars_SendStars_Title
|
||||||
amountTitle = environment.strings.Stars_SendStars_AmountTitle
|
amountTitle = environment.strings.Stars_SendStars_AmountTitle
|
||||||
amountPlaceholder = environment.strings.Stars_SendStars_AmountPlaceholder
|
amountPlaceholder = environment.strings.Stars_SendStars_AmountPlaceholder
|
||||||
|
|
||||||
minAmount = 1
|
minAmount = 1
|
||||||
//TODO:
|
|
||||||
maxAmount = configuration.maxPaidMediaAmount
|
maxAmount = configuration.maxPaidMediaAmount
|
||||||
|
amountLabel = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = title.update(
|
let title = title.update(
|
||||||
@ -264,11 +274,13 @@ private final class SheetContent: CombinedComponent {
|
|||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
AmountFieldComponent(
|
AmountFieldComponent(
|
||||||
textColor: theme.list.itemPrimaryTextColor,
|
textColor: theme.list.itemPrimaryTextColor,
|
||||||
|
secondaryColor: theme.list.itemSecondaryTextColor,
|
||||||
placeholderColor: theme.list.itemPlaceholderTextColor,
|
placeholderColor: theme.list.itemPlaceholderTextColor,
|
||||||
value: state.amount,
|
value: state.amount,
|
||||||
minValue: minAmount,
|
minValue: minAmount,
|
||||||
maxValue: maxAmount,
|
maxValue: maxAmount,
|
||||||
placeholderText: amountPlaceholder,
|
placeholderText: amountPlaceholder,
|
||||||
|
labelText: amountLabel,
|
||||||
amountUpdated: { [weak state] amount in
|
amountUpdated: { [weak state] amount in
|
||||||
state?.amount = amount
|
state?.amount = amount
|
||||||
state?.updated()
|
state?.updated()
|
||||||
@ -576,30 +588,36 @@ private final class AmountFieldComponent: Component {
|
|||||||
typealias EnvironmentType = Empty
|
typealias EnvironmentType = Empty
|
||||||
|
|
||||||
let textColor: UIColor
|
let textColor: UIColor
|
||||||
|
let secondaryColor: UIColor
|
||||||
let placeholderColor: UIColor
|
let placeholderColor: UIColor
|
||||||
let value: Int64?
|
let value: Int64?
|
||||||
let minValue: Int64?
|
let minValue: Int64?
|
||||||
let maxValue: Int64?
|
let maxValue: Int64?
|
||||||
let placeholderText: String
|
let placeholderText: String
|
||||||
|
let labelText: String?
|
||||||
let amountUpdated: (Int64?) -> Void
|
let amountUpdated: (Int64?) -> Void
|
||||||
let tag: AnyObject?
|
let tag: AnyObject?
|
||||||
|
|
||||||
init(
|
init(
|
||||||
textColor: UIColor,
|
textColor: UIColor,
|
||||||
|
secondaryColor: UIColor,
|
||||||
placeholderColor: UIColor,
|
placeholderColor: UIColor,
|
||||||
value: Int64?,
|
value: Int64?,
|
||||||
minValue: Int64?,
|
minValue: Int64?,
|
||||||
maxValue: Int64?,
|
maxValue: Int64?,
|
||||||
placeholderText: String,
|
placeholderText: String,
|
||||||
|
labelText: String?,
|
||||||
amountUpdated: @escaping (Int64?) -> Void,
|
amountUpdated: @escaping (Int64?) -> Void,
|
||||||
tag: AnyObject? = nil
|
tag: AnyObject? = nil
|
||||||
) {
|
) {
|
||||||
self.textColor = textColor
|
self.textColor = textColor
|
||||||
|
self.secondaryColor = secondaryColor
|
||||||
self.placeholderColor = placeholderColor
|
self.placeholderColor = placeholderColor
|
||||||
self.value = value
|
self.value = value
|
||||||
self.minValue = minValue
|
self.minValue = minValue
|
||||||
self.maxValue = maxValue
|
self.maxValue = maxValue
|
||||||
self.placeholderText = placeholderText
|
self.placeholderText = placeholderText
|
||||||
|
self.labelText = labelText
|
||||||
self.amountUpdated = amountUpdated
|
self.amountUpdated = amountUpdated
|
||||||
self.tag = tag
|
self.tag = tag
|
||||||
}
|
}
|
||||||
@ -608,6 +626,9 @@ private final class AmountFieldComponent: Component {
|
|||||||
if lhs.textColor != rhs.textColor {
|
if lhs.textColor != rhs.textColor {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.secondaryColor != rhs.secondaryColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.placeholderColor != rhs.placeholderColor {
|
if lhs.placeholderColor != rhs.placeholderColor {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -623,6 +644,9 @@ private final class AmountFieldComponent: Component {
|
|||||||
if lhs.placeholderText != rhs.placeholderText {
|
if lhs.placeholderText != rhs.placeholderText {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.labelText != rhs.labelText {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -640,6 +664,7 @@ private final class AmountFieldComponent: Component {
|
|||||||
private let placeholderView: ComponentView<Empty>
|
private let placeholderView: ComponentView<Empty>
|
||||||
private let iconView: UIImageView
|
private let iconView: UIImageView
|
||||||
private let textField: TextFieldNodeView
|
private let textField: TextFieldNodeView
|
||||||
|
private let labelView: ComponentView<Empty>
|
||||||
|
|
||||||
private var component: AmountFieldComponent?
|
private var component: AmountFieldComponent?
|
||||||
private weak var state: EmptyComponentState?
|
private weak var state: EmptyComponentState?
|
||||||
@ -647,6 +672,7 @@ private final class AmountFieldComponent: Component {
|
|||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
self.placeholderView = ComponentView<Empty>()
|
self.placeholderView = ComponentView<Empty>()
|
||||||
self.textField = TextFieldNodeView(frame: .zero)
|
self.textField = TextFieldNodeView(frame: .zero)
|
||||||
|
self.labelView = ComponentView<Empty>()
|
||||||
|
|
||||||
self.iconView = UIImageView(image: UIImage(bundleImageName: "Premium/Stars/StarLarge"))
|
self.iconView = UIImageView(image: UIImage(bundleImageName: "Premium/Stars/StarLarge"))
|
||||||
|
|
||||||
@ -740,6 +766,7 @@ private final class AmountFieldComponent: Component {
|
|||||||
|
|
||||||
let size = CGSize(width: availableSize.width, height: 44.0)
|
let size = CGSize(width: availableSize.width, height: 44.0)
|
||||||
|
|
||||||
|
let sideInset: CGFloat = 15.0
|
||||||
var leftInset: CGFloat = 15.0
|
var leftInset: CGFloat = 15.0
|
||||||
if let icon = self.iconView.image {
|
if let icon = self.iconView.image {
|
||||||
leftInset += icon.size.width + 6.0
|
leftInset += icon.size.width + 6.0
|
||||||
@ -765,10 +792,34 @@ private final class AmountFieldComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
placeholderComponentView.frame = CGRect(origin: CGPoint(x: leftInset, y: floorToScreenPixels((size.height - placeholderSize.height) / 2.0) + 1.0 - UIScreenPixel), size: placeholderSize)
|
placeholderComponentView.frame = CGRect(origin: CGPoint(x: leftInset, y: floorToScreenPixels((size.height - placeholderSize.height) / 2.0) + 1.0 - UIScreenPixel), size: placeholderSize)
|
||||||
|
|
||||||
placeholderComponentView.isHidden = !(self.textField.text ?? "").isEmpty
|
placeholderComponentView.isHidden = !(self.textField.text ?? "").isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let labelText = component.labelText {
|
||||||
|
let labelSize = self.labelView.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(
|
||||||
|
Text(
|
||||||
|
text: labelText,
|
||||||
|
font: Font.regular(17.0),
|
||||||
|
color: component.secondaryColor
|
||||||
|
)
|
||||||
|
),
|
||||||
|
environment: {},
|
||||||
|
containerSize: availableSize
|
||||||
|
)
|
||||||
|
|
||||||
|
if let labelView = self.labelView.view {
|
||||||
|
if labelView.superview == nil {
|
||||||
|
self.insertSubview(labelView, at: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
labelView.frame = CGRect(origin: CGPoint(x: size.width - sideInset - labelSize.width, y: floorToScreenPixels((size.height - labelSize.height) / 2.0) + 1.0 - UIScreenPixel), size: labelSize)
|
||||||
|
}
|
||||||
|
} else if let labelView = self.labelView.view, labelView.superview != nil {
|
||||||
|
labelView.removeFromSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
self.textField.frame = CGRect(x: leftInset, y: 0.0, width: size.width - 30.0, height: 44.0)
|
self.textField.frame = CGRect(x: leftInset, y: 0.0, width: size.width - 30.0, height: 44.0)
|
||||||
|
|
||||||
return size
|
return size
|
||||||
@ -808,15 +859,17 @@ func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor
|
|||||||
|
|
||||||
private struct StarsWithdrawConfiguration {
|
private struct StarsWithdrawConfiguration {
|
||||||
static var defaultValue: StarsWithdrawConfiguration {
|
static var defaultValue: StarsWithdrawConfiguration {
|
||||||
return StarsWithdrawConfiguration(minWithdrawAmount: nil, maxPaidMediaAmount: nil)
|
return StarsWithdrawConfiguration(minWithdrawAmount: nil, maxPaidMediaAmount: nil, usdWithdrawRate: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
let minWithdrawAmount: Int64?
|
let minWithdrawAmount: Int64?
|
||||||
let maxPaidMediaAmount: Int64?
|
let maxPaidMediaAmount: Int64?
|
||||||
|
let usdWithdrawRate: Double?
|
||||||
|
|
||||||
fileprivate init(minWithdrawAmount: Int64?, maxPaidMediaAmount: Int64?) {
|
fileprivate init(minWithdrawAmount: Int64?, maxPaidMediaAmount: Int64?, usdWithdrawRate: Double?) {
|
||||||
self.minWithdrawAmount = minWithdrawAmount
|
self.minWithdrawAmount = minWithdrawAmount
|
||||||
self.maxPaidMediaAmount = maxPaidMediaAmount
|
self.maxPaidMediaAmount = maxPaidMediaAmount
|
||||||
|
self.usdWithdrawRate = usdWithdrawRate
|
||||||
}
|
}
|
||||||
|
|
||||||
static func with(appConfiguration: AppConfiguration) -> StarsWithdrawConfiguration {
|
static func with(appConfiguration: AppConfiguration) -> StarsWithdrawConfiguration {
|
||||||
@ -829,8 +882,12 @@ private struct StarsWithdrawConfiguration {
|
|||||||
if let value = data["stars_paid_post_amount_max"] as? Double {
|
if let value = data["stars_paid_post_amount_max"] as? Double {
|
||||||
maxPaidMediaAmount = Int64(value)
|
maxPaidMediaAmount = Int64(value)
|
||||||
}
|
}
|
||||||
|
var usdWithdrawRate: Double?
|
||||||
|
if let value = data["stars_usd_withdraw_rate_x1000"] as? Double {
|
||||||
|
usdWithdrawRate = value
|
||||||
|
}
|
||||||
|
|
||||||
return StarsWithdrawConfiguration(minWithdrawAmount: minWithdrawAmount, maxPaidMediaAmount: maxPaidMediaAmount)
|
return StarsWithdrawConfiguration(minWithdrawAmount: minWithdrawAmount, maxPaidMediaAmount: maxPaidMediaAmount, usdWithdrawRate: usdWithdrawRate)
|
||||||
} else {
|
} else {
|
||||||
return .defaultValue
|
return .defaultValue
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,16 @@
|
|||||||
try {
|
try {
|
||||||
const docStr = new XMLSerializer().serializeToString(document);
|
const docStr = new XMLSerializer().serializeToString(document);
|
||||||
|
|
||||||
const clean = DOMPurify.sanitize(docStr, {WHOLE_DOCUMENT: true});
|
const clean = DOMPurify.sanitize(docStr, {WHOLE_DOCUMENT: true, ADD_TAGS: ["iframe"]});
|
||||||
const cleanDoc = new DOMParser().parseFromString(clean, "text/html");
|
const cleanDoc = new DOMParser().parseFromString(clean, "text/html");
|
||||||
|
|
||||||
const readability = new Readability(cleanDoc)
|
const readability = new Readability(cleanDoc)
|
||||||
const result = readability.parse()
|
const result = readability.parse()
|
||||||
|
|
||||||
|
if (result.length && result.length < 1000) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const doc = new DOMParser().parseFromString(result.content, 'text/html').body
|
const doc = new DOMParser().parseFromString(result.content, 'text/html').body
|
||||||
|
|
||||||
const parse = e => [...(e.childNodes || [])].map(node => {
|
const parse = e => [...(e.childNodes || [])].map(node => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user