A card of one's own: one_per_person, prefilled, replaced on send; no double sends; refusals are shown
Test / test (pull_request) Successful in 1m12s
Test / test (pull_request) Successful in 1m12s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GT37Z1Xtfd9pUuQtMTg6Yt
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
cae116b50c
commit
809a6bdfe8
@@ -114,6 +114,73 @@ pub async fn store_answer(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Whose a record is, on an alternative that keeps one per person
|
||||
/// (`one_per_person`): the signed-in person's username, written by the
|
||||
/// server into the record's fields, never taken from the form.
|
||||
pub const OWNER_FIELD: &str = "by";
|
||||
|
||||
/// Every record in `bucket` that belongs to `username`, newest first.
|
||||
#[cfg(feature = "ssr")]
|
||||
pub async fn own_records(js: &async_nats::jetstream::Context, bucket: &str, username: &str) -> Vec<Answer> {
|
||||
use futures::TryStreamExt;
|
||||
let Ok(store) = js.get_key_value(bucket).await else { return Vec::new() };
|
||||
let keys: Vec<String> = match store.keys().await {
|
||||
Ok(keys) => keys.try_collect().await.unwrap_or_default(),
|
||||
Err(_) => return Vec::new(),
|
||||
};
|
||||
let mut own = Vec::new();
|
||||
for key in keys {
|
||||
let Ok(Some(bytes)) = store.get(&key).await else { continue };
|
||||
let Ok(answer) = serde_json::from_slice::<Answer>(&bytes) else { continue };
|
||||
if answer.responses.get(OWNER_FIELD).and_then(|v| v.as_str()) == Some(username) {
|
||||
own.push(answer);
|
||||
}
|
||||
}
|
||||
own.sort_by(|a, b| (b.submitted_ms, &b.id).cmp(&(a.submitted_ms, &a.id)));
|
||||
own
|
||||
}
|
||||
|
||||
/// A person keeps one card: of their records still in the bucket's
|
||||
/// first state, only the newest stays in the list, and the rest leave
|
||||
/// it (the event log keeps them). Run after every store, so two sends
|
||||
/// racing each other - a double click - both settle on the same one.
|
||||
#[cfg(feature = "ssr")]
|
||||
pub async fn keep_newest_own(js: &async_nats::jetstream::Context, bucket: &str, initial: &str, username: &str) {
|
||||
let own = own_records(js, bucket, username).await;
|
||||
let Ok(store) = js.get_key_value(bucket).await else { return };
|
||||
for older in own.iter().filter(|a| a.state == initial).skip(1) {
|
||||
if let Err(e) = store.delete(&older.id).await {
|
||||
tracing::error!(bucket, id = %older.id, error = %e, "could not retire an older own record");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills the form of every `one_per_person` alternative with what the
|
||||
/// signed-in person's own newest record says, so sending it again is
|
||||
/// editing their card. A file field shows the file already there, at
|
||||
/// the address a list would open it from, and may be left empty to
|
||||
/// keep it.
|
||||
#[cfg(feature = "ssr")]
|
||||
pub async fn fill_own(js: &async_nats::jetstream::Context, question: &mut crate::content::Question, user: Option<&crate::auth::User>) {
|
||||
let Some(user) = user else { return };
|
||||
for alt in &mut question.alternatives {
|
||||
let (true, Some(bucket)) = (alt.one_per_person, alt.record_as.clone()) else { continue };
|
||||
let Some(mine) = own_records(js, &bucket, &user.username).await.into_iter().next() else { continue };
|
||||
for req in alt.features.iter_mut().flat_map(|f| f.requirements.iter_mut()) {
|
||||
if req.kind == "hidden" {
|
||||
continue;
|
||||
}
|
||||
let Some(value) = mine.responses.get(&req.name).and_then(|v| v.as_str()).filter(|v| !v.trim().is_empty()) else { continue };
|
||||
if req.kind == "file" {
|
||||
req.value = Some(format!("/attachment/{bucket}/{}/{}", mine.id, req.name));
|
||||
req.optional = true;
|
||||
} else {
|
||||
req.value = Some(value.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// One selected row's pending transition. `feature_name` is per-item
|
||||
/// so a batch can span more than one resource feature on the same
|
||||
/// alternative.
|
||||
|
||||
Reference in New Issue
Block a user