caracal 0.2.0

Nostr client for Gemini
use nostr_sdk::prelude::*;
use windmark_titanesque::{context::RouteContext, response::Response};

use urlencoding::decode;

use crate::aska::WindTemplate;
use crate::nostru;
use crate::routes::MyProfileTemplate;
use crate::routes::{resp_invalid_params, resp_nocert, ugate};
use crate::util::titan96;

pub async fn profile_init_reset(ctx: RouteContext) -> Response {
    let Some(user) = ugate(ctx.certificate).await else {
        return resp_nocert();
    };

    let default_meta = Metadata::new();

    match user.client.set_metadata(&default_meta).await {
        Ok(_event_id) => Response::temporary_redirect("/profile"),
        Err(_error) => Response::temporary_failure("Error"),
    }
}

pub async fn profile_index(ctx: RouteContext) -> Response {
    let url = ctx.url.clone();

    let Some(user) = ugate(ctx.certificate).await else {
        return resp_nocert();
    };

    let pubk = user.signer.get_public_key().await.unwrap();
    let npub = pubk.to_bech32().unwrap();

    user.client.connect().await;

    let usermeta = nostru::fetch_user_metadata(
        &user.client,
        user.signer.get_public_key().await.unwrap(),
    )
    .await;

    let template = MyProfileTemplate {
        url,
        npub,
        metadata: usermeta,
    };

    Response::success(WindTemplate::render(template))
}

pub async fn profile_edit(ctx: RouteContext) -> Response {
    let Some(user) = ugate(ctx.certificate).await else {
        return resp_nocert();
    };

    let attr = ctx.parameters.get("attr").unwrap();

    let Some(value) = ctx.url.query() else {
        return Response::input(format!("Type in the value for: {}", attr));
    };

    let value = decode(value).unwrap();

    let Some(mut metadata) = nostru::fetch_user_metadata(
        &user.client,
        user.signer.get_public_key().await.unwrap(),
    )
    .await
    else {
        return resp_invalid_params();
    };

    match attr.as_str() {
        "about" => {
            metadata.about = Some(value.into());
        }
        "name" => {
            metadata.name = Some(value.into());
        }
        "nip05" => {
            metadata.nip05 = Some(value.into());
        }
        "lud06" => {
            metadata.lud06 = Some(value.into());
        }
        "lud16" => {
            metadata.lud16 = Some(value.into());
        }
        "display_name" => {
            metadata.display_name = Some(value.into());
        }
        "website" => {
            if let Ok(url) = Url::parse(&value) {
                metadata.website = Some(url.to_string());
            }
        }
        "picture" => {
            if let Ok(url) = Url::parse(&value) {
                metadata.picture = Some(url.to_string());
            }
        }
        "banner" => {
            if let Ok(url) = Url::parse(&value) {
                metadata.banner = Some(url.to_string());
            }
        }
        &_ => todo!(),
    }

    user.client.connect().await;

    match user.client.set_metadata(&metadata).await {
        Ok(_event_id) => Response::permanent_redirect("/profile"),
        Err(_error) => Response::permanent_failure("Error updating metadata"),
    }
}

pub async fn profile_upload(ctx: RouteContext) -> Response {
    let Some(user) = ugate(ctx.certificate).await else {
        return resp_nocert();
    };

    if let Some(up_url) = titan96(ctx.titan_rsc, &user.upload_keys).await {
        let Some(metadata) = nostru::fetch_user_metadata(
            &user.client,
            user.signer.get_public_key().await.unwrap(),
        )
        .await
        else {
            return resp_invalid_params();
        };

        let metadata = metadata.picture(up_url);

        match user.client.set_metadata(&metadata).await {
            Ok(_event_id) => Response::temporary_redirect(format!(
                "gemini://{}/profile",
                ctx.url.authority()
            )),
            Err(_error) => {
                Response::permanent_failure("Error updating metadata")
            }
        }
    } else {
        Response::temporary_failure("No file")
    }
}