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.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 preloadedResouces: [Any]?
|
||||
private var originalContent: BrowserContent?
|
||||
private let url: String
|
||||
|
||||
private var webPage: TelegramMediaWebpage?
|
||||
|
||||
@ -102,6 +103,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
self.sourceLocation = sourceLocation
|
||||
self.preloadedResouces = preloadedResouces
|
||||
self.originalContent = originalContent
|
||||
self.url = url
|
||||
|
||||
self.uuid = UUID()
|
||||
|
||||
@ -904,6 +906,12 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
anchor = String(baseUrl[anchorRange.upperBound...]).removingPercentEncoding
|
||||
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 {
|
||||
self.scrollToAnchor(anchor)
|
||||
@ -914,7 +922,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
self.loadProgress.set(0.02)
|
||||
|
||||
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
|
||||
if let strongSelf = self {
|
||||
strongSelf.loadProgress.set(0.07)
|
||||
@ -997,8 +1005,15 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
}
|
||||
|
||||
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 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 {
|
||||
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:
|
||||
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 actionSheet = ActionSheetController(instantPageTheme: self.theme)
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: url.url),
|
||||
ActionSheetTextItem(title: baseUrl),
|
||||
ActionSheetButtonItem(title: openText, color: .accent, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
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
|
||||
actionSheet?.dismissAnimated()
|
||||
UIPasteboard.general.string = url.url
|
||||
UIPasteboard.general.string = baseUrl
|
||||
}),
|
||||
ActionSheetButtonItem(title: self.presentationData.strings.Conversation_AddToReadingList, color: .accent, action: { [weak actionSheet] in
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
@ -211,6 +211,7 @@ private func parseRichText(_ input: [Any], _ media: inout [MediaId: Media]) -> R
|
||||
result.append(parseRichText(string))
|
||||
} else if let item = item as? [String: Any], let tag = item["tag"] as? String {
|
||||
var text: RichText?
|
||||
var addLineBreak = false
|
||||
switch tag {
|
||||
case "b", "strong":
|
||||
text = .bold(parseRichText(item, &media))
|
||||
@ -273,6 +274,9 @@ private func parseRichText(_ input: [Any], _ media: inout [MediaId: Media]) -> R
|
||||
flags: []
|
||||
)
|
||||
text = .image(id: id, dimensions: PixelDimensions(width: width, height: height))
|
||||
if width > 100 {
|
||||
addLineBreak = true
|
||||
}
|
||||
}
|
||||
case "br":
|
||||
if let last = result.last {
|
||||
@ -284,6 +288,9 @@ private func parseRichText(_ input: [Any], _ media: inout [MediaId: Media]) -> R
|
||||
if var text {
|
||||
text = applyAnchor(text, item: item)
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
@ -341,8 +463,10 @@ private func addNewLine(_ input: RichText) -> RichText {
|
||||
case let .anchor(richText, name):
|
||||
text = .anchor(text: addNewLine(richText), name: name)
|
||||
case var .concat(array):
|
||||
array[array.count - 1] = addNewLine(array[array.count - 1])
|
||||
text = .concat(array)
|
||||
if !array.isEmpty {
|
||||
array[array.count - 1] = addNewLine(array[array.count - 1])
|
||||
text = .concat(array)
|
||||
}
|
||||
case .image:
|
||||
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 {
|
||||
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 {
|
||||
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"
|
||||
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)
|
||||
}
|
||||
|
||||
@ -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? {
|
||||
guard let content = input["content"] as? [Any] else {
|
||||
return nil
|
||||
@ -519,9 +704,19 @@ private func parseFigure(_ input: [String: Any], _ media: inout [MediaId: Media]
|
||||
var caption: RichText?
|
||||
for item in content {
|
||||
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)
|
||||
} else if tag == "figurecaption" {
|
||||
} else if tag == "figcaption" {
|
||||
caption = trim(parseRichText(item, &media))
|
||||
}
|
||||
}
|
||||
@ -539,7 +734,7 @@ private func parsePageBlocks(_ input: [Any], _ url: String, _ media: inout [Medi
|
||||
var result: [InstantPageBlock] = []
|
||||
for item in input {
|
||||
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 {
|
||||
let content = item["content"] as? [Any]
|
||||
switch tag {
|
||||
@ -557,7 +752,10 @@ private func parsePageBlocks(_ input: [Any], _ url: String, _ media: inout [Medi
|
||||
if let image = parseImage(item, &media) {
|
||||
result.append(image)
|
||||
}
|
||||
break
|
||||
case "iframe":
|
||||
if let video = parseVideo(item, &media) {
|
||||
result.append(video)
|
||||
}
|
||||
case "figure":
|
||||
if let figure = parseFigure(item, &media) {
|
||||
result.append(figure)
|
||||
@ -565,7 +763,7 @@ private func parsePageBlocks(_ input: [Any], _ url: String, _ media: inout [Medi
|
||||
case "table":
|
||||
result.append(parseTable(item, &media))
|
||||
case "ul", "ol":
|
||||
if let list = parseList(item, &media) {
|
||||
if let list = parseList(item, url, &media) {
|
||||
result.append(list)
|
||||
}
|
||||
case "hr":
|
||||
|
@ -753,7 +753,7 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo
|
||||
let runRange = NSMakeRange(cfRunRange.location == kCFNotFound ? NSNotFound : cfRunRange.location, cfRunRange.length)
|
||||
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 {
|
||||
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 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)
|
||||
case let .prepaid(_, title, subtitle, prepaidGiveaway):
|
||||
let color: GiftOptionItem.Icon.Color
|
||||
switch prepaidGiveaway.months {
|
||||
case 3:
|
||||
color = .green
|
||||
case 6:
|
||||
color = .blue
|
||||
case 12:
|
||||
color = .red
|
||||
default:
|
||||
color = .blue
|
||||
switch prepaidGiveaway.prize {
|
||||
case let .premium(months):
|
||||
switch months {
|
||||
case 3:
|
||||
color = .green
|
||||
case 6:
|
||||
color = .blue
|
||||
case 12:
|
||||
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)
|
||||
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))
|
||||
case let .prepaid(prepaidGiveaway):
|
||||
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 {
|
||||
@ -988,10 +1010,20 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
||||
let actionsDisposable = DisposableSet()
|
||||
|
||||
let initialSubscriptions: Int32
|
||||
let initialStars: Int64
|
||||
let initialWinners: Int32
|
||||
if case let .prepaid(prepaidGiveaway) = subject {
|
||||
if case let .stars(stars) = prepaidGiveaway.prize {
|
||||
initialStars = stars
|
||||
} else {
|
||||
initialStars = 500
|
||||
}
|
||||
initialSubscriptions = prepaidGiveaway.quantity
|
||||
initialWinners = prepaidGiveaway.quantity
|
||||
} else {
|
||||
initialSubscriptions = 5
|
||||
initialStars = 500
|
||||
initialWinners = 5
|
||||
}
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
|
||||
@ -1009,7 +1041,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
||||
let minDate = currentTime + 60 * 1
|
||||
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 stateValue = Atomic(value: initialState)
|
||||
|
@ -29,7 +29,7 @@ final class CreateGiveawayHeaderItem: ItemListControllerHeaderItem {
|
||||
|
||||
func isEqual(to: ItemListControllerHeaderItem) -> Bool {
|
||||
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 {
|
||||
return false
|
||||
}
|
||||
@ -198,16 +198,32 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
|
||||
|
||||
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(
|
||||
theme: self.item.theme,
|
||||
isIntro: true,
|
||||
isVisible: true,
|
||||
hasIdleAnimations: true,
|
||||
colors: [
|
||||
UIColor(rgb: 0x6a94ff),
|
||||
UIColor(rgb: 0x9472fd),
|
||||
UIColor(rgb: 0xe26bd3)
|
||||
]
|
||||
colors: colors,
|
||||
particleColor: particleColor
|
||||
))
|
||||
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):
|
||||
let color: GiftOptionItem.Icon.Color
|
||||
switch prepaidGiveaway.months {
|
||||
case 3:
|
||||
color = .green
|
||||
case 6:
|
||||
color = .blue
|
||||
case 12:
|
||||
color = .red
|
||||
default:
|
||||
color = .blue
|
||||
switch prepaidGiveaway.prize {
|
||||
case let .premium(months):
|
||||
switch months {
|
||||
case 3:
|
||||
color = .green
|
||||
case 6:
|
||||
color = .blue
|
||||
case 12:
|
||||
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: {
|
||||
arguments.createPrepaidGiveaway(prepaidGiveaway)
|
||||
@ -1423,7 +1434,17 @@ private func boostsEntries(
|
||||
entries.append(.boostPrepaidTitle(presentationData.theme, presentationData.strings.Stats_Boosts_PrepaidGiveawaysTitle))
|
||||
var i: Int32 = 0
|
||||
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
|
||||
}
|
||||
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[-1132882121] = { return Api.Bool.parse_boolFalse($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[1571189943] = { return Api.BotApp.parse_botAppNotModified($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[1171632161] = { return Api.MessageAction.parse_messageActionGiftStars($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[-1281329567] = { return Api.MessageAction.parse_messageActionGroupCallScheduled($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[1596792306] = { return Api.PremiumSubscriptionOption.parse_premiumSubscriptionOption($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[1124062251] = { return Api.PrivacyKey.parse_privacyKeyAddedByPhone($0) }
|
||||
dict[536913176] = { return Api.PrivacyKey.parse_privacyKeyBirthday($0) }
|
||||
|
@ -962,13 +962,13 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
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) {
|
||||
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 {
|
||||
buffer.appendInt32(706514033)
|
||||
buffer.appendInt32(1262359766)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(id, buffer: buffer, boxed: false)
|
||||
@ -978,14 +978,15 @@ public extension Api {
|
||||
serializeInt32(expires, 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 << 6) != 0 {serializeInt64(stars!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .boost(let flags, let id, let userId, let giveawayMsgId, let date, let expires, let usedGiftSlug, let multiplier):
|
||||
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)])
|
||||
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), ("stars", stars as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -1006,6 +1007,8 @@ public extension Api {
|
||||
if Int(_1!) & Int(1 << 4) != 0 {_7 = parseString(reader) }
|
||||
var _8: Int32?
|
||||
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 _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
@ -1014,8 +1017,9 @@ public extension Api {
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 5) == 0) || _8 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
|
||||
return Api.Boost.boost(flags: _1!, id: _2!, userId: _3, giveawayMsgId: _4, date: _5!, expires: _6!, usedGiftSlug: _7, multiplier: _8)
|
||||
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil
|
||||
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 {
|
||||
return nil
|
||||
|
@ -990,7 +990,7 @@ public extension Api {
|
||||
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 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 messageActionGroupCallScheduled(call: Api.InputGroupCall, scheduleDate: Int32)
|
||||
case messageActionHistoryClear
|
||||
@ -1183,10 +1183,11 @@ public extension Api {
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(stars!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .messageActionGiveawayResults(let winnersCount, let unclaimedCount):
|
||||
case .messageActionGiveawayResults(let flags, let winnersCount, let unclaimedCount):
|
||||
if boxed {
|
||||
buffer.appendInt32(715107781)
|
||||
buffer.appendInt32(-2015170219)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(winnersCount, buffer: buffer, boxed: false)
|
||||
serializeInt32(unclaimedCount, buffer: buffer, boxed: false)
|
||||
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)])
|
||||
case .messageActionGiveawayLaunch(let flags, let stars):
|
||||
return ("messageActionGiveawayLaunch", [("flags", flags as Any), ("stars", stars as Any)])
|
||||
case .messageActionGiveawayResults(let winnersCount, let unclaimedCount):
|
||||
return ("messageActionGiveawayResults", [("winnersCount", winnersCount as Any), ("unclaimedCount", unclaimedCount as Any)])
|
||||
case .messageActionGiveawayResults(let flags, let winnersCount, let unclaimedCount):
|
||||
return ("messageActionGiveawayResults", [("flags", flags as Any), ("winnersCount", winnersCount as Any), ("unclaimedCount", unclaimedCount as Any)])
|
||||
case .messageActionGroupCall(let flags, let call, let duration):
|
||||
return ("messageActionGroupCall", [("flags", flags as Any), ("call", call as Any), ("duration", duration as Any)])
|
||||
case .messageActionGroupCallScheduled(let call, let scheduleDate):
|
||||
@ -1794,10 +1795,13 @@ public extension Api {
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.MessageAction.messageActionGiveawayResults(winnersCount: _1!, unclaimedCount: _2!)
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.MessageAction.messageActionGiveawayResults(flags: _1!, winnersCount: _2!, unclaimedCount: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -1013,6 +1013,7 @@ public extension Api {
|
||||
public extension Api {
|
||||
enum PrepaidGiveaway: TypeConstructorDescription {
|
||||
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) {
|
||||
switch self {
|
||||
@ -1025,6 +1026,15 @@ public extension Api {
|
||||
serializeInt32(quantity, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
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 {
|
||||
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)])
|
||||
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
|
||||
}
|
||||
}
|
||||
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))
|
||||
case let .messageActionGiveawayLaunch(_, stars):
|
||||
return TelegramMediaAction(action: .giveawayLaunched(stars: stars))
|
||||
case let .messageActionGiveawayResults(winners, unclaimed):
|
||||
return TelegramMediaAction(action: .giveawayResults(winners: winners, unclaimed: unclaimed))
|
||||
case let .messageActionGiveawayResults(flags, winners, unclaimed):
|
||||
return TelegramMediaAction(action: .giveawayResults(winners: winners, unclaimed: unclaimed, stars: (flags & (1 << 0)) != 0))
|
||||
case let .messageActionBoostApply(boosts):
|
||||
return TelegramMediaAction(action: .boostsApplied(boosts: boosts))
|
||||
case let .messageActionPaymentRefunded(_, peer, currency, totalAmount, payload, charge):
|
||||
|
@ -279,7 +279,7 @@ private final class ChannelBoostersContextImpl {
|
||||
var resultBoosts: [ChannelBoostersContext.State.Boost] = []
|
||||
for boost in boosts {
|
||||
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 boostPeer: EnginePeer?
|
||||
if let userId = userId {
|
||||
@ -297,7 +297,7 @@ private final class ChannelBoostersContextImpl {
|
||||
if (flags & (1 << 3)) != 0 {
|
||||
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 {
|
||||
@ -388,6 +388,7 @@ public final class ChannelBoostersContext {
|
||||
public var date: Int32
|
||||
public var expires: Int32
|
||||
public var multiplier: Int32
|
||||
public var stars: Int64?
|
||||
public var slug: String?
|
||||
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 giveawayLaunched(stars: Int64?)
|
||||
case joinedChannel
|
||||
case giveawayResults(winners: Int32, unclaimed: Int32)
|
||||
case giveawayResults(winners: Int32, unclaimed: Int32, stars: Bool)
|
||||
case boostsApplied(boosts: Int32)
|
||||
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?)
|
||||
@ -235,7 +235,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
case 38:
|
||||
self = .joinedChannel
|
||||
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:
|
||||
self = .boostsApplied(boosts: decoder.decodeInt32ForKey("boosts", orElse: 0))
|
||||
case 41:
|
||||
@ -470,10 +470,11 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
}
|
||||
case .joinedChannel:
|
||||
encoder.encodeInt32(38, forKey: "_rawValue")
|
||||
case let .giveawayResults(winners, unclaimed):
|
||||
case let .giveawayResults(winners, unclaimed, stars):
|
||||
encoder.encodeInt32(39, forKey: "_rawValue")
|
||||
encoder.encodeInt32(winners, forKey: "winners")
|
||||
encoder.encodeInt32(unclaimed, forKey: "unclaimed")
|
||||
encoder.encodeBool(stars, forKey: "stars")
|
||||
case let .boostsApplied(boosts):
|
||||
encoder.encodeInt32(40, forKey: "_rawValue")
|
||||
encoder.encodeInt32(boosts, forKey: "boosts")
|
||||
|
@ -89,8 +89,13 @@ public enum PremiumGiveawayInfo: Equatable {
|
||||
}
|
||||
|
||||
public struct PrepaidGiveaway: Equatable {
|
||||
public enum Prize: Equatable {
|
||||
case premium(months: Int32)
|
||||
case stars(stars: Int64)
|
||||
}
|
||||
|
||||
public let id: Int64
|
||||
public let months: Int32
|
||||
public let prize: Prize
|
||||
public let quantity: Int32
|
||||
public let date: Int32
|
||||
}
|
||||
@ -304,7 +309,12 @@ extension PrepaidGiveaway {
|
||||
switch apiPrepaidGiveaway {
|
||||
case let .prepaidGiveaway(id, months, quantity, date):
|
||||
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.date = date
|
||||
}
|
||||
|
@ -981,7 +981,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
|
||||
case .joinedChannel:
|
||||
attributedString = NSAttributedString(string: strings.Notification_ChannelJoinedByYou, font: titleBoldFont, textColor: primaryTextColor)
|
||||
case let .giveawayResults(winners, unclaimed):
|
||||
case let .giveawayResults(winners, unclaimed, _):
|
||||
if winners == 0 {
|
||||
attributedString = parseMarkdownIntoAttributedString(strings.Notification_GiveawayResultsNoWinners(unclaimed), attributes: MarkdownAttributes(body: bodyAttributes, bold: boldAttributes, link: bodyAttributes, linkAttribute: { _ in return nil }))
|
||||
} 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))
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
@ -5967,7 +5967,17 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
}
|
||||
|
||||
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
|
||||
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor)
|
||||
}, 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 func setup() {
|
||||
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.delegate = self
|
||||
|
||||
if let component = self.component, let node = scene.rootNode.childNode(withName: "star", recursively: false), let colors =
|
||||
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
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.updateColors()
|
||||
|
||||
if self.animateFrom != nil {
|
||||
let _ = self.sceneView.snapshot()
|
||||
@ -515,10 +539,6 @@ public final class PremiumStarComponent: Component {
|
||||
return
|
||||
}
|
||||
|
||||
// if let material = node.geometry?.materials.first {
|
||||
// material.metalness.intensity = 0.4
|
||||
// }
|
||||
|
||||
let animation = CABasicAnimation(keyPath: "contentsTransform")
|
||||
animation.fillMode = .forwards
|
||||
animation.fromValue = NSValue(scnMatrix4: initial)
|
||||
@ -536,7 +556,13 @@ public final class PremiumStarComponent: Component {
|
||||
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 {
|
||||
return
|
||||
}
|
||||
@ -621,7 +647,7 @@ public final class PremiumStarComponent: Component {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
node.removeAnimation(forKey: "tapRotate")
|
||||
@ -633,6 +659,7 @@ public final class PremiumStarComponent: Component {
|
||||
if mirror {
|
||||
toValue *= -1
|
||||
}
|
||||
|
||||
let to = SCNVector3(x: 0.0, y: toValue, z: 0.0)
|
||||
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 {
|
||||
let previousComponent = self.component
|
||||
self.component = component
|
||||
|
||||
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 {
|
||||
self.sceneView.backgroundColor = component.theme.list.blocksBackgroundColor
|
||||
}
|
||||
|
@ -110,6 +110,7 @@ private final class SheetContent: CombinedComponent {
|
||||
let titleString: String
|
||||
let amountTitle: String
|
||||
let amountPlaceholder: String
|
||||
let amountLabel: String?
|
||||
|
||||
let minAmount: Int64?
|
||||
let maxAmount: Int64?
|
||||
@ -124,6 +125,7 @@ private final class SheetContent: CombinedComponent {
|
||||
|
||||
minAmount = configuration.minWithdrawAmount
|
||||
maxAmount = status.balances.availableBalance
|
||||
amountLabel = nil
|
||||
case .paidMedia:
|
||||
titleString = environment.strings.Stars_PaidContent_Title
|
||||
amountTitle = environment.strings.Stars_PaidContent_AmountTitle
|
||||
@ -131,14 +133,22 @@ private final class SheetContent: CombinedComponent {
|
||||
|
||||
minAmount = 1
|
||||
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:
|
||||
titleString = environment.strings.Stars_SendStars_Title
|
||||
amountTitle = environment.strings.Stars_SendStars_AmountTitle
|
||||
amountPlaceholder = environment.strings.Stars_SendStars_AmountPlaceholder
|
||||
|
||||
minAmount = 1
|
||||
//TODO:
|
||||
maxAmount = configuration.maxPaidMediaAmount
|
||||
amountLabel = nil
|
||||
}
|
||||
|
||||
let title = title.update(
|
||||
@ -264,11 +274,13 @@ private final class SheetContent: CombinedComponent {
|
||||
component: AnyComponent(
|
||||
AmountFieldComponent(
|
||||
textColor: theme.list.itemPrimaryTextColor,
|
||||
secondaryColor: theme.list.itemSecondaryTextColor,
|
||||
placeholderColor: theme.list.itemPlaceholderTextColor,
|
||||
value: state.amount,
|
||||
minValue: minAmount,
|
||||
maxValue: maxAmount,
|
||||
placeholderText: amountPlaceholder,
|
||||
labelText: amountLabel,
|
||||
amountUpdated: { [weak state] amount in
|
||||
state?.amount = amount
|
||||
state?.updated()
|
||||
@ -576,30 +588,36 @@ private final class AmountFieldComponent: Component {
|
||||
typealias EnvironmentType = Empty
|
||||
|
||||
let textColor: UIColor
|
||||
let secondaryColor: UIColor
|
||||
let placeholderColor: UIColor
|
||||
let value: Int64?
|
||||
let minValue: Int64?
|
||||
let maxValue: Int64?
|
||||
let placeholderText: String
|
||||
let labelText: String?
|
||||
let amountUpdated: (Int64?) -> Void
|
||||
let tag: AnyObject?
|
||||
|
||||
init(
|
||||
textColor: UIColor,
|
||||
secondaryColor: UIColor,
|
||||
placeholderColor: UIColor,
|
||||
value: Int64?,
|
||||
minValue: Int64?,
|
||||
maxValue: Int64?,
|
||||
placeholderText: String,
|
||||
labelText: String?,
|
||||
amountUpdated: @escaping (Int64?) -> Void,
|
||||
tag: AnyObject? = nil
|
||||
) {
|
||||
self.textColor = textColor
|
||||
self.secondaryColor = secondaryColor
|
||||
self.placeholderColor = placeholderColor
|
||||
self.value = value
|
||||
self.minValue = minValue
|
||||
self.maxValue = maxValue
|
||||
self.placeholderText = placeholderText
|
||||
self.labelText = labelText
|
||||
self.amountUpdated = amountUpdated
|
||||
self.tag = tag
|
||||
}
|
||||
@ -608,6 +626,9 @@ private final class AmountFieldComponent: Component {
|
||||
if lhs.textColor != rhs.textColor {
|
||||
return false
|
||||
}
|
||||
if lhs.secondaryColor != rhs.secondaryColor {
|
||||
return false
|
||||
}
|
||||
if lhs.placeholderColor != rhs.placeholderColor {
|
||||
return false
|
||||
}
|
||||
@ -623,6 +644,9 @@ private final class AmountFieldComponent: Component {
|
||||
if lhs.placeholderText != rhs.placeholderText {
|
||||
return false
|
||||
}
|
||||
if lhs.labelText != rhs.labelText {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -640,6 +664,7 @@ private final class AmountFieldComponent: Component {
|
||||
private let placeholderView: ComponentView<Empty>
|
||||
private let iconView: UIImageView
|
||||
private let textField: TextFieldNodeView
|
||||
private let labelView: ComponentView<Empty>
|
||||
|
||||
private var component: AmountFieldComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
@ -647,6 +672,7 @@ private final class AmountFieldComponent: Component {
|
||||
override init(frame: CGRect) {
|
||||
self.placeholderView = ComponentView<Empty>()
|
||||
self.textField = TextFieldNodeView(frame: .zero)
|
||||
self.labelView = ComponentView<Empty>()
|
||||
|
||||
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 sideInset: CGFloat = 15.0
|
||||
var leftInset: CGFloat = 15.0
|
||||
if let icon = self.iconView.image {
|
||||
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.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)
|
||||
|
||||
return size
|
||||
@ -808,15 +859,17 @@ func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor
|
||||
|
||||
private struct StarsWithdrawConfiguration {
|
||||
static var defaultValue: StarsWithdrawConfiguration {
|
||||
return StarsWithdrawConfiguration(minWithdrawAmount: nil, maxPaidMediaAmount: nil)
|
||||
return StarsWithdrawConfiguration(minWithdrawAmount: nil, maxPaidMediaAmount: nil, usdWithdrawRate: nil)
|
||||
}
|
||||
|
||||
let minWithdrawAmount: 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.maxPaidMediaAmount = maxPaidMediaAmount
|
||||
self.usdWithdrawRate = usdWithdrawRate
|
||||
}
|
||||
|
||||
static func with(appConfiguration: AppConfiguration) -> StarsWithdrawConfiguration {
|
||||
@ -829,8 +882,12 @@ private struct StarsWithdrawConfiguration {
|
||||
if let value = data["stars_paid_post_amount_max"] as? Double {
|
||||
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 {
|
||||
return .defaultValue
|
||||
}
|
||||
|
@ -7,12 +7,16 @@
|
||||
try {
|
||||
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 readability = new Readability(cleanDoc)
|
||||
const result = readability.parse()
|
||||
|
||||
|
||||
if (result.length && result.length < 1000) {
|
||||
return null
|
||||
}
|
||||
|
||||
const doc = new DOMParser().parseFromString(result.content, 'text/html').body
|
||||
|
||||
const parse = e => [...(e.childNodes || [])].map(node => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user