Skip to content
Merged
2 changes: 1 addition & 1 deletion backend/app/src/fswatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub fn setup_watchers<P: AsRef<std::path::Path>>(
delay,
move |res: Result<Vec<DebouncedEvent>, notify::Error>| match res {
Ok(evs) => {
log::info!("Changes detected in watched paths");
log::debug!("Detected changes in watched paths");
let _ = tx.send(evs);
}
Err(e) => {
Expand Down
24 changes: 23 additions & 1 deletion backend/app/src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ where
let (_debouncer, fs_rx) = fswatcher::setup_watchers(&paths, std::time::Duration::from_secs(1));

tokio::spawn(async move {
let mut previous_status: Option<git2_ox::Status> = None;
loop {
let res = fs_rx.recv();
match res {
Expand All @@ -149,7 +150,28 @@ where
let actor = app_state.git_actor();
let msg = actors::git::GetRepositoryStatus;
let status = actor.call(msg).await.unwrap().unwrap();
let _ = app_state.git_status_tx().send(status);
log::debug!(
"Received FS event: worktree {:?}, index: {:?}",
status.worktree(),
status.index()
);

let status_changed = match previous_status {
None => true,
Some(ref s) => *s != status,
};

if status_changed {
previous_status = Some(status.clone());
let tx = app_state.git_status_tx();
let num_rx = tx.receiver_count();
match tx.send(status) {
Ok(_) => log::debug!("Sent git status to channel ({num_rx})"),
Err(e) => {
log::error!("Error sending status to channel ({num_rx}): {e}")
}
}
}
}
Err(errs) => {
log::error!("watch error: {errs:?}")
Expand Down
7 changes: 5 additions & 2 deletions backend/app/src/web/api/v1/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,12 +373,14 @@ async fn repository_status_sse_handler(
State(state): State<web::AppState>,
) -> response::sse::Sse<impl Stream<Item = Result<response::sse::Event, std::convert::Infallible>>>
{
log::info!("Received SSE request");
let tx = state.git_status_tx();
let rx = tx.subscribe();
// wrap into a stream and map to SSE Events
let stream = tokio_stream::wrappers::BroadcastStream::new(rx).filter_map(|msg| async move {
match msg {
Ok(payload) => {
log::info!("Received Git status from channel");
let ev = response::sse::Event::default()
.event("git-status")
.json_data(payload)
Expand All @@ -387,7 +389,8 @@ async fn repository_status_sse_handler(

Some(Ok(ev))
}
Err(_) => {
Err(e) => {
log::error!("{e}");
// drop silently; next message will arrive
None
}
Expand All @@ -399,7 +402,7 @@ async fn repository_status_sse_handler(
if let Ok(Ok(status)) = actor.call(msg).await {
let _ = tx
.send(status)
.inspect_err(|e| log::error!("Error sending initial git status: {e}"));
.inspect_err(|e| log::error!("Error sending initial Git status: {e}"));
}

response::sse::Sse::new(stream).keep_alive(
Expand Down
7 changes: 3 additions & 4 deletions backend/git2-ox/src/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub trait CommitProperties {
derive(serde::Serialize),
serde(rename_all = "camelCase")
)]
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
#[allow(dead_code)]
struct Signature {
name: String,
Expand Down Expand Up @@ -43,8 +43,7 @@ impl From<Git2Time> for chrono::DateTime<chrono::Utc> {
derive(serde::Serialize),
serde(rename_all = "camelCase")
)]
#[derive(Clone, Debug)]
#[allow(dead_code)]
#[derive(Clone, Debug, PartialEq)]
pub struct Commit {
id: String,
summary: String,
Expand Down Expand Up @@ -105,7 +104,7 @@ impl<'repo> Commit {
derive(serde::Serialize),
serde(rename_all = "camelCase")
)]
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
#[allow(dead_code)]
pub struct CommitWithReferences {
#[cfg_attr(feature = "serde", serde(flatten))]
Expand Down
3 changes: 1 addition & 2 deletions backend/git2-ox/src/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ impl<'repo> TryFrom<git2::Reference<'repo>> for ReferenceKind {
derive(serde::Serialize),
serde(rename_all = "camelCase")
)]
#[derive(Clone, Debug)]
#[allow(dead_code)]
#[derive(Clone, Debug, PartialEq)]
pub struct ReferenceMetadata {
name: String,
kind: ReferenceKind,
Expand Down
4 changes: 2 additions & 2 deletions backend/git2-ox/src/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type Files = Vec<String>;
derive(serde::Serialize),
serde(rename_all = "camelCase")
)]
#[derive(Default, Debug, Clone)]
#[derive(Default, Debug, Clone, PartialEq)]
pub struct TreeStatus {
/// Added files
new_files: Files,
Expand Down Expand Up @@ -48,7 +48,7 @@ impl TreeStatus {
derive(serde::Serialize),
serde(rename_all = "camelCase")
)]
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct Status {
/// Name of the current branch, not set if `is_detached_head` is true
current_branch: Option<String>,
Expand Down
30 changes: 18 additions & 12 deletions frontend/src/components/status-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { notify } from "@/lib/notify";
import { TypedEventSource } from "@/lib/sse";
import { cn } from "@/lib/utils";
import { useStore, useUiStore } from "@/store";
Expand Down Expand Up @@ -82,6 +83,7 @@ const GitStatusFileList = ({
{paths.map((path) => {
return (
<div
key={path}
className={cn(
"flex items-center gap-1 pl-1 font-mono text-sm",
className,
Expand Down Expand Up @@ -298,33 +300,37 @@ export const StatusBar = ({
const [repositoryStatus, setRepositoryStatus] =
React.useState<null | RepositoryStatus>(null);

React.useEffect(() => {
// // First fetch the repository status when the component is mounted
// fetchRepositoryStatus()
// .then((data) => {
// setRepositoryStatus(data);
// })
// .catch((err: unknown) => {
// notify.error(err);
// });
const [eventSource, setEventSource] =
React.useState<TypedEventSource<GitStatusEventMap> | null>(null);

const connect = () => {
const es = new TypedEventSource<GitStatusEventMap>(
"/api/v1/git/repository/status/stream",
);
es.onOpen(() => {
_logger.info("SSE connection established");
});
es.onError(() => {
_logger.warn("SSE connection lost");
if (es.isClosed()) {
_logger.warn("SSE connection lost");
notify.error("Event source connection lost");
}
});
es.on("git-status", (ev) => {
console.log("new repository status", ev.data);
_logger.info("new repository status", ev.data);
setRepositoryStatus(ev.data); // prepend latest
});
setEventSource(es);
};

React.useEffect(() => {
connect();
return () => {
es.close();
if (eventSource) {
eventSource.close();
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

if (repositoryStatus === null) {
Expand Down