~ 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

A pull's header shows its repository owner's picture, whoever opened it

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:ios/owner-avatar
ios/Knotless/Support/Faces.swift+6−0
@@ -17,6 +17,12 @@ actor Faces {
private var loading: [URL: Task<UIImage, Error>] = [:]
private nonisolated(unsafe) static let addresses = NSCache<NSString, NSString>()
/// Where an account's picture is, where an answer has said.
nonisolated static func address(of did: String) -> String? { addresses.object(forKey: did as NSString) as String? }
nonisolated static func keep(address: String, of did: String) { addresses.setObject(address as NSString, forKey: did as NSString) }
nonisolated static func kept(_ url: URL) -> UIImage? { store.object(forKey: url as NSURL) }
func load(_ url: URL) async throws -> UIImage {
ios/Knotless/Views/HomeView.swift+2−0
@@ -326,11 +326,13 @@ struct MyItemsView: View {
Text("Closed").tag("closed")
}
} label: { chip(state == "open" ? "Open" : "Closed") }
.tint(.primary)
Menu {
Picker("Which", selection: $relation) {
ForEach(relations, id: \.0) { Text($0.1).tag($0.0) }
}
} label: { chip(relations.first { $0.0 == relation }?.1 ?? "") }
.tint(.primary)
Spacer()
}
.padding(.horizontal, 16)
ios/Knotless/Views/ThreadView.swift+41−2
@@ -5,6 +5,7 @@ struct ThreadView: View {
let uri: String
@Environment(Account.self) private var account
@State private var screen: CGFloat = 0
@State private var expanded = false
/// Moved by a write, so the thread is read again as it now stands.
@State private var edits = 0
@@ -49,6 +50,7 @@ struct ThreadView: View {
.padding(.bottom, 8)
}
}
.onGeometryChange(for: CGFloat.self, of: \.size.width) { screen = $0 }
.alert("That didn't work", isPresented: Binding(get: { failure != nil }, set: { if !$0 { failure = nil } })) {
Button("OK", role: .cancel) {}
} message: {
@@ -61,7 +63,8 @@ struct ThreadView: View {
Text(title).font(.subheadline.weight(.semibold)).lineLimit(1)
Text(repo.label).font(.caption).foregroundStyle(.secondary).lineLimit(1)
}
.frame(maxWidth: .infinity, alignment: .leading)
// A bar doesn't keep it off the buttons beside it when it's long.
.frame(width: titleWidth, alignment: .leading)
}
}
if let url = pageURL {
@@ -103,6 +106,12 @@ struct ThreadView: View {
return URL(string: "https://knotless.dev/\(repo.handle)/\(repo.name)/\(kind)/\(parts[at - 1])/\(parts[at + 1])")
}
/// The screen, less the back button on one side and the bar's buttons on the other.
private var titleWidth: CGFloat {
let buttons: CGFloat = (pageURL == nil ? 0 : 1) + (canSetState == true && state != "merged" ? 1 : 0)
return max(0, screen - 76 - 16 - buttons * 52)
}
private func content(_ thread: PullThread) -> some View {
ScrollView {
VStack(spacing: 0) {
@@ -130,7 +139,7 @@ struct ThreadView: View {
return VStack(alignment: .leading, spacing: 12) {
NavigationLink(value: Route.repo(repo)) {
HStack(spacing: 6) {
Avatar(url: item.author.did == repo.did ? item.author.avatar : nil, name: repo.handle, did: repo.did, size: 24)
OwnerAvatar(repo: repo, known: item.author.did == repo.did ? item.author.avatar : nil)
Text(repo.handle).lineLimit(1)
Text("/").foregroundStyle(.secondary)
Text(repo.name).lineLimit(1)
@@ -449,6 +458,36 @@ struct CommentRow: View {
}
}
/// A repository's owner's picture. A thread's answer doesn't name it, so unless
/// its author is the owner it's asked for, once while the app runs. A plain
/// ground until the answer, as the identicon would flash first.
private struct OwnerAvatar: View {
let repo: RepoRef
let known: String?
@State private var answered = false
@State private var asked: String?
private var url: String? { known ?? asked ?? Faces.address(of: repo.did) }
var body: some View {
Group {
if url != nil || answered {
Avatar(url: url, name: repo.handle, did: repo.did, size: 24)
} else {
Circle().fill(Color(.tertiarySystemFill)).frame(width: 24, height: 24)
}
}
.task(id: repo.did) {
guard url == nil else { return }
let profile: Profile? = try? await Client.shared.query("getProfile", ["actor": repo.did])
guard !Task.isCancelled else { return }
if let avatar = profile?.avatar { Faces.keep(address: avatar, of: repo.did) }
asked = profile?.avatar
answered = true
}
}
}
/// The lines of a pull's diff a comment is on, as they were when it was
/// written: those it names marked, and for one alone a few before it.
private struct LineQuote: View {