mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Added copy for an unrecoverable startup case Added some additional logs to better debug ValueObservation query errors Increased the pageSize to 20 on iPad devices (to prevent it immediately loading a second page) Cleaned up a bunch of threading logic (try to avoid overriding subscribe/receive threads specified at subscription) Consolidated the 'sendMessage' and 'sendAttachments' functions Updated the various frameworks to use 'DAWRF with DSYM' to allow for better debugging during debug mode (at the cost of a longer build time) Updated the logic to optimistically insert messages when sending to avoid any database write delays Updated the logic to avoid sending notifications for messages which are already marked as read by the config Fixed an issue where multiple paths could incorrectly get built at the same time in some cases Fixed an issue where other job queues could be started before the blockingQueue finishes Fixed a potential bug with the snode version comparison (was just a string comparison which would fail when getting to double-digit values) Fixed a bug where you couldn't remove the last reaction on a message Fixed the broken media message zoom animations Fixed a bug where the last message read in a conversation wouldn't be correctly detected as already read Fixed a bug where the QuoteView had no line limits (resulting in the '@You' mention background highlight being incorrectly positioned in the quote preview) Fixed a bug where a large number of configSyncJobs could be scheduled (only one would run at a time but this could result in performance impacts)
162 lines
6.9 KiB
Swift
162 lines
6.9 KiB
Swift
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
|
|
|
import UIKit
|
|
|
|
public extension NSAttributedString.Key {
|
|
static let currentUserMentionBackgroundColor: NSAttributedString.Key = NSAttributedString.Key(rawValue: "currentUserMentionBackgroundColor")
|
|
static let currentUserMentionBackgroundCornerRadius: NSAttributedString.Key = NSAttributedString.Key(rawValue: "currentUserMentionBackgroundCornerRadius")
|
|
static let currentUserMentionBackgroundPadding: NSAttributedString.Key = NSAttributedString.Key(rawValue: "currentUserMentionBackgroundPadding")
|
|
}
|
|
|
|
public class HighlightMentionBackgroundView: UIView {
|
|
weak var targetLabel: UILabel?
|
|
var maxPadding: CGFloat = 0
|
|
|
|
init(targetLabel: UILabel) {
|
|
self.targetLabel = targetLabel
|
|
|
|
super.init(frame: .zero)
|
|
|
|
self.isOpaque = false
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
// MARK: - Functions
|
|
|
|
public func calculateMaxPadding(for attributedText: NSAttributedString) -> CGFloat {
|
|
var allMentionRadii: [CGFloat?] = []
|
|
let path: CGMutablePath = CGMutablePath()
|
|
path.addRect(CGRect(
|
|
x: 0,
|
|
y: 0,
|
|
width: CGFloat.greatestFiniteMagnitude,
|
|
height: CGFloat.greatestFiniteMagnitude
|
|
))
|
|
|
|
let framesetter = CTFramesetterCreateWithAttributedString(attributedText as CFAttributedString)
|
|
let frame: CTFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributedText.length), path, nil)
|
|
let lines: [CTLine] = frame.lines
|
|
|
|
lines.forEach { line in
|
|
let runs: [CTRun] = line.ctruns
|
|
|
|
runs.forEach { run in
|
|
let attributes: NSDictionary = CTRunGetAttributes(run)
|
|
allMentionRadii.append(
|
|
attributes
|
|
.value(forKey: NSAttributedString.Key.currentUserMentionBackgroundPadding.rawValue) as? CGFloat
|
|
)
|
|
}
|
|
}
|
|
|
|
let maxRadii: CGFloat? = allMentionRadii
|
|
.compactMap { $0 }
|
|
.max()
|
|
|
|
return (maxRadii ?? 0)
|
|
}
|
|
|
|
// MARK: - Drawing
|
|
|
|
override public func draw(_ rect: CGRect) {
|
|
guard
|
|
let targetLabel: UILabel = self.targetLabel,
|
|
let attributedText: NSAttributedString = targetLabel.attributedText,
|
|
let context = UIGraphicsGetCurrentContext()
|
|
else { return }
|
|
|
|
// Need to invery the Y axis because iOS likes to render from the bottom left instead of the top left
|
|
context.textMatrix = .identity
|
|
context.translateBy(x: 0, y: bounds.size.height)
|
|
context.scaleBy(x: 1.0, y: -1.0)
|
|
|
|
// Note: Calculations MUST happen based on the 'targetLabel' size as this class has extra padding
|
|
// which can result in calculations being off
|
|
let path = CGMutablePath()
|
|
let size = targetLabel.sizeThatFits(CGSize(width: targetLabel.bounds.width, height: .greatestFiniteMagnitude))
|
|
path.addRect(CGRect(x: 0, y: 0, width: size.width, height: size.height), transform: .identity)
|
|
|
|
let framesetter = CTFramesetterCreateWithAttributedString(attributedText as CFAttributedString)
|
|
let frame: CTFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributedText.length), path, nil)
|
|
let lines: [CTLine] = frame.lines
|
|
|
|
var origins = [CGPoint](repeating: .zero, count: lines.count)
|
|
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), &origins)
|
|
|
|
for lineIndex in 0..<lines.count {
|
|
let line = lines[lineIndex]
|
|
let runs: [CTRun] = line.ctruns
|
|
var ascent: CGFloat = 0
|
|
var descent: CGFloat = 0
|
|
var leading: CGFloat = 0
|
|
_ = CGFloat(CTLineGetTypographicBounds(line, &ascent, &descent, &leading))
|
|
|
|
for run in runs {
|
|
let attributes: NSDictionary = CTRunGetAttributes(run)
|
|
|
|
guard let mentionBackgroundColor: UIColor = attributes.value(forKey: NSAttributedString.Key.currentUserMentionBackgroundColor.rawValue) as? UIColor else {
|
|
continue
|
|
}
|
|
|
|
let maybeCornerRadius: CGFloat? = (attributes
|
|
.value(forKey: NSAttributedString.Key.currentUserMentionBackgroundCornerRadius.rawValue) as? CGFloat)
|
|
let maybePadding: CGFloat? = (attributes
|
|
.value(forKey: NSAttributedString.Key.currentUserMentionBackgroundPadding.rawValue) as? CGFloat)
|
|
let padding: CGFloat = (maybePadding ?? 0)
|
|
|
|
let range = CTRunGetStringRange(run)
|
|
var runBounds: CGRect = .zero
|
|
var runAscent: CGFloat = 0
|
|
var runDescent: CGFloat = 0
|
|
runBounds.size.width = CGFloat(CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &runAscent, &runDescent, nil) + (padding * 2))
|
|
runBounds.size.height = (runAscent + runDescent + (padding * 2))
|
|
|
|
let xOffset: CGFloat = {
|
|
switch CTRunGetStatus(run) {
|
|
case .rightToLeft:
|
|
return CTLineGetOffsetForStringIndex(line, range.location + range.length, nil)
|
|
|
|
default:
|
|
return CTLineGetOffsetForStringIndex(line, range.location, nil)
|
|
}
|
|
}()
|
|
|
|
// HACK: This `extraYOffset` value is a hack to resolve a weird issue where the
|
|
// positioning seems to be slightly off every additional line of text we add (it
|
|
// doesn't seem to be related to line spacing or anything, more related to the
|
|
// bold mention text being positioned slightly differently from the non-bold text)
|
|
let extraYOffset: CGFloat = (CGFloat(lineIndex) * (runDescent / 12))
|
|
|
|
// Note: Changes to `origin.y` need to be inverted since the context has been flipped
|
|
runBounds.origin.x = origins[lineIndex].x + rect.origin.x + self.maxPadding + xOffset - padding
|
|
runBounds.origin.y = (
|
|
origins[lineIndex].y + rect.origin.y +
|
|
self.maxPadding -
|
|
padding -
|
|
runDescent -
|
|
extraYOffset
|
|
)
|
|
|
|
let path = UIBezierPath(roundedRect: runBounds, cornerRadius: (maybeCornerRadius ?? 0))
|
|
mentionBackgroundColor.setFill()
|
|
path.fill()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension CTFrame {
|
|
var lines: [CTLine] {
|
|
return ((CTFrameGetLines(self) as [AnyObject] as? [CTLine]) ?? [])
|
|
}
|
|
}
|
|
|
|
extension CTLine {
|
|
var ctruns: [CTRun] {
|
|
return ((CTLineGetGlyphRuns(self) as [AnyObject] as? [CTRun]) ?? [])
|
|
}
|
|
}
|