feat: setup wizard shell
This commit is contained in:
@@ -3,10 +3,10 @@ use serde::Deserialize;
|
||||
use crate::{api, query};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct CreateDeviceCode;
|
||||
pub struct CreateDeviceCode;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DeviceCodeResponse {
|
||||
pub struct DeviceCodeResponse {
|
||||
device_code: String,
|
||||
user_code: String,
|
||||
vertification_uri: String,
|
||||
@@ -20,7 +20,7 @@ impl query::QueryFn for CreateDeviceCode {
|
||||
type Context = api::QueryContext;
|
||||
|
||||
fn key(&self) -> &'static str {
|
||||
todo!()
|
||||
"auth.device_code"
|
||||
}
|
||||
|
||||
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 crate::screen::welcome;
|
||||
|
||||
mod api;
|
||||
mod app;
|
||||
mod asset;
|
||||
@@ -12,6 +14,14 @@ mod theme;
|
||||
mod titlebar;
|
||||
|
||||
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()
|
||||
.with_assets(asset::Asset)
|
||||
.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.))),
|
||||
..Default::default()
|
||||
}),
|
||||
is_resizable: false,
|
||||
..Default::default()
|
||||
},
|
||||
|window, cx| cx.new(|cx| app::Chrome::new(window, cx)),
|
||||
|window, cx| cx.new(|cx| welcome::Screen::new(cx)),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
58
src/query.rs
58
src/query.rs
@@ -45,20 +45,29 @@ where
|
||||
T: 'static,
|
||||
Store<F::Context>: gpui::Global,
|
||||
{
|
||||
let ent = cx
|
||||
.update_global::<Store<F::Context>, _>(|store, cx| store.ensure_query_data(&query_fn, cx));
|
||||
let ent = cx.update_global::<Store<F::Context>, _>(|store, 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();
|
||||
})
|
||||
.detach();
|
||||
|
||||
let cloned_ent = ent.clone();
|
||||
let query_context = cx.global::<Store<F::Context>>().query_context.clone();
|
||||
cx.observe(&query_context, move |_, _, cx| {
|
||||
cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
||||
store.invalidate_query(&query_fn, cx);
|
||||
store.ensure_query_data(&query_fn, cx);
|
||||
})
|
||||
store.invalidate_query(&cloned_ent, cx);
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
|
||||
@@ -73,23 +82,41 @@ where
|
||||
{
|
||||
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();
|
||||
})
|
||||
.detach();
|
||||
|
||||
let cloned_ent = ent.clone();
|
||||
let query_context = cx.global::<Store<F::Context>>().query_context.clone();
|
||||
cx.observe(&query_context, move |_, _, cx| {
|
||||
cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
||||
store.invalidate_query(&query_fn, cx);
|
||||
store.ensure_query_data(&query_fn, cx);
|
||||
})
|
||||
store.invalidate_query(&cloned_ent, cx);
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
|
||||
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>(
|
||||
query: &Entity<F>,
|
||||
cx: &'a gpui::Context<T>,
|
||||
@@ -125,8 +152,8 @@ where
|
||||
{
|
||||
pub fn new(ctx: C, cx: &mut gpui::App) -> Self {
|
||||
Self {
|
||||
query_data: std::collections::HashMap::new(),
|
||||
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
|
||||
Q: QueryFn<Context = C>,
|
||||
T: 'static,
|
||||
E: '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| {
|
||||
if !matches!(query.data, QueryData::Loading) {
|
||||
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(
|
||||
&mut self,
|
||||
window: &mut gpui::Window,
|
||||
cx: &mut gpui::Context<Self>,
|
||||
) -> 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