fix: date repositories from their state #23
@@ -184,6 +184,11 @@ pub fn all_announcements() -> Filter {
|
|||||||
Filter::new().kind(Kind::GitRepoAnnouncement)
|
Filter::new().kind(Kind::GitRepoAnnouncement)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// All repository state events, carrying each repository's refs and last push time.
|
||||||
|
pub fn all_states() -> Filter {
|
||||||
|
Filter::new().kind(Kind::RepoState)
|
||||||
|
}
|
||||||
|
|
||||||
/// How far back deletion requests are fetched and stored.
|
/// How far back deletion requests are fetched and stored.
|
||||||
const DELETIONS_LOOKBACK: Duration = Duration::from_secs(3 * 365 * 86_400);
|
const DELETIONS_LOOKBACK: Duration = Duration::from_secs(3 * 365 * 86_400);
|
||||||
|
|
||||||
|
|||||||
@@ -207,7 +207,11 @@ impl RepoStore {
|
|||||||
update.kind == Kind::EventDeletion || update.kind == Kind::RequestToVanish;
|
update.kind == Kind::EventDeletion || update.kind == Kind::RequestToVanish;
|
||||||
let coordinate = update.coordinate.as_ref() == Some(addr);
|
let coordinate = update.coordinate.as_ref() == Some(addr);
|
||||||
let author = update.author == addr.public_key;
|
let author = update.author == addr.public_key;
|
||||||
let kind = update.kind == Kind::GitRepoAnnouncement;
|
// Announcements and state share the owner and identifier.
|
||||||
|
// State events carry `d`, not `a`, so `coordinate` misses them.
|
||||||
|
let authored = (update.kind == Kind::GitRepoAnnouncement
|
||||||
|
|| update.kind == Kind::RepoState)
|
||||||
|
&& author;
|
||||||
// NIP-22 comments carry no `a` tag.
|
// NIP-22 comments carry no `a` tag.
|
||||||
// Coordinate matching fails for them.
|
// Coordinate matching fails for them.
|
||||||
// Any comment may reference this repository's roots.
|
// Any comment may reference this repository's roots.
|
||||||
@@ -216,18 +220,22 @@ impl RepoStore {
|
|||||||
// Any status event may reference a root of this repository.
|
// Any status event may reference a root of this repository.
|
||||||
let status = RepoStatus::from_kind(update.kind).is_some();
|
let status = RepoStatus::from_kind(update.kind).is_some();
|
||||||
|
|
||||||
deletion || coordinate || (author && kind) || comment || status
|
deletion || coordinate || authored || comment || status
|
||||||
}),
|
}),
|
||||||
BackendEvent::Published(event) => {
|
BackendEvent::Published(event) => {
|
||||||
let kind = event.kind == Kind::GitRepoAnnouncement;
|
let announcement = event.kind == Kind::GitRepoAnnouncement;
|
||||||
let author = event.pubkey == addr.public_key;
|
let author = event.pubkey == addr.public_key;
|
||||||
let coordinate = event.tags.coordinates().into_iter().any(|c| c == *addr);
|
let coordinate = event.tags.coordinates().into_iter().any(|c| c == *addr);
|
||||||
|
// State events carry their repository id in the `d` tag.
|
||||||
|
let state = event.kind == Kind::RepoState
|
||||||
|
&& author
|
||||||
|
&& event.tags.identifier().as_deref() == Some(addr.identifier.as_str());
|
||||||
// Locally published deletions may target any event of this repository.
|
// Locally published deletions may target any event of this repository.
|
||||||
// Refresh so they take effect immediately, like relay deletions.
|
// Refresh so they take effect immediately, like relay deletions.
|
||||||
let deletion =
|
let deletion =
|
||||||
event.kind == Kind::EventDeletion || event.kind == Kind::RequestToVanish;
|
event.kind == Kind::EventDeletion || event.kind == Kind::RequestToVanish;
|
||||||
|
|
||||||
coordinate || (kind && author) || deletion
|
coordinate || (announcement && author) || state || deletion
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -83,12 +83,16 @@ impl RepoListStore {
|
|||||||
BackendEvent::Published(event) => {
|
BackendEvent::Published(event) => {
|
||||||
let announcement = event.kind == Kind::GitRepoAnnouncement;
|
let announcement = event.kind == Kind::GitRepoAnnouncement;
|
||||||
|
|
||||||
|
// Locally published state events advance last activity.
|
||||||
|
// Refresh so a push made in this session dates the card immediately.
|
||||||
|
let state = event.kind == Kind::RepoState;
|
||||||
|
|
||||||
// Locally published deletions are already in the local database.
|
// Locally published deletions are already in the local database.
|
||||||
// Refresh so they take effect immediately, like relay deletions.
|
// Refresh so they take effect immediately, like relay deletions.
|
||||||
let deletion =
|
let deletion =
|
||||||
event.kind == Kind::EventDeletion || event.kind == Kind::RequestToVanish;
|
event.kind == Kind::EventDeletion || event.kind == Kind::RequestToVanish;
|
||||||
|
|
||||||
announcement || deletion
|
announcement || state || deletion
|
||||||
}
|
}
|
||||||
// Only a completed sync refreshes the list.
|
// Only a completed sync refreshes the list.
|
||||||
// Progress ticks would re-scan the whole database several times
|
// Progress ticks would re-scan the whole database several times
|
||||||
@@ -133,6 +137,9 @@ impl RepoListStore {
|
|||||||
|
|
||||||
backend.update(cx, |backend, cx| {
|
backend.update(cx, |backend, cx| {
|
||||||
backend.sync_bootstrap(filters::all_announcements(), cx);
|
backend.sync_bootstrap(filters::all_announcements(), cx);
|
||||||
|
// State events carry the last push time, so syncing them globally
|
||||||
|
// lets the list date every repository without the user opening it.
|
||||||
|
backend.sync_bootstrap(filters::all_states(), cx);
|
||||||
// Deletion requests, NIP-09/62, must be known before any announcement is shown.
|
// Deletion requests, NIP-09/62, must be known before any announcement is shown.
|
||||||
backend.sync_bootstrap(filters::deletions(), cx);
|
backend.sync_bootstrap(filters::deletions(), cx);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -123,24 +123,26 @@ flowchart TD
|
|||||||
|
|
||||||
## Remediation options
|
## Remediation options
|
||||||
|
|
||||||
- **A. Global state sync (smallest change).**
|
- **A. Global state sync (smallest change). — implemented.**
|
||||||
Add `sync_bootstrap(Filter::new().kind(Kind::RepoState))` alongside the announcement
|
`RepoListStore::subscribe_remote` now also syncs `filters::all_states()`
|
||||||
sync in `RepoListStore::subscribe_remote`. Kind `30618` is addressable, so relays
|
(`Filter::new().kind(Kind::RepoState)`) alongside the announcement sync. Kind `30618`
|
||||||
keep one event per repository — volume comparable to announcements. Caveat: coverage
|
is addressable, so relays keep one event per repository — volume comparable to
|
||||||
depends on whether state events reach the bootstrap relays; if a grasp server is the
|
announcements. Caveat: coverage depends on whether state events reach the bootstrap
|
||||||
only holder, those repositories remain unseen.
|
relays; if a grasp server is the only holder, those repositories remain unseen.
|
||||||
|
|
||||||
- **B. Targeted fetch for the user's own repositories.**
|
- **B. Targeted fetch for the user's own repositories. — not implemented.**
|
||||||
In `sync_inbox` (`crates/signed_state/src/backend.rs` L1126-1152), where the user's
|
In `sync_inbox` (`crates/signed_state/src/backend.rs` L1126-1152), where the user's
|
||||||
own repositories' relays are already connected, also sync `filters::state(addr)` per
|
own repositories' relays are already connected, also sync `filters::state(addr)` per
|
||||||
own repository. This directly addresses "my repositories updated elsewhere" at low
|
own repository. This directly addresses "my repositories updated elsewhere" at low
|
||||||
cost. Note the existing race: `sync_inbox` reads the announcement list right after
|
cost. Note the existing race: `sync_inbox` reads the announcement list right after
|
||||||
login, which may not be loaded yet.
|
login, which may not be loaded yet.
|
||||||
|
|
||||||
- **C. Reactivity fixes.**
|
- **C. Reactivity fixes. — implemented.**
|
||||||
Include `Kind::RepoState` in the `Published` refresh conditions, and in
|
`Kind::RepoState` is now included in the `BackendEvent::Published` refresh condition of
|
||||||
`RepoStore::subscribe_backend` either match kind plus author, or extend `Update` to
|
both `RepoListStore` and `RepoStore`. `RepoStore::subscribe_backend` also matches relay
|
||||||
carry the `d`-tag identifier.
|
state updates by owner plus kind — state events carry `d`, not `a`, so coordinate
|
||||||
|
matching misses them — and locally published state updates by owner plus the `d`
|
||||||
|
identifier.
|
||||||
|
|
||||||
Suggested combination: A + C, plus B if own-repository freshness on fresh profiles
|
A + C are implemented; B remains available if own-repository freshness on fresh profiles
|
||||||
matters.
|
matters.
|
||||||
|
|||||||
Reference in New Issue
Block a user