caracal 0.4.3

Nostr client for Gemini
use super::route_prelude::*;
use std::collections::HashSet;

#[derive(Template, Clone)]
#[template(path = "nostriches_list.gmi", escape = "txt")]
struct UserListTemplate {
    meta_events: Vec<Event>,
}

#[derive(Template, Clone)]
#[template(path = "nostriches_search.gmi", escape = "txt")]
struct UserSearchTemplate {
    meta_events: Events,
}

pub async fn nostriches_list_all(
    _ctx: RouteContext,
    user: &'static mut CaracalUser,
) -> Response {
    let meta_filter = Filter::new().kind(Kind::Metadata);

    let Ok(meta_events) = user.client.database().query(meta_filter).await
    else {
        return Response::temporary_failure("db");
    };

    Response::success(WindTemplate::render(UserListTemplate {
        meta_events: meta_events.into_iter().collect(),
    }))
}

pub async fn nostriches_list_recent(
    _ctx: RouteContext,
    user: &'static mut CaracalUser,
) -> Response {
    let recent_notes_filter = Filter::new()
        .kind(Kind::TextNote)
        .since(Timestamp::now() - duration_str::parse("1w").unwrap());
    let meta_filter = Filter::new().kind(Kind::Metadata);

    let Ok(events) = user.fetch_quick(recent_notes_filter).await else {
        return Response::temporary_failure("ev");
    };
    let recent_pubkeys: HashSet<PublicKey> =
        events.into_iter().map(|event| event.pubkey).collect();

    let Ok(metas) = user.client.database().query(meta_filter).await else {
        return Response::temporary_failure("db");
    };

    let meta_events: Vec<Event> = metas
        .into_iter()
        .filter(|ev| recent_pubkeys.contains(&ev.pubkey))
        .collect();

    Response::success(WindTemplate::render(UserListTemplate { meta_events }))
}

pub async fn nostriches_search(
    ctx: RouteContext,
    user: &'static mut CaracalUser,
) -> Response {
    let Some(input) = ctx.url.query() else {
        return Response::input(t!("input_nostr_name"));
    };
    let Ok(input) = urlencoding::decode(input) else {
        return Response::input(t!("input_nostr_name"));
    };

    let meta_filter = Filter::new().kind(Kind::Metadata);

    let Ok(all_meta) = user.client.database().query(meta_filter).await else {
        return Response::temporary_failure("db");
    };

    let events = all_meta
        .iter()
        .filter_map(|ev| {
            if let Ok(meta) = Metadata::try_from(ev) {
                Some((ev, meta))
            } else {
                None
            }
        })
        .filter_map(|(ev, meta)| {
            if let Some(name) = meta.name
                && name.contains(&input.to_string())
            {
                Some(ev.clone())
            } else {
                None
            }
        });

    Response::success(WindTemplate::render(UserSearchTemplate {
        meta_events: Events::from_iter(events),
    }))
}