feat: add event emission for virtual branches after certain actions to keep UI in sync

This commit is contained in:
Kiril Videlov 2023-11-16 12:16:10 +01:00 committed by Kiril Videlov
parent eda172ac19
commit d7306d4cd9
7 changed files with 191 additions and 31 deletions

View File

@ -6,6 +6,7 @@ use crate::{
projects::ProjectId,
reader,
sessions::{self, SessionId},
virtual_branches,
};
#[derive(Clone)]
@ -126,4 +127,17 @@ impl Event {
project_id: *project_id,
}
}
pub fn virtual_branches(
project_id: &ProjectId,
virtual_branches: &Vec<virtual_branches::VirtualBranch>,
) -> Self {
Event {
name: format!("project://{}/virtual-branches", project_id),
payload: serde_json::json!({
"virtualBranches": virtual_branches,
}),
project_id: *project_id,
}
}
}

View File

@ -1,3 +1,4 @@
use crate::watcher;
use anyhow::Context;
use tauri::{AppHandle, Manager};
use tracing::instrument;
@ -5,7 +6,7 @@ use tracing::instrument;
use crate::{
assets,
error::{Code, Error},
git,
git, projects,
};
use super::{
@ -57,11 +58,13 @@ pub async fn commit_virtual_branch(
code: Code::Validation,
message: "Malformed ownership".to_string(),
})?;
handle
let result = handle
.state::<Controller>()
.create_commit(&project_id, &branch_id, message, ownership.as_ref())
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
#[tauri::command(async)]
@ -95,11 +98,13 @@ pub async fn create_virtual_branch(
code: Code::Validation,
message: "Malformed project id".to_string(),
})?;
handle
let result = handle
.state::<Controller>()
.create_virtual_branch(&project_id, &branch)
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
#[tauri::command(async)]
@ -113,11 +118,13 @@ pub async fn create_virtual_branch_from_branch(
code: Code::Validation,
message: "Malformed project id".to_string(),
})?;
handle
let result = handle
.state::<Controller>()
.create_virtual_branch_from_branch(&project_id, &branch)
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
#[tauri::command(async)]
@ -135,11 +142,13 @@ pub async fn merge_virtual_branch_upstream(
code: Code::Validation,
message: "Malformed branch id".to_string(),
})?;
handle
let result = handle
.state::<Controller>()
.merge_virtual_branch_upstream(&project_id, &branch_id)
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
#[tauri::command(async)]
@ -185,6 +194,7 @@ pub async fn set_base_branch(
.state::<assets::Proxy>()
.proxy_base_branch(base_branch)
.await;
emit_vbranches(handle, &project_id);
Ok(base_branch)
}
@ -195,11 +205,13 @@ pub async fn update_base_branch(handle: AppHandle, project_id: &str) -> Result<(
code: Code::Validation,
message: "Malformed project id".into(),
})?;
handle
let result = handle
.state::<Controller>()
.update_base_branch(&project_id)
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
#[tauri::command(async)]
@ -213,11 +225,13 @@ pub async fn update_virtual_branch(
code: Code::Validation,
message: "Malformed project id".to_string(),
})?;
handle
let result = handle
.state::<Controller>()
.update_virtual_branch(&project_id, branch)
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
#[tauri::command(async)]
@ -235,11 +249,13 @@ pub async fn delete_virtual_branch(
code: Code::Validation,
message: "Malformed branch id".to_string(),
})?;
handle
let result = handle
.state::<Controller>()
.delete_virtual_branch(&project_id, &branch_id)
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
#[tauri::command(async)]
@ -253,11 +269,13 @@ pub async fn apply_branch(handle: AppHandle, project_id: &str, branch: &str) ->
code: Code::Validation,
message: "Malformed branch id".to_string(),
})?;
handle
let result = handle
.state::<Controller>()
.apply_virtual_branch(&project_id, &branch_id)
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
#[tauri::command(async)]
@ -275,11 +293,13 @@ pub async fn unapply_branch(
code: Code::Validation,
message: "Malformed branch id".to_string(),
})?;
handle
let result = handle
.state::<Controller>()
.unapply_virtual_branch(&project_id, &branch_id)
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
#[tauri::command(async)]
@ -297,11 +317,13 @@ pub async fn unapply_ownership(
code: Code::Validation,
message: "Malformed ownership".to_string(),
})?;
handle
let result = handle
.state::<Controller>()
.unapply_ownership(&project_id, &ownership)
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
#[tauri::command(async)]
@ -320,11 +342,13 @@ pub async fn push_virtual_branch(
code: Code::Validation,
message: "Malformed branch id".to_string(),
})?;
handle
let result = handle
.state::<Controller>()
.push_virtual_branch(&project_id, &branch_id, with_force)
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
#[tauri::command(async)]
@ -410,11 +434,13 @@ pub async fn reset_virtual_branch(
code: Code::Validation,
message: "Malformed commit oid".to_string(),
})?;
handle
let result = handle
.state::<Controller>()
.reset_virtual_branch(&project_id, &branch_id, target_commit_oid)
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
#[tauri::command(async)]
@ -437,11 +463,13 @@ pub async fn cherry_pick_onto_virtual_branch(
code: Code::Validation,
message: "Malformed commit oid".to_string(),
})?;
handle
let result = handle
.state::<Controller>()
.cherry_pick(&project_id, &branch_id, target_commit_oid)
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
#[tauri::command(async)]
@ -464,11 +492,13 @@ pub async fn amend_virtual_branch(
code: Code::Validation,
message: "Malformed ownership".into(),
})?;
handle
let result = handle
.state::<Controller>()
.amend(&project_id, &branch_id, &ownership)
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
#[tauri::command(async)]
@ -511,9 +541,25 @@ pub async fn squash_branch_commit(
code: Code::Validation,
message: "Malformed commit oid".into(),
})?;
handle
let result = handle
.state::<Controller>()
.squash(&project_id, &branch_id, target_commit_oid)
.await
.map_err(Into::into)
.map_err(Into::into);
emit_vbranches(handle, &project_id);
result
}
fn emit_vbranches(handle: AppHandle, project_id: &projects::ProjectId) {
match futures::executor::block_on(async {
handle
.state::<watcher::Watchers>()
.post(watcher::Event::VirtualBranch(*project_id))
.await
}) {
Ok(()) => {}
Err(error) => {
tracing::error!(?error);
}
}
}

