edc-web-components 0.8.1

A Rust Web Components with patternfly-yew client for EDC
Documentation
use crate::contexts::use_edc_connector_context;
use edc_connector_client::types::contract_negotiation::{
  ContractNegotiation, ContractNegotiationState,
};
use patternfly_yew::prelude::*;
use std::time::Duration;
use yew::platform::spawn_local;
use yew::platform::time::sleep;
use yew::prelude::*;

const HAPPY_PATH_STATES: [(&str, ContractNegotiationState); 7] = [
  ("Initial", ContractNegotiationState::Initial),
  ("Requested", ContractNegotiationState::Requested),
  ("Offered", ContractNegotiationState::Offered),
  ("Accepted", ContractNegotiationState::Accepted),
  ("Agreed", ContractNegotiationState::Agreed),
  ("Verified", ContractNegotiationState::Verified),
  ("Finalized", ContractNegotiationState::Finalized),
];

#[derive(Clone, Debug, PartialEq, Properties)]
pub struct ContractNegotiationStatusProps {
  pub contract_negotiation_id: String,
  pub on_finalized: Callback<()>,
}

#[component]
pub fn ContractNegotiationStatus(props: &ContractNegotiationStatusProps) -> Html {
  let edc_connector_client = use_edc_connector_context();

  let contract_negotiation_state = use_state(|| None);
  let contract_negotiation_error = use_state(|| None);

  use_effect_with(
    (
      props.contract_negotiation_id.clone(),
      edc_connector_client.clone(),
      contract_negotiation_state.setter(),
      contract_negotiation_error.setter(),
      props.on_finalized.clone(),
    ),
    |(
      contract_negotiation_id,
      edc_connector_client,
      contract_negotiation_state_setter,
      contract_negotiation_error_setter,
      on_finalized,
    )| {
      let contract_negotiation_id = contract_negotiation_id.clone();
      let edc_connector_client = edc_connector_client.clone();
      let contract_negotiation_state_setter = contract_negotiation_state_setter.clone();
      let contract_negotiation_error_setter = contract_negotiation_error_setter.clone();
      let on_finalized = on_finalized.clone();

      spawn_local(async move {
        loop {
          let contract_negotiation = if let Some(client) = edc_connector_client.get_client() {
            client
              .contract_negotiations(edc_connector_client::EdcConnectorApiVersion::V4)
              .get(&contract_negotiation_id)
              .await
              .ok()
          } else {
            None
          };

          let state = contract_negotiation
            .as_ref()
            .map(|contract_negotiation| contract_negotiation.state().clone());

          let error_message = contract_negotiation
            .as_ref()
            .and_then(|contract_negotiation| {
              contract_negotiation
                .error_detail()
                .map(|error_detail| error_detail.to_string())
            });

          contract_negotiation_error_setter.set(error_message);

          contract_negotiation_state_setter.set(contract_negotiation);

          if state == Some(ContractNegotiationState::Finalized)
            || state == Some(ContractNegotiationState::Terminated)
          {
            on_finalized.emit(());
            break;
          }

          sleep(Duration::from_secs(1)).await;
        }
      });
    },
  );

  match (
    (*contract_negotiation_error).clone(),
    (*contract_negotiation_state).clone(),
  ) {
    (Some(error_message), _) => html!(
      <Alert title="Negotiation failed" r#type={AlertType::Danger}>
        <div>{ error_message }</div>
      </Alert>
    ),
    (None, Some(contract_negotiation)) => {
      let contract_negotiation: ContractNegotiation = contract_negotiation;

      let current_state_index = HAPPY_PATH_STATES
        .iter()
        .position(|(_, state)| state == contract_negotiation.state())
        .unwrap_or_default();

      let steps = HAPPY_PATH_STATES
        .iter()
        .enumerate()
        .map(|(index, (state_label, _))| {
          let status = if index < current_state_index {
            ProgressStepperStepStatus::Success
          } else if index == current_state_index {
            if index == HAPPY_PATH_STATES.len() - 1 {
              ProgressStepperStepStatus::Success
            } else {
              ProgressStepperStepStatus::Default
            }
          } else {
            ProgressStepperStepStatus::Pending
          };

          html_nested!(
            <ProgressStepperStep {status}>
              <div>{ state_label.to_string() }</div>
            </ProgressStepperStep>
          )
        });

      html!(<ProgressStepper>{ for steps }</ProgressStepper>)
    }
    _ => html!(),
  }
}