~ 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's repository and profile screens follow GitHub's

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:ios/repo-profile
ios/Knotless/API/Client.swift+16−5
@@ -45,18 +45,28 @@ actor Client {
/// A write, as the account signed in. What it answers is left, as a screen
/// that writes reads what it shows again.
func send(_ write: Write) async throws {
try await call(write.method, write.input)
#if DEBUG
if DevAccount.recording != nil { return }
#endif
_ = try await call(write)
}
private func call(_ method: String, _ input: [String: String]) async throws {
/// A write whose answer the screen needs, as a fork's says what it made.
func answer<T: Decodable & Sendable>(to write: Write) async throws -> T {
#if DEBUG
if DevAccount.recording != nil { return }
if let recorded = DevAccount.recording {
return try decoder.decode(T.self, from: recorded.answer(write.method, write.input))
}
#endif
var request = URLRequest(url: Self.site.appendingPathComponent("xrpc/dev.knotless." + method))
return try decoder.decode(T.self, from: await call(write))
}
private func call(_ write: Write) async throws -> Data {
var request = URLRequest(url: Self.site.appendingPathComponent("xrpc/dev.knotless." + write.method))
request.httpMethod = "POST"
request.timeoutInterval = 60
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONSerialization.data(withJSONObject: input)
request.httpBody = try JSONSerialization.data(withJSONObject: write.input)
authorize(&request, nil)
let (data, response) = try await session.data(for: request)
let status = (response as? HTTPURLResponse)?.statusCode ?? 0
@@ -65,6 +75,7 @@ actor Client {
let text = failure == nil ? String(data: data, encoding: .utf8) : nil
throw APIError(status: status, name: failure?.error, message: failure?.message ?? text)
}
return data
}
/// Trades the code a login came back with for the session's token.
ios/Knotless/API/Models.swift+100−0
@@ -24,10 +24,23 @@ struct Repo: Decodable, Identifiable, Hashable {
var starCount: Int?
var forkCount: Int?
var `private`: Bool?
var language: Language?
var id: String { uri }
var ref: RepoRef { RepoRef(did: owner.did, handle: owner.handle, name: name) }
}
struct Language: Decodable, Hashable {
let name: String
var color: String?
}
/// A link a repository or an account shows, by its title or else its host.
struct WebLink: Decodable, Hashable {
let url: String
var title: String?
var label: String { title ?? URL(string: url)?.host() ?? url }
}
struct RepoList: Decodable {
var repos: [Repo] = []
}
@@ -42,14 +55,93 @@ struct RepoDetail: Decodable {
let openIssues: Int
var starCount: Int?
var `private`: Bool?
var links: [WebLink]?
var forkOf: Basic?
var releases: Releases?
var viewer: Viewing?
struct Viewing: Decodable {
/// The asker's star of it, if they starred it.
var star: String?
/// What they chose to hear of it: `all`, `releases` or `ignore`.
var subscription: String?
}
struct Basic: Decodable, Hashable {
let did: String
let handle: String
let name: String
var ref: RepoRef { RepoRef(did: did, handle: handle, name: name) }
}
struct Releases: Decodable {
let count: Int
var release: Latest?
struct Latest: Decodable {
let package: String
let version: String
var latest: Bool?
var createdAt: String?
}
}
}
struct RepoMade: Decodable, Sendable {
let uri: String
let name: String
}
struct Branch: Decodable, Identifiable, Hashable {
let name: String
var id: String { name }
}
struct BranchList: Decodable {
var branches: [Branch] = []
}
struct CommitList: Decodable {
var commits: [Commit] = []
var cursor: String?
}
struct Release: Decodable, Identifiable, Hashable {
let uri: String
let package: String
let version: String
var latest: Bool?
var notes: String?
var files: [File]?
var createdAt: String?
var id: String { uri }
struct File: Decodable, Hashable {
let name: String
var size: Int?
}
}
struct ReleaseList: Decodable {
var releases: [Release] = []
var cursor: String?
}
struct Run: Decodable, Identifiable, Hashable {
let check: Check
let ref: String
var pull: Pull?
var batch: Int?
var subject: String?
var id: String { check.id }
struct Pull: Decodable, Hashable { var title: String? }
}
struct RunList: Decodable {
var runs: [Run] = []
var cursor: String?
}
struct ItemLabel: Decodable, Hashable {
let name: String
var color: String?
@@ -145,6 +237,7 @@ struct Check: Decodable, Identifiable, Hashable {
let status: String
/// Why it was skipped, when it was.
var skipped: String?
var startedAt: String?
var id: String { uri }
}
@@ -254,7 +347,14 @@ struct Profile: Decodable {
var avatar: String?
var followersCount: Int?
var followsCount: Int?
var links: [WebLink]?
var viewer: Viewing?
var orgs: [Org]?
struct Viewing: Decodable {
/// The asker's follow of them, if they follow them.
var following: String?
}
}
struct InboxItem: Decodable, Identifiable, Hashable {
ios/Knotless/API/Writes.swift+13−0
@@ -40,6 +40,19 @@ enum Writes {
Write(method: undo ? "unstarRepo" : "starRepo", input: ["repo": repo.uri])
}
static func fork(repo: RepoRef) -> Write {
Write(method: "forkRepo", input: ["repo": repo.uri])
}
/// `all`, `releases` or `ignore`, or `participating` to forget the choice.
static func hear(repo: RepoRef, level: String) -> Write {
Write(method: "putSubscription", input: ["subject": repo.uri, "level": level])
}
static func follow(did: String, undo: Bool) -> Write {
Write(method: undo ? "unfollowActor" : "followActor", input: ["subject": did])
}
static func seen(at: String) -> Write {
Write(method: "updateSeen", input: ["seenAt": at])
}
ios/Knotless/Assets.xcassets/oct-bell-fill.imageset/bell-fill.svgnew+1−0
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8 16c.9 0 1.7-.6 1.9-1.5.1-.3-.1-.5-.4-.5h-3c-.3 0-.5.2-.4.5.2.9 1 1.5 1.9 1.5ZM3 5c0-2.8 2.2-5 5-5s5 2.2 5 5v3l1.7 2.6c.2.2.3.5.3.8 0 .8-.7 1.5-1.5 1.5h-11c-.8.1-1.5-.6-1.5-1.4 0-.3.1-.6.3-.8L3 8.1V5Z"/></svg>
\ No newline at end of file
ios/Knotless/Assets.xcassets/oct-bell-fill.imageset/Contents.jsonnew+1−0
@@ -0,0 +1 @@
{"images":[{"filename":"bell-fill.svg","idiom":"universal"}],"info":{"author":"xcode","version":1},"properties":{"preserves-vector-representation":true,"template-rendering-intent":"template"}}
\ No newline at end of file
ios/Knotless/Assets.xcassets/oct-bell-slash.imageset/bell-slash.svgnew+1−0
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="m4.182 4.31.016.011 10.104 7.316.013.01 1.375.996a.75.75 0 1 1-.88 1.214L13.626 13H2.518a1.516 1.516 0 0 1-1.263-2.36l1.703-2.554A.255.255 0 0 0 3 7.947V5.305L.31 3.357a.75.75 0 1 1 .88-1.214Zm7.373 7.19L4.5 6.391v1.556c0 .346-.102.683-.294.97l-1.703 2.556a.017.017 0 0 0-.003.01c0 .005.002.009.005.012l.006.004.007.001ZM8 1.5c-.997 0-1.895.416-2.534 1.086A.75.75 0 1 1 4.38 1.55 5 5 0 0 1 13 5v2.373a.75.75 0 0 1-1.5 0V5A3.5 3.5 0 0 0 8 1.5ZM8 16a2 2 0 0 1-1.985-1.75c-.017-.137.097-.25.235-.25h3.5c.138 0 .252.113.235.25A2 2 0 0 1 8 16Z"/></svg>
\ No newline at end of file
ios/Knotless/Assets.xcassets/oct-bell-slash.imageset/Contents.jsonnew+1−0
@@ -0,0 +1 @@
{"images":[{"filename":"bell-slash.svg","idiom":"universal"}],"info":{"author":"xcode","version":1},"properties":{"preserves-vector-representation":true,"template-rendering-intent":"template"}}
\ No newline at end of file
ios/Knotless/Assets.xcassets/oct-bell.imageset/bell.svgnew+1−0
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8 16a2 2 0 0 0 1.985-1.75c.017-.137-.097-.25-.235-.25h-3.5c-.138 0-.252.113-.235.25A2 2 0 0 0 8 16ZM3 5a5 5 0 0 1 10 0v2.947c0 .05.015.098.042.139l1.703 2.555A1.519 1.519 0 0 1 13.482 13H2.518a1.516 1.516 0 0 1-1.263-2.36l1.703-2.554A.255.255 0 0 0 3 7.947Zm5-3.5A3.5 3.5 0 0 0 4.5 5v2.947c0 .346-.102.683-.294.97l-1.703 2.556a.017.017 0 0 0-.003.01l.001.006c0 .002.002.004.004.006l.006.004.007.001h10.964l.007-.001.006-.004.004-.006.001-.007a.017.017 0 0 0-.003-.01l-1.703-2.554a1.745 1.745 0 0 1-.294-.97V5A3.5 3.5 0 0 0 8 1.5Z"/></svg>
\ No newline at end of file
ios/Knotless/Assets.xcassets/oct-bell.imageset/Contents.jsonnew+1−0
@@ -0,0 +1 @@
{"images":[{"filename":"bell.svg","idiom":"universal"}],"info":{"author":"xcode","version":1},"properties":{"preserves-vector-representation":true,"template-rendering-intent":"template"}}
\ No newline at end of file
ios/Knotless/Assets.xcassets/oct-git-branch.imageset/Contents.jsonnew+1−0
@@ -0,0 +1 @@
{"images":[{"filename":"git-branch.svg","idiom":"universal"}],"info":{"author":"xcode","version":1},"properties":{"preserves-vector-representation":true,"template-rendering-intent":"template"}}
\ No newline at end of file
ios/Knotless/Assets.xcassets/oct-git-branch.imageset/git-branch.svgnew+1−0
ios/Knotless/Assets.xcassets/oct-info.imageset/Contents.jsonnew+1−0
ios/Knotless/Assets.xcassets/oct-info.imageset/info.svgnew+1−0
ios/Knotless/Assets.xcassets/oct-link.imageset/Contents.jsonnew+1−0
ios/Knotless/Assets.xcassets/oct-link.imageset/link.svgnew+1−0
ios/Knotless/Assets.xcassets/oct-organization.imageset/Contents.jsonnew+1−0
ios/Knotless/Assets.xcassets/oct-organization.imageset/organization.svgnew+1−0
ios/Knotless/Assets.xcassets/oct-play.imageset/Contents.jsonnew+1−0
ios/Knotless/Assets.xcassets/oct-play.imageset/play.svgnew+1−0
ios/Knotless/Assets.xcassets/oct-repo-forked.imageset/Contents.jsonnew+1−0
ios/Knotless/Assets.xcassets/oct-repo-forked.imageset/repo-forked.svgnew+1−0
ios/Knotless/Assets.xcassets/oct-repo.imageset/Contents.jsonnew+1−0
ios/Knotless/Assets.xcassets/oct-repo.imageset/repo.svgnew+1−0
ios/Knotless/Assets.xcassets/oct-star-fill.imageset/Contents.jsonnew+1−0
ios/Knotless/Assets.xcassets/oct-star-fill.imageset/star-fill.svgnew+1−0
ios/Knotless/Assets.xcassets/oct-star.imageset/Contents.jsonnew+1−0
ios/Knotless/Assets.xcassets/oct-star.imageset/star.svgnew+1−0
ios/Knotless/Assets.xcassets/oct-tag.imageset/Contents.jsonnew+1−0
ios/Knotless/Assets.xcassets/oct-tag.imageset/tag.svgnew+1−0
ios/Knotless/Views/FilesView.swift+17−0
ios/Knotless/Views/ProfileView.swift+174−22
ios/Knotless/Views/RepoView.swift+621−88
709 lines changed: not loaded until asked for.
ios/Knotless/App.swift+15−0
ios/KnotlessUITests/FlowTests.swift+1−1
ios/ModelCheck/main.swift+11−0
ios/look-answers.json+1−1
ios/look.mjs+5−0