Skip to content

Proposal: Event tracking #530

@ManevilleF

Description

@ManevilleF

In our crux app we quickly got the need for the shell to know when an Event is finished, and potentially get an output from it.

A typical example would be the shell sending a SyncWithRemote event which the core process by doing a bunch of database and http calls.
The core could build a state machine in the view model to expose the progress with a value like sync: Pending/Done but it doesn't scale well and view models can get pretty complex.

So we built a system similar to the following:

pub enum Event {
   Init,
   SyncWithRemote,
   DoSomething
}

pub struct TrackedEvent {
  pub event: Event,
  pub id: Option<String>
}

the App::Event is actually TrackedEvent.

We added a Track capability, able to notify the shell with the following operations:

// Status of a tracked event
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, facet::Facet)]
#[repr(C)]
pub enum OperationStatus {
    /// The event was started by the core
    Started,
    /// The event was successful
    Success,
    /// The event failed -- error message
    Error(String),
}

/// Notify the shell of the status of a tracked event
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, facet::Facet)]
#[repr(C)]
pub struct TrackOperation {
    /// Identifier of the tracked event
    pub id: String,
    /// New status of that event
    pub status: OperationStatus,
}

The simplified logic is that if the shell specifies an TrackEvent::id we are going to wrap the associated Command with some tracking

Command::new(|ctx| {
  ctx.notify_shell(TrackOperation { id, status: OperatioStatus::Started });
  let result = {
      // The actual event processing return a `Result`
  }.await;
  match result {
      Ok(()) -> ctx.notify_shell(TrackOperation { id, status: OperationStatus::Success }),
      Err(e) -> ctx.notify_shell(TrackOperation { id, status: OperationStatus::Error(e.to_string()) })
  } 
}

This way the shell is notified when:

  • The core actually starts polling the associated Command
  • The core finishes processing and gives a status

Concretely it allows many things for the shell:

  • Display loading screens
  • Show sucess toasts
  • Given context (The original user instruction) to failures instead of a simple error capability.

This works well as long as the shell has to specify unique ids itself based on an Event + some entropy to avoid duplicates but we can go even further by adding data to the Success variant.

Doing so unlocks some cool features:

  • Being able to query the core without having to be based on the view model
  • Returning relevant data

An example could be that for a calendar app the Event::CreateCalendarItem could return the actual new calendar item unique id and avoid for the shell the necessity to do some comparisons between the previous view model and the new one to find the "new" item that was created
This goes even further if we are in a workspace environment with potentially multiple new calendar items popping up. The shell is often confused on what is a result of an instruction it sent and what is coming from something else

I think crux could benefit from having a tracking mechanism built-in, would that make sense ? If so I could work on an RFC for that

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions