2026-04-20 15:13:26 +01:00
|
|
|
use gpui::{bounds, point, prelude::*, px, size};
|
|
|
|
|
|
2026-05-06 01:42:38 +08:00
|
|
|
use crate::screen::{dashboard, setup_wizard};
|
2026-04-22 12:41:33 +01:00
|
|
|
|
2026-04-21 11:50:04 +01:00
|
|
|
mod api;
|
2026-04-20 15:13:26 +01:00
|
|
|
mod app;
|
|
|
|
|
mod asset;
|
|
|
|
|
mod colors;
|
|
|
|
|
mod component;
|
2026-04-25 00:49:50 +01:00
|
|
|
mod http;
|
2026-04-20 15:13:26 +01:00
|
|
|
mod query;
|
2026-04-21 20:30:41 +01:00
|
|
|
mod screen;
|
2026-04-24 19:22:25 +01:00
|
|
|
mod storage;
|
2026-04-20 15:13:26 +01:00
|
|
|
mod theme;
|
2026-04-26 00:01:57 +01:00
|
|
|
mod util;
|
|
|
|
|
|
|
|
|
|
enum Start {
|
|
|
|
|
FromScratch,
|
|
|
|
|
FromSetup(setup_wizard::StoredSetupState),
|
2026-04-26 16:06:49 +01:00
|
|
|
FromSaved(storage::PersistedState),
|
2026-04-26 00:01:57 +01:00
|
|
|
}
|
2026-04-20 15:13:26 +01:00
|
|
|
|
|
|
|
|
fn main() {
|
2026-04-22 12:41:33 +01:00
|
|
|
// GPUI polls our async query futures, but reqwest relies on Tokio's
|
|
|
|
|
// reactor and blocking pool for DNS, sockets, and timers.
|
|
|
|
|
let runtime = tokio::runtime::Builder::new_multi_thread()
|
|
|
|
|
.enable_all()
|
|
|
|
|
.build()
|
|
|
|
|
.expect("failed to build Tokio runtime");
|
|
|
|
|
let _runtime_guard = runtime.enter();
|
|
|
|
|
|
2026-04-20 15:13:26 +01:00
|
|
|
gpui::Application::new()
|
|
|
|
|
.with_assets(asset::Asset)
|
2026-04-25 00:49:50 +01:00
|
|
|
.with_http_client(std::sync::Arc::new(http::Client::new(
|
|
|
|
|
reqwest::Client::new(),
|
|
|
|
|
)))
|
2026-04-20 15:13:26 +01:00
|
|
|
.run(setup_application);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn setup_application(cx: &mut gpui::App) {
|
2026-05-06 01:42:38 +08:00
|
|
|
let use_github_fixtures = api::use_github_fixtures();
|
2026-04-24 19:22:25 +01:00
|
|
|
let query_store = query::Store::new(api::QueryContext {
|
|
|
|
|
http: reqwest::Client::new(),
|
|
|
|
|
auth: None,
|
|
|
|
|
github: api::GithubCredentials {
|
|
|
|
|
base_url: "https://api.github.com",
|
|
|
|
|
client_id: "Iv23liZD4bMQpGJICsR7",
|
2026-04-21 11:50:04 +01:00
|
|
|
},
|
2026-05-06 01:42:38 +08:00
|
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
|
should_use_fixtures: false,
|
2026-04-24 19:22:25 +01:00
|
|
|
});
|
2026-04-20 15:13:26 +01:00
|
|
|
|
2026-04-26 00:46:11 +01:00
|
|
|
let theme_family = theme::ThemeFamily::default();
|
2026-04-20 15:13:26 +01:00
|
|
|
let global = app::Global {
|
|
|
|
|
safe_area: bounds(point(px(0.), px(0.)), size(px(72.), px(12.))),
|
2026-04-26 00:46:11 +01:00
|
|
|
theme_family,
|
|
|
|
|
current_theme: theme_family.theme_for_appearance(cx.window_appearance()),
|
2026-04-23 11:18:43 +01:00
|
|
|
rng: rand::rng(),
|
2026-04-20 15:13:26 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
cx.set_global(global);
|
2026-04-21 11:50:04 +01:00
|
|
|
cx.set_global(query_store);
|
2026-04-20 15:13:26 +01:00
|
|
|
|
2026-04-26 00:01:57 +01:00
|
|
|
// TODO: handle failure
|
|
|
|
|
_ = storage::ensure_data_dir();
|
|
|
|
|
|
2026-05-06 01:42:38 +08:00
|
|
|
if use_github_fixtures {
|
|
|
|
|
_ = dashboard::open_window(cx);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-26 00:01:57 +01:00
|
|
|
let start = resume_application_state(cx);
|
|
|
|
|
|
|
|
|
|
match start {
|
|
|
|
|
Start::FromScratch => {
|
|
|
|
|
let screen = setup_wizard::new();
|
|
|
|
|
_ = setup_wizard::open_window(screen, cx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Start::FromSetup(state) => {
|
|
|
|
|
let screen = setup_wizard::from_saved(state);
|
|
|
|
|
_ = setup_wizard::open_window(screen, cx);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-26 16:06:49 +01:00
|
|
|
Start::FromSaved(_) => {
|
2026-05-06 01:42:38 +08:00
|
|
|
_ = dashboard::open_window(cx);
|
2026-04-26 16:06:49 +01:00
|
|
|
}
|
2026-04-26 00:01:57 +01:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn resume_application_state(cx: &mut gpui::App) -> Start {
|
|
|
|
|
let state = storage::load_persisted_state();
|
2026-04-26 16:06:49 +01:00
|
|
|
let Some(mut state) = state else {
|
2026-04-26 00:01:57 +01:00
|
|
|
return Start::FromScratch;
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-06 01:42:38 +08:00
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
|
let auth_tokens = state.debug_auth_tokens.take();
|
|
|
|
|
|
|
|
|
|
#[cfg(not(debug_assertions))]
|
|
|
|
|
let auth_tokens = cx
|
|
|
|
|
.background_executor()
|
|
|
|
|
.block(storage::load_auth_tokens(cx, state.selected_account));
|
|
|
|
|
|
2026-04-26 00:01:57 +01:00
|
|
|
let Some(auth_tokens) = auth_tokens else {
|
|
|
|
|
return Start::FromScratch;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_ = cx.update_global::<query::Store<api::QueryContext>, _>(|store, _| {
|
|
|
|
|
store.update_query_context(|cx| {
|
|
|
|
|
cx.auth = Some(auth_tokens);
|
2026-05-06 01:42:38 +08:00
|
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
|
{
|
|
|
|
|
cx.should_use_fixtures = state.debug_should_use_fixtures.unwrap_or(false);
|
|
|
|
|
}
|
2026-04-26 00:01:57 +01:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let setup_status = setup_wizard::read_setup_status();
|
|
|
|
|
|
|
|
|
|
println!("[main] setup status: {:?}", setup_status);
|
|
|
|
|
|
|
|
|
|
match setup_status {
|
|
|
|
|
setup_wizard::SetupStatus::NotStarted => Start::FromScratch,
|
|
|
|
|
setup_wizard::SetupStatus::InProgress(state) => Start::FromSetup(state),
|
2026-04-26 16:06:49 +01:00
|
|
|
setup_wizard::SetupStatus::Completed => Start::FromSaved(state),
|
2026-04-26 00:01:57 +01:00
|
|
|
}
|
2026-04-20 15:13:26 +01:00
|
|
|
}
|