~ 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 feed event says where its pull or issue stands now

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:ios/recent-state
crates/knotless-serve/src/web/feed.rs+26−5
@@ -86,23 +86,35 @@ pub enum Action {
refname: String,
},
/// A pull or an issue opened.
#[serde(rename = "dev.knotless.getFeed#opened")]
Opened { subject: String, title: String },
#[serde(rename = "dev.knotless.getFeed#opened", rename_all = "camelCase")]
Opened {
subject: String,
title: String,
/// Where the subject stands now, whoever moved it there.
#[serde(skip_serializing_if = "Option::is_none")]
subject_state: Option<String>,
},
/// A comment, or a review with a verdict.
#[serde(rename = "dev.knotless.getFeed#commented")]
#[serde(rename = "dev.knotless.getFeed#commented", rename_all = "camelCase")]
Commented {
subject: String,
title: String,
text: String,
#[serde(skip_serializing_if = "Option::is_none")]
verdict: Option<String>,
/// Where the subject stands now, whoever moved it there.
#[serde(skip_serializing_if = "Option::is_none")]
subject_state: Option<String>,
},
/// A pull or an issue moved to another state.
#[serde(rename = "dev.knotless.getFeed#changed")]
#[serde(rename = "dev.knotless.getFeed#changed", rename_all = "camelCase")]
Changed {
subject: String,
title: String,
state: String,
/// Where the subject stands now, whoever moved it there.
#[serde(skip_serializing_if = "Option::is_none")]
subject_state: Option<String>,
},
/// The event's repository starred, by its record.
#[serde(rename = "dev.knotless.getFeed#starred")]
@@ -273,6 +285,7 @@ async fn events(
// Pulls and issues, as their repositories' owners let them stand, by
// title, and the repositories they are on.
let mut titles: HashMap<String, String> = HashMap::new();
let mut states: HashMap<String, String> = HashMap::new();
let on: HashSet<(&str, &str)> = said
.iter()
.map(|(_, did, key)| (did.as_str(), key.as_str()))
@@ -302,6 +315,7 @@ async fn events(
titles.insert(e.uri.clone(), title);
}
}
states.extend(made.items.iter().map(|i| (i.uri.clone(), i.state.clone())));
on_repos.insert((did, key), (name, record, made));
}
// Who each is shown as said or done by.
@@ -488,20 +502,27 @@ async fn events(
};
let shown = titles.contains_key(subject);
let title = titles.get(subject).cloned().unwrap_or_default();
let subject_state = states.get(subject).cloned();
let text = |field: &str| e.value[field].as_str().unwrap_or_default().to_string();
let subject = subject.to_string();
let action = match forge::parse_uri(&e.uri).unwrap_or_default().1 {
nsid::PULL | nsid::ISSUE => Action::Opened { subject, title },
nsid::PULL | nsid::ISSUE => Action::Opened {
subject,
title,
subject_state,
},
nsid::COMMENT => Action::Commented {
subject,
title,
text: text("body"),
verdict: e.value["verdict"].as_str().map(str::to_string),
subject_state,
},
_ => Action::Changed {
subject,
title,
state: text("state"),
subject_state,
},
};
let event = Event {
crates/knotless-serve/tests/web.rs+12−4
@@ -2475,10 +2475,15 @@ fn the_feed() {
o,
("changed", json!({"subject": pull, "state": "merged"})),
&[
("opened", json!({"subject": pull, "title": "Add b"})),
// Each says where its subject stands now, not then: the pull
// merged, and the issue, closed and reopened above, open.
(
"opened",
json!({"subject": pull, "title": "Add b", "subjectState": "merged"}),
),
(
"commented",
json!({"subject": pull, "verdict": "changes", "text": "Not *yet*"}),
json!({"subject": pull, "verdict": "changes", "text": "Not *yet*", "subjectState": "merged"}),
),
(
"commented",
@@ -2486,13 +2491,16 @@ fn the_feed() {
),
(
"opened",
json!({"subject": issue, "title": "Broken <thing>"}),
json!({"subject": issue, "title": "Broken <thing>", "subjectState": "open"}),
),
(
"commented",
json!({"subject": issue, "text": "Seen it", "title": "Broken <thing>"}),
),
("changed", json!({"subject": issue, "state": "closed"})),
(
"changed",
json!({"subject": issue, "state": "closed", "subjectState": "open"}),
),
],
);
// Opened, said and decided about, by title.
docs/reference/api.md+3−3
@@ -246,9 +246,9 @@ Answers `events` (`createdAt`, `actor` `{did, handle, avatar?}`, `repo`
| `dev.knotless.getFeed#created` | `description?`, `forkOf?` |
| `dev.knotless.getFeed#pushed` | `ref`, `created`, `to?`, `forced?`, `count`, `new`, `commits` (`sha`, `message`, `merge?`) |
| `dev.knotless.getFeed#deleted` | `ref` |
| `dev.knotless.getFeed#opened` | `subject`, `title` |
| `dev.knotless.getFeed#commented` | `subject`, `title`, `text`, `verdict?` |
| `dev.knotless.getFeed#changed` | `subject`, `title`, `state` |
| `dev.knotless.getFeed#opened` | `subject`, `title`, `subjectState?` (where it stands now, whoever moved it there) |
| `dev.knotless.getFeed#commented` | `subject`, `title`, `text`, `verdict?`, `subjectState?` |
| `dev.knotless.getFeed#changed` | `subject`, `title`, `state`, `subjectState?` |
| `dev.knotless.getFeed#starred` | `subject`, the repository record |
A push's `actor` is the account that pushed: a ref's claim or record that
ios/Knotless/API/Models.swift+11−2
@@ -569,6 +569,7 @@ struct FeedEvent: Decodable, Identifiable, Hashable {
var subject: String?
var title: String?
var state: String?
var subjectState: String?
var text: String?
var verdict: String?
var ref: String?
@@ -576,14 +577,22 @@ struct FeedEvent: Decodable, Identifiable, Hashable {
var forkOf: RepoRef?
enum CodingKeys: String, CodingKey {
case type = "$type", subject, title, state, text, verdict, ref, count, forkOf
case type = "$type", subject, title, state, subjectState, text, verdict, ref, count, forkOf
}
var kind: String { type.split(separator: "#").last.map(String.init) ?? type }
/// Where the subject stands now, or as this event left it on an older server.
var standing: String? { subjectState ?? state }
/// What the account whose feed this is did, in a line of its own.
var yours: String {
switch kind {
case "opened": "You opened"
// A queue's runner merges what you opened, so no event of yours says so.
case "opened":
switch subjectState {
case "merged": "Merged"
case "closed": "Closed"
default: "You opened"
}
case "changed": "You \(state == "open" ? "reopened" : state ?? "changed")"
case "commented":
switch verdict {
ios/Knotless/Views/HomeView.swift+2−2
@@ -184,10 +184,10 @@ private struct RecentRow: View {
let event: FeedEvent
var body: some View {
let style = StateStyle(kind: kind(ofURI: event.action.subject), state: event.action.state)
let style = StateStyle(kind: kind(ofURI: event.action.subject), state: event.action.standing)
HStack(alignment: .top, spacing: Space.s3) {
Oct(name: String(style.icon.dropFirst(4)), size: Icon.xl)
.foregroundStyle(event.action.state == nil ? Color.neutral : style.color)
.foregroundStyle(event.action.standing == nil ? Color.neutral : style.color)
.frame(width: 30)
.padding(.top, Space.s0_5)
VStack(alignment: .leading, spacing: Space.s1) {
lexicons/dev/knotless/getFeed.json+6−3
@@ -100,7 +100,8 @@
"required": ["subject", "title"],
"properties": {
"subject": { "type": "string", "format": "at-uri", "description": "The dev.knotless.pull or dev.knotless.issue record." },
"title": { "type": "string" }
"title": { "type": "string" },
"subjectState": { "type": "string", "knownValues": ["open", "closed", "merged"], "description": "Where the subject stands now, whoever moved it there." }
}
},
"commented": {
@@ -111,7 +112,8 @@
"subject": { "type": "string", "format": "at-uri" },
"title": { "type": "string", "description": "The subject's title." },
"text": { "type": "string", "description": "The comment, as Markdown." },
"verdict": { "type": "string", "knownValues": ["approve", "changes"] }
"verdict": { "type": "string", "knownValues": ["approve", "changes"] },
"subjectState": { "type": "string", "knownValues": ["open", "closed", "merged"], "description": "Where the subject stands now, whoever moved it there." }
}
},
"changed": {
@@ -121,7 +123,8 @@
"properties": {
"subject": { "type": "string", "format": "at-uri" },
"title": { "type": "string", "description": "The subject's title." },
"state": { "type": "string", "knownValues": ["open", "closed", "merged"] }
"state": { "type": "string", "knownValues": ["open", "closed", "merged"] },
"subjectState": { "type": "string", "knownValues": ["open", "closed", "merged"], "description": "Where the subject stands now, whoever moved it there." }
}
},
"starred": {