mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-25 02:26:14 +03:00
Merge pull request #4682 from gitbutlerapp/Provide-current-mode-to-frontend
Provide current mode to frontend
This commit is contained in:
commit
83283d918c
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -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",
|
||||
|
@ -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/', ''))
|
||||
);
|
||||
}
|
36
apps/desktop/src/lib/modes/service.ts
Normal file
36
apps/desktop/src/lib/modes/service.ts
Normal 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)
|
||||
);
|
||||
}
|
@ -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()}>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
10
crates/gitbutler-operating-modes/src/commands.rs
Normal file
10
crates/gitbutler-operating-modes/src/commands.rs
Normal 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))
|
||||
}
|
@ -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,
|
||||
|
@ -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]
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
|
15
crates/gitbutler-tauri/src/modes.rs
Normal file
15
crates/gitbutler-tauri/src/modes.rs
Normal 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)
|
||||
}
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user