feat: setup wizard shell
This commit is contained in:
@@ -10,3 +10,4 @@ paste = "1.0"
|
|||||||
reqwest = "0.13.2"
|
reqwest = "0.13.2"
|
||||||
serde = "1.0.228"
|
serde = "1.0.228"
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.149"
|
||||||
|
tokio = { version = "1.52.1", features = ["rt-multi-thread", "net", "time"] }
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ use serde::Deserialize;
|
|||||||
use crate::{api, query};
|
use crate::{api, query};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct CreateDeviceCode;
|
pub struct CreateDeviceCode;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct DeviceCodeResponse {
|
pub struct DeviceCodeResponse {
|
||||||
device_code: String,
|
device_code: String,
|
||||||
user_code: String,
|
user_code: String,
|
||||||
vertification_uri: String,
|
vertification_uri: String,
|
||||||
@@ -20,7 +20,7 @@ impl query::QueryFn for CreateDeviceCode {
|
|||||||
type Context = api::QueryContext;
|
type Context = api::QueryContext;
|
||||||
|
|
||||||
fn key(&self) -> &'static str {
|
fn key(&self) -> &'static str {
|
||||||
todo!()
|
"auth.device_code"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(&self, c: &Self::Context) -> Result<Self::Data, Self::Error> {
|
async fn run(&self, c: &Self::Context) -> Result<Self::Data, Self::Error> {
|
||||||
|
|||||||
13
src/main.rs
13
src/main.rs
@@ -1,5 +1,7 @@
|
|||||||
use gpui::{bounds, point, prelude::*, px, size};
|
use gpui::{bounds, point, prelude::*, px, size};
|
||||||
|
|
||||||
|
use crate::screen::welcome;
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod app;
|
mod app;
|
||||||
mod asset;
|
mod asset;
|
||||||
@@ -12,6 +14,14 @@ mod theme;
|
|||||||
mod titlebar;
|
mod titlebar;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
// 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();
|
||||||
|
|
||||||
gpui::Application::new()
|
gpui::Application::new()
|
||||||
.with_assets(asset::Asset)
|
.with_assets(asset::Asset)
|
||||||
.run(setup_application);
|
.run(setup_application);
|
||||||
@@ -48,9 +58,10 @@ fn setup_application(cx: &mut gpui::App) {
|
|||||||
traffic_light_position: Some(top_left + point(px(12.), px(12.))),
|
traffic_light_position: Some(top_left + point(px(12.), px(12.))),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
|
is_resizable: false,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|window, cx| cx.new(|cx| app::Chrome::new(window, cx)),
|
|window, cx| cx.new(|cx| welcome::Screen::new(cx)),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
58
src/query.rs
58
src/query.rs
@@ -45,20 +45,29 @@ where
|
|||||||
T: 'static,
|
T: 'static,
|
||||||
Store<F::Context>: gpui::Global,
|
Store<F::Context>: gpui::Global,
|
||||||
{
|
{
|
||||||
let ent = cx
|
let ent = cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
||||||
.update_global::<Store<F::Context>, _>(|store, cx| store.ensure_query_data(&query_fn, cx));
|
let ent = store.entity_for(&query_fn, cx);
|
||||||
|
store.ensure_query_data(&query_fn, cx);
|
||||||
|
ent
|
||||||
|
});
|
||||||
|
|
||||||
cx.observe(&ent.raw, |_, _, cx| {
|
cx.observe(&ent.raw, move |_, ent, cx| {
|
||||||
|
let query = ent.read(cx);
|
||||||
|
if matches!(query.data, QueryData::Stale) {
|
||||||
|
cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
||||||
|
store.ensure_query_data(&query_fn, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
|
let cloned_ent = ent.clone();
|
||||||
let query_context = cx.global::<Store<F::Context>>().query_context.clone();
|
let query_context = cx.global::<Store<F::Context>>().query_context.clone();
|
||||||
cx.observe(&query_context, move |_, _, cx| {
|
cx.observe(&query_context, move |_, _, cx| {
|
||||||
cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
||||||
store.invalidate_query(&query_fn, cx);
|
store.invalidate_query(&cloned_ent, cx);
|
||||||
store.ensure_query_data(&query_fn, cx);
|
});
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
@@ -73,23 +82,41 @@ where
|
|||||||
{
|
{
|
||||||
let ent = cx.update_global::<Store<F::Context>, _>(|store, cx| store.entity_for(&query_fn, cx));
|
let ent = cx.update_global::<Store<F::Context>, _>(|store, cx| store.entity_for(&query_fn, cx));
|
||||||
|
|
||||||
cx.observe(&ent.raw, |_, ent, cx| {
|
cx.observe(&ent.raw, move |_, ent, cx| {
|
||||||
|
let query = ent.read(cx);
|
||||||
|
if matches!(query.data, QueryData::Stale) {
|
||||||
|
cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
||||||
|
store.ensure_query_data(&query_fn, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
|
let cloned_ent = ent.clone();
|
||||||
let query_context = cx.global::<Store<F::Context>>().query_context.clone();
|
let query_context = cx.global::<Store<F::Context>>().query_context.clone();
|
||||||
cx.observe(&query_context, move |_, _, cx| {
|
cx.observe(&query_context, move |_, _, cx| {
|
||||||
cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
||||||
store.invalidate_query(&query_fn, cx);
|
store.invalidate_query(&cloned_ent, cx);
|
||||||
store.ensure_query_data(&query_fn, cx);
|
});
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
ent
|
ent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<F> Entity<F>
|
||||||
|
where
|
||||||
|
F: QueryFn,
|
||||||
|
Store<F::Context>: gpui::Global,
|
||||||
|
{
|
||||||
|
pub fn refetch(&self, cx: &mut gpui::Context<F::Context>) {
|
||||||
|
cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
||||||
|
store.invalidate_query(self, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_query<'a, F, T>(
|
pub fn read_query<'a, F, T>(
|
||||||
query: &Entity<F>,
|
query: &Entity<F>,
|
||||||
cx: &'a gpui::Context<T>,
|
cx: &'a gpui::Context<T>,
|
||||||
@@ -125,8 +152,8 @@ where
|
|||||||
{
|
{
|
||||||
pub fn new(ctx: C, cx: &mut gpui::App) -> Self {
|
pub fn new(ctx: C, cx: &mut gpui::App) -> Self {
|
||||||
Self {
|
Self {
|
||||||
query_data: std::collections::HashMap::new(),
|
|
||||||
query_context: cx.new(|_| ctx),
|
query_context: cx.new(|_| ctx),
|
||||||
|
query_data: std::collections::HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,12 +232,13 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalidate_query<Q, T>(&mut self, query: &Q, cx: &mut gpui::Context<T>)
|
fn invalidate_query<E, F>(&self, entity: &Entity<F>, cx: &mut gpui::Context<E>)
|
||||||
where
|
where
|
||||||
Q: QueryFn<Context = C>,
|
E: 'static,
|
||||||
T: 'static,
|
F: QueryFn<Context = C>,
|
||||||
{
|
{
|
||||||
if let Some(entity) = self.query_data.get(query.key()) {
|
let entity = entity.raw.read(cx);
|
||||||
|
if let Some(entity) = self.query_data.get(entity.key) {
|
||||||
entity.update(cx, |query, cx| {
|
entity.update(cx, |query, cx| {
|
||||||
if !matches!(query.data, QueryData::Loading) {
|
if !matches!(query.data, QueryData::Loading) {
|
||||||
query.data = QueryData::Stale;
|
query.data = QueryData::Stale;
|
||||||
|
|||||||
@@ -1,11 +1,92 @@
|
|||||||
struct Welcome {}
|
use gpui::{FontWeight, ParentElement, Styled, div, px};
|
||||||
|
|
||||||
impl gpui::Render for Welcome {
|
use crate::{
|
||||||
|
api, app,
|
||||||
|
component::{
|
||||||
|
font_icon::{FontIcon, font_icon},
|
||||||
|
text::text,
|
||||||
|
},
|
||||||
|
query::{self, use_lazy_query, use_query},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) struct Screen {
|
||||||
|
create_device_code_query: query::Entity<api::auth::CreateDeviceCode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Screen {
|
||||||
|
pub fn new(cx: &mut gpui::Context<Self>) -> Self {
|
||||||
|
Self {
|
||||||
|
create_device_code_query: use_lazy_query(api::auth::CreateDeviceCode, cx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl gpui::Render for Screen {
|
||||||
fn render(
|
fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
window: &mut gpui::Window,
|
window: &mut gpui::Window,
|
||||||
cx: &mut gpui::Context<Self>,
|
cx: &mut gpui::Context<Self>,
|
||||||
) -> impl gpui::IntoElement {
|
) -> impl gpui::IntoElement {
|
||||||
todo!()
|
let theme = app::current_theme(cx);
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.items_center()
|
||||||
|
.justify_center()
|
||||||
|
.size_full()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.justify_center()
|
||||||
|
.w_1_3()
|
||||||
|
.h_full()
|
||||||
|
.bg(theme.colors.surface)
|
||||||
|
.relative()
|
||||||
|
.child(
|
||||||
|
text("Novem", cx)
|
||||||
|
.font_weight(FontWeight(700.))
|
||||||
|
.absolute()
|
||||||
|
.top_20()
|
||||||
|
.left_8(),
|
||||||
|
)
|
||||||
|
.child(step_list(cx)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.w_2_3()
|
||||||
|
.px_8()
|
||||||
|
.h_full()
|
||||||
|
.bg(theme.colors.background)
|
||||||
|
.items_start()
|
||||||
|
.justify_center()
|
||||||
|
.child(
|
||||||
|
text(
|
||||||
|
"Welcome to Novem!\nThis wizard will guide you through setting up Novem.\n",
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.opacity(0.8),
|
||||||
|
)
|
||||||
|
.child(text("Press 'Next' to begin setup.", cx).font_weight(FontWeight(500.)))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn step_list(cx: &gpui::Context<impl gpui::Render>) -> impl gpui::IntoElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.items_start()
|
||||||
|
.w_full()
|
||||||
|
.px_8()
|
||||||
|
.justify_center()
|
||||||
|
.gap_3()
|
||||||
|
.children(vec![
|
||||||
|
text("Welcome!", cx),
|
||||||
|
text("Connect to GitHub", cx),
|
||||||
|
text("Customize Novem", cx),
|
||||||
|
text("Complete!", cx),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user