mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-08 08:31:13 +00:00
Added support for inline anchors in Instant View
This commit is contained in:
parent
4d2c2a022d
commit
cc55b0ed93
@ -21,7 +21,7 @@ final class AuthorizationSequenceSplashController: ViewController {
|
|||||||
|
|
||||||
var nextPressed: ((PresentationStrings?) -> Void)?
|
var nextPressed: ((PresentationStrings?) -> Void)?
|
||||||
|
|
||||||
private let suggestedLocalization = Promise<SuggestedLocalizationInfo?>(nil)
|
private let suggestedLocalization = Promise<SuggestedLocalizationInfo?>()
|
||||||
private let activateLocalizationDisposable = MetaDisposable()
|
private let activateLocalizationDisposable = MetaDisposable()
|
||||||
|
|
||||||
init(postbox: Postbox, network: Network, theme: AuthorizationTheme) {
|
init(postbox: Postbox, network: Network, theme: AuthorizationTheme) {
|
||||||
@ -29,7 +29,8 @@ final class AuthorizationSequenceSplashController: ViewController {
|
|||||||
self.network = network
|
self.network = network
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
|
||||||
self.suggestedLocalization.set(currentlySuggestedLocalization(network: network, extractKeys: ["Login.ContinueWithLocalization"]))
|
self.suggestedLocalization.set(.single(nil)
|
||||||
|
|> then(currentlySuggestedLocalization(network: network, extractKeys: ["Login.ContinueWithLocalization"])))
|
||||||
let suggestedLocalization = self.suggestedLocalization
|
let suggestedLocalization = self.suggestedLocalization
|
||||||
|
|
||||||
let localizationSignal = SSignal(generator: { subscriber in
|
let localizationSignal = SSignal(generator: { subscriber in
|
||||||
|
@ -893,11 +893,16 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func findAnchorItem(_ anchor: String, items: [InstantPageItem]) -> InstantPageAnchorItem? {
|
private func findAnchorItem(_ anchor: String, items: [InstantPageItem]) -> (InstantPageItem, CGFloat)? {
|
||||||
for item in items {
|
for item in items {
|
||||||
if let item = item as? InstantPageAnchorItem, item.anchor == anchor {
|
if let item = item as? InstantPageAnchorItem, item.anchor == anchor {
|
||||||
return item
|
return (item, 0.0)
|
||||||
} else if let item = item as? InstantPageDetailsItem {
|
} else if let item = item as? InstantPageTextItem {
|
||||||
|
if let lineIndex = item.anchors[anchor] {
|
||||||
|
return (item, item.lines[lineIndex].frame.minY - 10.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if let item = item as? InstantPageDetailsItem {
|
||||||
if let anchorItem = findAnchorItem(anchor, items: item.items) {
|
if let anchorItem = findAnchorItem(anchor, items: item.items) {
|
||||||
return anchorItem
|
return anchorItem
|
||||||
}
|
}
|
||||||
@ -910,12 +915,18 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
guard let items = self.currentLayout?.items else {
|
guard let items = self.currentLayout?.items else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let webPage = self.webPage, url.webpageId == webPage.id, let anchorRange = url.url.range(of: "#") {
|
var baseUrl = url.url
|
||||||
let anchor = url.url[anchorRange.upperBound...]
|
var anchor: String?
|
||||||
|
if let anchorRange = url.url.range(of: "#") {
|
||||||
|
anchor = String(baseUrl[anchorRange.upperBound...])
|
||||||
|
baseUrl = String(baseUrl[..<anchorRange.lowerBound])
|
||||||
|
}
|
||||||
|
|
||||||
|
if let webPage = self.webPage, case let .Loaded(content) = webPage.content, let page = content.instantPage, page.url == baseUrl, let anchor = anchor {
|
||||||
if !anchor.isEmpty {
|
if !anchor.isEmpty {
|
||||||
if let anchorItem = findAnchorItem(String(anchor), items: items) {
|
if let (anchorItem, offset) = findAnchorItem(String(anchor), items: items) {
|
||||||
self.scrollNode.view.setContentOffset(CGPoint(x: 0.0, y: anchorItem.frame.origin.y - self.scrollNode.view.contentInset.top), animated: true)
|
self.scrollNode.view.setContentOffset(CGPoint(x: 0.0, y: anchorItem.frame.minY + offset - self.scrollNode.view.contentInset.top), animated: true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,10 @@ struct InstantPageTextImageItem {
|
|||||||
let id: MediaId
|
let id: MediaId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct InstantPageTextAnchorItem {
|
||||||
|
let name: String
|
||||||
|
}
|
||||||
|
|
||||||
final class InstantPageTextLine {
|
final class InstantPageTextLine {
|
||||||
let line: CTLine
|
let line: CTLine
|
||||||
let range: NSRange
|
let range: NSRange
|
||||||
@ -40,15 +44,17 @@ final class InstantPageTextLine {
|
|||||||
let strikethroughItems: [InstantPageTextStrikethroughItem]
|
let strikethroughItems: [InstantPageTextStrikethroughItem]
|
||||||
let markedItems: [InstantPageTextMarkedItem]
|
let markedItems: [InstantPageTextMarkedItem]
|
||||||
let imageItems: [InstantPageTextImageItem]
|
let imageItems: [InstantPageTextImageItem]
|
||||||
|
let anchorItems: [InstantPageTextAnchorItem]
|
||||||
let isRTL: Bool
|
let isRTL: Bool
|
||||||
|
|
||||||
init(line: CTLine, range: NSRange, frame: CGRect, strikethroughItems: [InstantPageTextStrikethroughItem], markedItems: [InstantPageTextMarkedItem], imageItems: [InstantPageTextImageItem], isRTL: Bool) {
|
init(line: CTLine, range: NSRange, frame: CGRect, strikethroughItems: [InstantPageTextStrikethroughItem], markedItems: [InstantPageTextMarkedItem], imageItems: [InstantPageTextImageItem], anchorItems: [InstantPageTextAnchorItem], isRTL: Bool) {
|
||||||
self.line = line
|
self.line = line
|
||||||
self.range = range
|
self.range = range
|
||||||
self.frame = frame
|
self.frame = frame
|
||||||
self.strikethroughItems = strikethroughItems
|
self.strikethroughItems = strikethroughItems
|
||||||
self.markedItems = markedItems
|
self.markedItems = markedItems
|
||||||
self.imageItems = imageItems
|
self.imageItems = imageItems
|
||||||
|
self.anchorItems = anchorItems
|
||||||
self.isRTL = isRTL
|
self.isRTL = isRTL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,6 +76,7 @@ final class InstantPageTextItem: InstantPageItem {
|
|||||||
var frame: CGRect
|
var frame: CGRect
|
||||||
let alignment: NSTextAlignment
|
let alignment: NSTextAlignment
|
||||||
let medias: [InstantPageMedia] = []
|
let medias: [InstantPageMedia] = []
|
||||||
|
let anchors: [String: Int]
|
||||||
let wantsNode: Bool = false
|
let wantsNode: Bool = false
|
||||||
let separatesTiles: Bool = false
|
let separatesTiles: Bool = false
|
||||||
var selectable: Bool = true
|
var selectable: Bool = true
|
||||||
@ -85,13 +92,18 @@ final class InstantPageTextItem: InstantPageItem {
|
|||||||
self.lines = lines
|
self.lines = lines
|
||||||
var index = 0
|
var index = 0
|
||||||
var rtlLineIndices = Set<Int>()
|
var rtlLineIndices = Set<Int>()
|
||||||
|
var anchors: [String: Int] = [:]
|
||||||
for line in lines {
|
for line in lines {
|
||||||
if line.isRTL {
|
if line.isRTL {
|
||||||
rtlLineIndices.insert(index)
|
rtlLineIndices.insert(index)
|
||||||
}
|
}
|
||||||
|
for anchor in line.anchorItems {
|
||||||
|
anchors[anchor.name] = index
|
||||||
|
}
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
self.rtlLineIndices = rtlLineIndices
|
self.rtlLineIndices = rtlLineIndices
|
||||||
|
self.anchors = anchors
|
||||||
}
|
}
|
||||||
|
|
||||||
func drawInTile(context: CGContext) {
|
func drawInTile(context: CGContext) {
|
||||||
@ -410,6 +422,15 @@ func attributedStringForRichText(_ text: RichText, styleStack: InstantPageTextSt
|
|||||||
let delegate = CTRunDelegateCreate(&callbacks, extentBuffer)
|
let delegate = CTRunDelegateCreate(&callbacks, extentBuffer)
|
||||||
let attrDictionaryDelegate = [(kCTRunDelegateAttributeName as NSAttributedStringKey): (delegate as Any), NSAttributedStringKey(rawValue: InstantPageMediaIdAttribute): id.id]
|
let attrDictionaryDelegate = [(kCTRunDelegateAttributeName as NSAttributedStringKey): (delegate as Any), NSAttributedStringKey(rawValue: InstantPageMediaIdAttribute): id.id]
|
||||||
return NSAttributedString(string: " ", attributes: attrDictionaryDelegate)
|
return NSAttributedString(string: " ", attributes: attrDictionaryDelegate)
|
||||||
|
case let .anchor(text, name):
|
||||||
|
styleStack.push(.anchor(name))
|
||||||
|
var text = text
|
||||||
|
if case .empty = text {
|
||||||
|
text = .plain("\u{200b}")
|
||||||
|
}
|
||||||
|
let result = attributedStringForRichText(text, styleStack: styleStack, url: url)
|
||||||
|
styleStack.pop()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,6 +518,7 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo
|
|||||||
|
|
||||||
var strikethroughItems: [InstantPageTextStrikethroughItem] = []
|
var strikethroughItems: [InstantPageTextStrikethroughItem] = []
|
||||||
var markedItems: [InstantPageTextMarkedItem] = []
|
var markedItems: [InstantPageTextMarkedItem] = []
|
||||||
|
var anchorItems: [InstantPageTextAnchorItem] = []
|
||||||
|
|
||||||
string.enumerateAttributes(in: lineRange, options: []) { attributes, range, _ in
|
string.enumerateAttributes(in: lineRange, options: []) { attributes, range, _ in
|
||||||
if let _ = attributes[NSAttributedStringKey.strikethroughStyle] {
|
if let _ = attributes[NSAttributedStringKey.strikethroughStyle] {
|
||||||
@ -507,6 +529,8 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo
|
|||||||
let lowerX = floor(CTLineGetOffsetForStringIndex(line, range.location, nil))
|
let lowerX = floor(CTLineGetOffsetForStringIndex(line, range.location, nil))
|
||||||
let upperX = ceil(CTLineGetOffsetForStringIndex(line, range.location + range.length, nil))
|
let upperX = ceil(CTLineGetOffsetForStringIndex(line, range.location + range.length, nil))
|
||||||
markedItems.append(InstantPageTextMarkedItem(frame: CGRect(x: currentLineOrigin.x + lowerX, y: currentLineOrigin.y, width: upperX - lowerX, height: fontLineHeight), color: item))
|
markedItems.append(InstantPageTextMarkedItem(frame: CGRect(x: currentLineOrigin.x + lowerX, y: currentLineOrigin.y, width: upperX - lowerX, height: fontLineHeight), color: item))
|
||||||
|
} else if let item = attributes[NSAttributedStringKey.init(rawValue: InstantPageAnchorAttribute)] as? String {
|
||||||
|
anchorItems.append(InstantPageTextAnchorItem(name: item))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -556,7 +580,7 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo
|
|||||||
}
|
}
|
||||||
|
|
||||||
let height = !fontLineHeight.isZero ? fontLineHeight : maxImageHeight
|
let height = !fontLineHeight.isZero ? fontLineHeight : maxImageHeight
|
||||||
let textLine = InstantPageTextLine(line: line, range: lineRange, frame: CGRect(x: currentLineOrigin.x, y: currentLineOrigin.y, width: lineWidth, height: height), strikethroughItems: strikethroughItems, markedItems: markedItems, imageItems: lineImageItems, isRTL: isRTL)
|
let textLine = InstantPageTextLine(line: line, range: lineRange, frame: CGRect(x: currentLineOrigin.x, y: currentLineOrigin.y, width: lineWidth, height: height), strikethroughItems: strikethroughItems, markedItems: markedItems, imageItems: lineImageItems, anchorItems: anchorItems, isRTL: isRTL)
|
||||||
|
|
||||||
lines.append(textLine)
|
lines.append(textLine)
|
||||||
imageItems.append(contentsOf: lineImageItems)
|
imageItems.append(contentsOf: lineImageItems)
|
||||||
|
@ -16,11 +16,13 @@ enum InstantPageTextStyle {
|
|||||||
case superscript
|
case superscript
|
||||||
case markerColor(UIColor)
|
case markerColor(UIColor)
|
||||||
case marker
|
case marker
|
||||||
|
case anchor(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
let InstantPageLineSpacingFactorAttribute = "LineSpacingFactorAttribute"
|
let InstantPageLineSpacingFactorAttribute = "LineSpacingFactorAttribute"
|
||||||
let InstantPageMarkerColorAttribute = "MarkerColorAttribute"
|
let InstantPageMarkerColorAttribute = "MarkerColorAttribute"
|
||||||
let InstantPageMediaIdAttribute = "MediaIdAttribute"
|
let InstantPageMediaIdAttribute = "MediaIdAttribute"
|
||||||
|
let InstantPageAnchorAttribute = "AnchorAttribute"
|
||||||
|
|
||||||
final class InstantPageTextStyleStack {
|
final class InstantPageTextStyleStack {
|
||||||
private var items: [InstantPageTextStyle] = []
|
private var items: [InstantPageTextStyle] = []
|
||||||
@ -48,6 +50,7 @@ final class InstantPageTextStyleStack {
|
|||||||
var baselineOffset: CGFloat?
|
var baselineOffset: CGFloat?
|
||||||
var markerColor: UIColor?
|
var markerColor: UIColor?
|
||||||
var marker: Bool?
|
var marker: Bool?
|
||||||
|
var anchor: String?
|
||||||
|
|
||||||
for item in self.items.reversed() {
|
for item in self.items.reversed() {
|
||||||
switch item {
|
switch item {
|
||||||
@ -104,6 +107,10 @@ final class InstantPageTextStyleStack {
|
|||||||
if marker == nil {
|
if marker == nil {
|
||||||
marker = true
|
marker = true
|
||||||
}
|
}
|
||||||
|
case let .anchor(name):
|
||||||
|
if anchor == nil {
|
||||||
|
anchor = name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,6 +184,10 @@ final class InstantPageTextStyleStack {
|
|||||||
attributes[NSAttributedStringKey(rawValue: InstantPageMarkerColorAttribute)] = markerColor
|
attributes[NSAttributedStringKey(rawValue: InstantPageMarkerColorAttribute)] = markerColor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let anchor = anchor {
|
||||||
|
attributes[NSAttributedStringKey(rawValue: InstantPageAnchorAttribute)] = anchor
|
||||||
|
}
|
||||||
|
|
||||||
return attributes
|
return attributes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -288,6 +288,9 @@ private final class LanguageSuggestionAlertContentNode: AlertContentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||||
|
var size = size
|
||||||
|
size.width = min(size.width , 270.0)
|
||||||
|
|
||||||
self.validLayout = size
|
self.validLayout = size
|
||||||
|
|
||||||
var origin: CGPoint = CGPoint(x: 0.0, y: 17.0)
|
var origin: CGPoint = CGPoint(x: 0.0, y: 17.0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user