View File

@ -30,6 +30,8 @@ pub enum Event {
Emit(events::Event),
Analytics(analytics::Event),
VirtualBranch(ProjectId),
}
impl Event {
@ -48,6 +50,7 @@ impl Event {
| Event::Session(project_id, _)
| Event::SessionFile((project_id, _, _, _))
| Event::SessionDelta((project_id, _, _, _))
| Event::VirtualBranch(project_id)
| Event::PushGitbutlerData(project_id)
| Event::PushProjectToGitbutler(project_id) => project_id,
}
@ -88,6 +91,7 @@ impl Display for Event {
delta.timestamp_ms
)
}
Event::VirtualBranch(pid) => write!(f, "VirtualBranch({})", pid),
Event::PushGitbutlerData(pid) => write!(f, "PushGitbutlerData({})", pid),
Event::PushProjectToGitbutler(pid) => write!(f, "PushProjectToGitbutler({})", pid),
Event::IndexAll(pid) => write!(f, "IndexAll({})", pid),

View File

@ -8,6 +8,7 @@ mod project_file_change;
mod push_gitbutler_data;
mod push_project_to_gitbutler;
mod tick_handler;
mod vbranch_handler;
use std::time;
@ -31,6 +32,7 @@ pub struct Handler {
analytics_handler: analytics_handler::Handler,
index_handler: index_handler::Handler,
push_project_to_gitbutler: push_project_to_gitbutler::Handler,
virtual_branch_handler: vbranch_handler::Handler,
events_sender: app_events::Sender,
}
@ -51,6 +53,7 @@ impl TryFrom<&AppHandle> for Handler {
fetch_gitbutler_handler: fetch_gitbutler_data::Handler::try_from(value)?,
analytics_handler: analytics_handler::Handler::from(value),
push_project_to_gitbutler: push_project_to_gitbutler::Handler::try_from(value)?,
virtual_branch_handler: vbranch_handler::Handler::try_from(value)?,
})
}
}
@ -128,6 +131,11 @@ impl Handler {
))])
}
events::Event::VirtualBranch(project_id) => self
.virtual_branch_handler
.handle(project_id)
.context("failed to handle virtual branch event"),
events::Event::Emit(event) => {
self.events_sender
.send(event)

View File

@ -173,6 +173,8 @@ impl Handler {
path.to_path_buf(),
new_delta.clone(),
)),
// TODO: extract all the sessions stuff in a separate event handler
events::Event::VirtualBranch(*project_id),
])
} else {
tracing::debug!(%project_id, path = %path.display(), "no new deltas, ignoring");

View File

@ -0,0 +1,52 @@
use anyhow::{anyhow, Result};
use tauri::{AppHandle, Manager};
use super::events;
use crate::events as app_events;
use crate::{assets, projects::ProjectId, virtual_branches};
#[derive(Clone)]
pub struct Handler {
vbranch_controller: virtual_branches::Controller,
assets_proxy: assets::Proxy,
}
impl TryFrom<&AppHandle> for Handler {
type Error = anyhow::Error;
fn try_from(value: &AppHandle) -> std::result::Result<Self, Self::Error> {
Ok(Self {
vbranch_controller: value
.state::<virtual_branches::Controller>()
.inner()
.clone(),
assets_proxy: value.state::<assets::Proxy>().inner().clone(),
})
}
}
impl Handler {
pub fn handle(&self, project_id: &ProjectId) -> Result<Vec<events::Event>> {
let branches = futures::executor::block_on(async {
self.vbranch_controller
.list_virtual_branches(project_id)
.await
});
let branches = match branches {
Ok(branches) => {
let branches = futures::executor::block_on(async {
self.assets_proxy.proxy_virtual_branches(branches).await
});
Ok(branches)
}
Err(error) => Err(anyhow!(error)),
};
match branches {
Ok(branches) => Ok(vec![events::Event::Emit(
app_events::Event::virtual_branches(project_id, &branches.clone()),
)]),
Err(error) => Err(error),
}
}
}

View File

@ -0,0 +1,34 @@
import { invoke, listen } from '$lib/backend/ipc';
import { plainToInstance } from 'class-transformer';
import { Branch } from './types';
import { Observable, from, concat, shareReplay } from 'rxjs';
export function vbExperiment(projectId: string) {
return concat(
from(listVirtualBranches({ projectId })),
new Observable<Branch[]>((subscriber) => {
return subscribeToVirtualBranches(projectId, (branches) => subscriber.next(branches));
})
).pipe(shareReplay(1));
}
function subscribeToVirtualBranches(projectId: string, callback: (branches: Branch[]) => void) {
return listen<Branch[]>(`project://${projectId}/virtual-branches`, (event) => {
console.log('GOT VBRANCH', event.payload);
callback(event.payload);
});
}
// Also: lets not attach file content to the branche eagerly, at least not until its needed
async function listVirtualBranches(params: { projectId: string }): Promise<Branch[]> {
const result = plainToInstance(Branch, await invoke<any[]>('list_virtual_branches', params));
result.forEach((branch) => {
branch.files.sort((a) => (a.conflicted ? -1 : 0));
// this is somehow pointless
branch.isMergeable = invoke<boolean>('can_apply_virtual_branch', {
projectId: params.projectId,
branchId: branch.id
});
});
return result;
}