~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

The iOS app folds a description by how tall it is, not how long

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:ios/fold-by-height
ios/Knotless/Views/ThreadView.swift+30−12
@@ -1,12 +1,20 @@
import SwiftUI
struct ThreadView: View {
/// How much of a long description shows before Read more.
private static let foldedHeight: CGFloat = 360
/// How much taller than that it must be to fold at all.
private static let foldSlack: CGFloat = 80
/// As much as is drawn folded, far more than `foldedHeight` holds.
private static let foldedLength = 2000
let repo: RepoRef
let uri: String
@Environment(Account.self) private var account
@State private var screen: CGFloat = 0
@State private var expanded = false
@State private var bodyHeight: CGFloat = 0
/// Moved by a write, so the thread is read again as it now stands.
@State private var edits = 0
@State private var composing = false
@@ -294,28 +302,38 @@ struct ThreadView: View {
@ViewBuilder private func body(_ text: String?, html: String?) -> some View {
if let text, !text.isEmpty {
if text.count > 800, !expanded {
MarkdownView(source: String(text.prefix(2000)), html: html, place: MarkdownPlace(repo: repo, subject: uri))
// Its whole height and then cut, or its text is squeezed to
// fit and truncates, rather than reading as it does opened.
.fixedSize(horizontal: false, vertical: true)
.frame(maxHeight: 360, alignment: .top)
.clipped()
.mask(LinearGradient(stops: [.init(color: .black, location: 0.7), .init(color: .clear, location: 1)], startPoint: .top, endPoint: .bottom))
.overlay(alignment: .bottom) {
let folded = !expanded && folds(text)
MarkdownView(source: expanded ? text : String(text.prefix(Self.foldedLength)), html: html, place: MarkdownPlace(repo: repo, subject: uri))
// Its whole height and then cut, or its text is squeezed to
// fit and truncates, rather than reading as it does opened.
.fixedSize(horizontal: false, vertical: true)
.onGeometryChange(for: CGFloat.self, of: \.size.height) { bodyHeight = $0 }
.frame(maxHeight: folded ? Self.foldedHeight : nil, alignment: .top)
.clipped()
.mask(LinearGradient(stops: [
.init(color: .black, location: folded ? 0.7 : 1),
.init(color: folded ? .clear : .black, location: 1),
], startPoint: .top, endPoint: .bottom))
.overlay(alignment: .bottom) {
if folded {
Button { expanded = true } label: {
SwiftUI.Label { Text("Read more") } icon: { Oct(name: "arrow-down", size: Icon.md) }.font(.callout.weight(.medium))
}
.floating()
}
} else {
MarkdownView(source: text, html: html, place: MarkdownPlace(repo: repo, subject: uri))
}
}
} else {
Text("No description.").foregroundStyle(.secondary)
}
}
/// Whether folding hides enough to be worth a button. Until it's measured,
/// a guess from its length, so what's going to fold doesn't first draw whole.
private func folds(_ text: String) -> Bool {
if text.count > Self.foldedLength { return true }
ios/Knotless/Views/ThreadView.swiftline 333
review.knotless.dev

The length check wins over the measured height here. A body over 2000 characters that draws short still folds and is cut at 2000 characters of source. Consider checking bodyHeight first when it is non-zero.

ios/Knotless/Views/ThreadView.swiftline 333
review.knotless.dev

This returns true on length alone, before the measured height is read. A long body of links that draws short still folds, and the 2000-character prefix can cut it mid-link. Consider checking bodyHeight first and using the length only while it is 0.

return bodyHeight == 0 ? text.count > 800 : bodyHeight > Self.foldedHeight + Self.foldSlack
}
@ViewBuilder private func verdict(_ verdict: String?, outdated: Bool) -> some View {
switch verdict {
case "approve":