mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2025-01-04 15:53:30 +03:00
feat: add event emission for virtual branches after certain actions to keep UI in sync
This commit is contained in:
parent
eda172ac19
commit
d7306d4cd9
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
@ -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");
|
||||
|
52
packages/tauri/src/watcher/handlers/vbranch_handler.rs
Normal file
52
packages/tauri/src/watcher/handlers/vbranch_handler.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
34
packages/ui/src/lib/vbranches/experiment.ts
Normal file
34
packages/ui/src/lib/vbranches/experiment.ts
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user