Merge pull request #4682 from gitbutlerapp/Provide-current-mode-to-frontend

Provide current mode to frontend
This commit is contained in:
Caleb Owens 2024-08-14 10:02:37 +02:00 committed by GitHub
commit 83283d918c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 99 additions and 46 deletions

2
Cargo.lock generated
View File

@ -2276,6 +2276,7 @@ dependencies = [
"git2",
"gitbutler-command-context",
"gitbutler-fs",
"gitbutler-project",
"gitbutler-reference",
"gitbutler-serde",
"gitbutler-testsupport",
@ -2447,6 +2448,7 @@ dependencies = [
"gitbutler-error",
"gitbutler-feedback",
"gitbutler-id",
"gitbutler-operating-modes",
"gitbutler-oplog",
"gitbutler-project",
"gitbutler-reference",

View File

@ -1,30 +0,0 @@
import { invoke, listen } from '$lib/backend/ipc';
import { derived, writable } from 'svelte/store';
export class HeadService {
readonly head = writable<string>(undefined, () => {
this.refresh();
this.unsubscribe = subscribeToHead(this.projectId, (head) => this.head.set(head));
return () => {
this.unsubscribe?.();
};
});
readonly gbBranchActive = derived(this.head, (head) => head === 'gitbutler/integration');
unsubscribe?: () => Promise<void>;
constructor(private projectId: string) {}
private async refresh() {
let head = await invoke<string>('git_head', { projectId: this.projectId });
head = head.replace('refs/heads/', '');
this.head.set(head);
}
}
function subscribeToHead(projectId: string, callback: (head: string) => void) {
return listen<{ head: string }>(`project://${projectId}/git/head`, (event) =>
callback(event.payload.head.replace('refs/heads/', ''))
);
}

View File

@ -0,0 +1,36 @@
import { invoke, listen } from '$lib/backend/ipc';
import { derived, writable } from 'svelte/store';
type Mode = { type: 'OpenWorkspace' } | { type: 'OutsideWorksapce' } | { type: 'Edit' };
export class ModeService {
readonly head = writable<string | undefined>(undefined);
readonly mode = writable<Mode | undefined>(undefined);
readonly gbBranchActive = derived(this.head, (head) => head === 'gitbutler/integration');
unsubscribe?: () => Promise<void>;
constructor(private projectId: string) {
this.unsubscribe = subscribeToHead(projectId, (head, mode) => {
this.head.set(head);
this.mode.set(mode);
});
this.refresh();
}
private async refresh() {
const head = await invoke<string>('git_head', { projectId: this.projectId });
this.head.set(head);
const mode = await invoke<Mode>('operating_mode', { projectId: this.projectId });
this.mode.set(mode);
}
}
function subscribeToHead(projectId: string, callback: (head: string, mode: Mode) => void) {
return listen<{ head: string; operating_mode: Mode }>(
`project://${projectId}/git/head`,
(event) => callback(event.payload.head, event.payload.operating_mode)
);
}

View File

@ -43,7 +43,7 @@
projectMetrics,
baseBranchService,
remoteBranchService,
headService,
modeService,
userService,
fetchSignal
} = $derived(data);
@ -96,8 +96,9 @@
});
// Refresh base branch if git fetch event is detected.
const head = $derived(headService.head);
const gbBranchActive = $derived($head === 'gitbutler/integration');
const mode = $derived(modeService.mode);
const head = $derived(modeService.head);
const openWorkspace = $derived($mode?.type === 'OpenWorkspace');
// We end up with a `state_unsafe_mutation` when switching projects if we
// don't use $effect.pre here.
@ -155,7 +156,10 @@
if (intervalId) clearInterval(intervalId);
}
onDestroy(() => clearFetchInterval());
onDestroy(() => {
clearFetchInterval();
modeService.unsubscribe?.();
});
</script>
<!-- forces components to be recreated when projectId changes -->
@ -176,7 +180,7 @@
<ProblemLoadingRepo error={$branchesError} />
{:else if $projectError}
<ProblemLoadingRepo error={$projectError} />
{:else if !gbBranchActive && $baseBranch}
{:else if !openWorkspace && $baseBranch}
<NotOnGitButlerBranch baseBranch={$baseBranch} />
{:else if $baseBranch}
<div class="view-wrap" role="group" ondragover={(e) => e.preventDefault()}>

View File

