refactor project menu

This commit is contained in:
Nikita Galaiko 2023-11-23 12:58:15 +01:00 committed by GitButler
parent 43a3a8b71e
commit 8e0b0a6bad
10 changed files with 95 additions and 79 deletions

View File

@ -4,7 +4,7 @@ use anyhow::Context;
use tauri::{generate_context, Manager};
use gblib::{
analytics, app, assets, commands, database, deltas, github, keys, logs, projects, sentry,
analytics, app, assets, commands, database, deltas, github, keys, logs, menu, projects, sentry,
sessions, storage, users, virtual_branches, watcher, zip,
};
@ -27,13 +27,7 @@ fn main() {
let tray_menu = tauri::SystemTrayMenu::new().add_item(hide).add_item(quit);
let tray = tauri::SystemTray::new().with_menu(tray_menu);
let project_settings = tauri::CustomMenuItem::new("projectsettings".to_string(), "Project Settings");
let project_submenu = tauri::Submenu::new("Project", tauri::Menu::new().add_item(project_settings.disabled()));
let menu = tauri::Menu::os_default(&app_title)
.add_submenu(project_submenu);
tauri::Builder::default()
.menu(menu)
.system_tray(tray)
.on_system_tray_event(|app_handle, event| {
if let tauri::SystemTrayEvent::MenuItemClick { id, .. } = event {
@ -68,11 +62,6 @@ fn main() {
api.prevent_close();
}
})
.on_menu_event(move |event| {
if event.menu_item_id() == "projectsettings" {
_ = event.window().emit("menuAction", Some("projectSettings"));
}
})
.setup(move |tauri_app| {
let window =
create_window(&tauri_app.handle()).expect("Failed to create window");
@ -168,8 +157,6 @@ fn main() {
users::commands::set_user,
users::commands::delete_user,
users::commands::get_user,
users::commands::get_current_project,
users::commands::set_current_project,
projects::commands::add_project,
projects::commands::get_project,
projects::commands::update_project,
@ -199,10 +186,13 @@ fn main() {
virtual_branches::commands::amend_virtual_branch,
virtual_branches::commands::list_remote_branches,
virtual_branches::commands::squash_branch_commit,
menu::menu_item_set_enabled,
keys::commands::get_public_key,
github::commands::init_device_oauth,
github::commands::check_auth_status,
])
.menu(menu::build(tauri_context.package_info()))
.on_menu_event(|event|menu::handle_event(&event))
.build(tauri_context)
.expect("Failed to build tauri app")
.run(|app_handle, event| match event {

View File

@ -12,11 +12,13 @@ pub enum Code {
ProjectGitRemote,
ProjectConflict,
ProjectHead,
Menu,
}
impl fmt::Display for Code {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Code::Menu => write!(f, "errors.menu"),
Code::Unknown => write!(f, "errors.unknown"),
Code::Validation => write!(f, "errors.validation"),
Code::Projects => write!(f, "errors.projects"),

View File

@ -17,6 +17,7 @@ pub mod id;
pub mod keys;
pub mod lock;
pub mod logs;
pub mod menu;
pub mod paths;
pub mod project_repository;
pub mod projects;

View File

@ -0,0 +1,58 @@
use serde_json::json;
use tauri::{
AppHandle, CustomMenuItem, Manager, Menu, MenuEntry, PackageInfo, Runtime, Submenu,
WindowMenuEvent,
};
use tracing::instrument;
use crate::error::{Code, Error};
#[tauri::command(async)]
#[instrument(skip(handle))]
pub async fn menu_item_set_enabled(
handle: AppHandle,
menu_item_id: &str,
enabled: bool,
) -> Result<(), Error> {
let window = handle
.get_window("main")
.expect("main window always present");
let menu_item = window
.menu_handle()
.try_get_item(menu_item_id)
.ok_or_else(|| Error::UserError {
message: format!("menu item not found: {}", menu_item_id),
code: Code::Menu,
})?;
menu_item.set_enabled(enabled).map_err(|error| {
tracing::error!(error = ?error, "failed to set menu item enabled state");
Error::Unknown
})?;
Ok(())
}
pub fn build(package_info: &PackageInfo) -> Menu {
Menu::os_default(&package_info.name).add_submenu(Submenu::new(
"Project",
Menu::with_items([disabled_menu_item("project/settings", "Project Settings")]),
))
}
fn disabled_menu_item(id: &str, title: &str) -> MenuEntry {
let mut item = CustomMenuItem::new(id, title);
item.enabled = false;
item.into()
}
pub fn handle_event<R: Runtime>(event: &WindowMenuEvent<R>) {
emit(
event.window(),
format!("menu://{}/clicked", event.menu_item_id()).as_str(),
);
}
fn emit<R: Runtime>(window: &tauri::Window<R>, event: &str) {
if let Err(err) = window.emit(event, json!({})) {
tracing::error!(error = ?err, "failed to emit event");
}
}

View File

@ -55,48 +55,6 @@ pub async fn set_user(handle: AppHandle, user: User) -> Result<User, Error> {
Ok(proxy.proxy_user(user).await)
}
#[tauri::command(async)]
#[instrument(skip(handle))]
pub async fn set_current_project(
handle: tauri::AppHandle,
project_id: Option<&str>,
) -> Result<(), Error> {
let app = handle.state::<Controller>();
if let Some(user) = app.get_user()? {
let mut user = user;
match project_id {
Some(project_id) => {
user.current_project = Some(project_id.to_string());
}
None => {
user.current_project = None;
}
}
app.set_user(&user)?;
if let Some(win) = handle.get_window("main") {
let menu_handle = win.menu_handle();
if let Some(project_settings) = menu_handle.try_get_item("projectsettings") {
_ = project_settings.set_enabled(project_id.is_some());
}
}
}
// TODO: persist this not on the user object
Ok(())
}
#[tauri::command(async)]
#[instrument(skip(handle))]
pub async fn get_current_project(handle: tauri::AppHandle) -> Result<Option<String>, Error> {
let app = handle.state::<Controller>();
match app.get_user()? {
Some(user) => Ok(user.current_project),
None => Err({
tracing::error!("failed to get user");
Error::Unknown
}),
}
}
impl From<controller::DeleteError> for Error {
fn from(value: controller::DeleteError) -> Self {
match value {

View File

@ -17,8 +17,6 @@ pub struct User {
pub github_access_token: Option<String>,
#[serde(default)]
pub github_username: Option<String>,
#[serde(default)]
pub current_project: Option<String>,
}
impl TryFrom<User> for git::Signature<'_> {

View File

@ -2,8 +2,6 @@ import { handleErrorWithSentry, init } from '@sentry/sveltekit';
import type { NavigationEvent } from '@sveltejs/kit';
import { dev } from '$app/environment';
import { PUBLIC_SENTRY_ENVIRONMENT } from '$env/static/public';
import { goto } from '$app/navigation';
import { getCurrentProject } from '$lib/backend/users';
init({
enabled: !dev,
@ -30,10 +28,3 @@ window.onunhandledrejection = (event: PromiseRejectionEvent) => {
console.log('Unhandled exception', event.reason);
originalUnhandledHandler?.bind(window)(event);
};
// getCurrentProject().then((projectId) => {
// // Start on the last used project
// if (projectId) {
// goto(`/${projectId}/base`);
// }
// });

View File

@ -9,13 +9,5 @@ export async function set(params: { user: User }) {
return invoke<User>('set_user', params);
}
export async function setCurrentProject(params: { projectId: string | undefined }) {
return invoke<void>('set_current_project', params);
}
export async function getCurrentProject() {
return invoke<string | undefined>('get_current_project');
}
const del = () => invoke<void>('delete_user');
export { del as delete };

View File

@ -0,0 +1,19 @@
import { emit } from '$lib/utils/events';
import { invoke, listen as listenIpc } from '$lib/backend/ipc';
export function subscribe(projectId: string) {
invoke('menu_item_set_enabled', {
menuItemId: 'project/settings',
enabled: true
});
const unsubscribeProjectSettings = listenIpc<string>('menu://project/settings/clicked', () => {
emit('goto', `/${projectId}/settings/`);
});
return () => {
unsubscribeProjectSettings();
invoke('menu_item_set_enabled', {
menuItemId: 'project/settings',
enabled: false
});
};
}

View File

@ -16,7 +16,7 @@
import { SETTINGS_CONTEXT, loadUserSettings } from '$lib/settings/userSettings';
import { initTheme } from './user/theme';
import { navigating } from '$app/stores';
import { setCurrentProject } from '$lib/backend/users';
import { subscribe as menuSubscribe } from '$lib/menu';
export let data: LayoutData;
const { projectService, cloud, user$ } = data;
@ -31,10 +31,17 @@
$: zoom = $userSettings.zoom || 1;
$: document.documentElement.style.fontSize = zoom + 'rem';
$: userSettings.update((s) => ({ ...s, zoom: zoom }));
// listen for current project events
let unsubscribeMenu = () => {};
$: if ($navigating) {
// Keeps the backend aware of what is the current project
let projectId = $navigating?.to?.params?.projectId;
setCurrentProject({ projectId });
const fromProject = $navigating?.from?.params?.projectId;
const toProject = $navigating?.to?.params?.projectId;
const projectHasChanged = fromProject !== toProject;
if (projectHasChanged && toProject) {
unsubscribeMenu?.();
unsubscribeMenu = menuSubscribe(toProject);
}
}
onMount(() =>