@ -5,9 +5,9 @@ import { BranchDragActionsFactory } from '$lib/branches/dragActions.js';
import { CommitDragActionsFactory } from '$lib/commits/dragActions.js';
import { ReorderDropzoneManagerFactory } from '$lib/dragging/reorderDropzoneManager';
import { FetchSignal } from '$lib/fetchSignal/fetchSignal.js';
import { HeadService } from '$lib/head/headService';
import { HistoryService } from '$lib/history/history';
import { ProjectMetrics } from '$lib/metrics/projectMetrics';
import { ModeService } from '$lib/modes/service';
import { RemoteBranchService } from '$lib/stores/remoteBranches';
import { BranchController } from '$lib/vbranches/branchController';
import { VirtualBranchService } from '$lib/vbranches/virtualBranch';
@ -45,7 +45,7 @@ export const load: LayoutLoad = async ({ params, parent }) => {
const projectMetrics = new ProjectMetrics(projectId);
const headService = new HeadService(projectId);
const modeService = new ModeService(projectId);
const fetchSignal = new FetchSignal(projectId);
const historyService = new HistoryService(projectId);
@ -86,7 +86,7 @@ export const load: LayoutLoad = async ({ params, parent }) => {
remoteBranchService,
vbranchService,
projectMetrics,
headService,
modeService,
fetchSignal,
// These observables are provided for convenience

View File

@ -15,6 +15,7 @@ gitbutler-command-context.workspace = true
gitbutler-serde.workspace = true
gitbutler-fs.workspace = true
gitbutler-reference.workspace = true
gitbutler-project.workspace = true
[dev-dependencies]
gitbutler-testsupport.workspace = true

View File

@ -0,0 +1,10 @@
use anyhow::Result;
use gitbutler_command_context::CommandContext;
use gitbutler_project::Project;
use crate::OperatingMode;
pub fn operating_mode(project: &Project) -> Result<OperatingMode> {
let ctx = CommandContext::open(project)?;
Ok(crate::operating_mode(&ctx))
}

View File

@ -5,6 +5,8 @@ use gitbutler_command_context::CommandContext;
use gitbutler_reference::ReferenceName;
use serde::{Deserialize, Serialize};
pub mod commands;
/// The reference the app will checkout when the workspace is open
pub const INTEGRATION_BRANCH_REF: &str = "refs/heads/gitbutler/integration";
/// The reference the app will checkout when in edit mode
@ -39,7 +41,7 @@ pub fn write_edit_mode_metadata(
}
/// Holds relevant state required to switch to and from edit mode
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct EditModeMetadata {
/// The sha of the commit getting edited.
#[serde(with = "gitbutler_serde::oid")]
@ -48,7 +50,8 @@ pub struct EditModeMetadata {
pub editee_branch: ReferenceName,
}
#[derive(PartialEq)]
#[derive(PartialEq, Debug, Clone, Serialize)]
#[serde(tag = "type", content = "subject")]
pub enum OperatingMode {
/// The typical app state when its on the gitbutler/integration branch
OpenWorkspace,

View File

@ -63,6 +63,7 @@ gitbutler-secret.workspace = true
gitbutler-id.workspace = true
gitbutler-storage.workspace = true
gitbutler-diff.workspace = true
gitbutler-operating-modes.workspace = true
open = "5"
[dependencies.tauri]

View File

@ -25,6 +25,7 @@ pub mod askpass;
pub mod config;
pub mod error;
pub mod github;
pub mod modes;
pub mod projects;
pub mod remotes;
pub mod repo;

View File

@ -13,8 +13,8 @@
use gitbutler_repo::credentials;
use gitbutler_tauri::{
askpass, commands, config, github, logs, menu, projects, remotes, repo, secret, undo, users,
virtual_branches, zip, App, WindowState,
askpass, commands, config, github, logs, menu, modes, projects, remotes, repo, secret, undo,
users, virtual_branches, zip, App, WindowState,
};
use tauri::{generate_context, Manager};
use tauri_plugin_log::LogTarget;
@ -193,7 +193,8 @@ fn main() {
github::commands::check_auth_status,
askpass::commands::submit_prompt_response,
remotes::list_remotes,
remotes::add_remote
remotes::add_remote,
modes::operating_mode,
])
.menu(menu::build(tauri_context.package_info()))
.on_menu_event(|event| menu::handle_event(&event))

View File

@ -0,0 +1,15 @@
use gitbutler_operating_modes::OperatingMode;
use gitbutler_project::Controller;
use gitbutler_project::ProjectId;
use tauri::State;
use crate::error::Error;
#[tauri::command(async)]
pub fn operating_mode(
projects: State<'_, Controller>,
project_id: ProjectId,
) -> Result<OperatingMode, Error> {
let project = projects.get(project_id)?;
gitbutler_operating_modes::commands::operating_mode(&project).map_err(Into::into)
}

View File

@ -30,9 +30,13 @@ pub(super) mod state {
payload: serde_json::json!({}),
project_id,
},
Change::GitHead { project_id, head } => ChangeForFrontend {
Change::GitHead {
project_id,
head,
operating_mode,
} => ChangeForFrontend {
name: format!("project://{}/git/head", project_id),
payload: serde_json::json!({ "head": head }),
payload: serde_json::json!({ "head": head, "operating_mode": operating_mode }),
project_id,
},
Change::GitActivity(project_id) => ChangeForFrontend {

View File

@ -1,6 +1,7 @@
use std::{fmt::Display, path::PathBuf};
use gitbutler_branch_actions::VirtualBranches;
use gitbutler_operating_modes::OperatingMode;
use gitbutler_project::ProjectId;
/// An event for internal use, as merge between [super::file_monitor::Event] and [Action].
@ -93,6 +94,7 @@ pub enum Change {
GitHead {
project_id: ProjectId,
head: String,
operating_mode: OperatingMode,
},
GitActivity(ProjectId),
VirtualBranches {

View File

@ -4,7 +4,9 @@ use anyhow::{Context, Result};
use gitbutler_branch_actions::{VirtualBranchActions, VirtualBranches};
use gitbutler_command_context::CommandContext;
use gitbutler_error::error::Marker;
use gitbutler_operating_modes::{in_open_workspace_mode, in_outside_workspace_mode};
use gitbutler_operating_modes::{
in_open_workspace_mode, in_outside_workspace_mode, operating_mode,
};
use gitbutler_oplog::{
entry::{OperationKind, SnapshotDetails},
OplogExt,
@ -187,6 +189,7 @@ impl Handler {
self.emit_app_event(Change::GitHead {
project_id,
head: head.to_string(),
operating_mode: operating_mode(&ctx),
})?;
}
}