wip: connect to github

This commit is contained in:
2026-04-23 11:18:43 +01:00
parent 302d0d3222
commit b327648d31
12 changed files with 399 additions and 97 deletions

View File

@@ -1,39 +1,117 @@
use gpui::{FontWeight, ParentElement, Styled, div};
use std::time::{Duration, Instant};
use gpui::{AppContext, FontWeight, ParentElement, Styled, div, prelude::FluentBuilder};
use rand::RngExt;
use crate::{
api,
api, app,
component::text::text,
query::{self, use_lazy_query},
query::{self, QueryStatus, read_query, use_lazy_query},
};
pub(crate) struct GithubStepView {
last_tick: Instant,
placeholder_code: String,
create_device_code_query: query::Entity<api::auth::CreateDeviceCode>,
}
pub(crate) fn new(cx: &mut gpui::Context<GithubStepView>) -> GithubStepView {
GithubStepView {
last_tick: Instant::now(),
placeholder_code: "ABCDEFGH".to_owned(),
create_device_code_query: use_lazy_query(api::auth::CreateDeviceCode, cx),
}
}
impl GithubStepView {
const CHAR_POOL: &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
fn generate_random_code(&mut self, cx: &mut gpui::Context<Self>) -> String {
let rng = app::rng(cx);
(0..8)
.map(|_| {
let idx = rng.random_range(0..Self::CHAR_POOL.len());
Self::CHAR_POOL.chars().nth(idx).unwrap()
})
.collect()
}
}
impl gpui::Render for GithubStepView {
fn render(
&mut self,
_window: &mut gpui::Window,
window: &mut gpui::Window,
cx: &mut gpui::Context<Self>,
) -> impl gpui::IntoElement {
div().flex().flex_col().size_full().child(header(cx))
let theme = app::current_theme(cx);
let border_color = theme.colors.surface_elevated.clone();
let bg_color = theme.colors.surface.clone();
let create_device_code_query = read_query(&self.create_device_code_query, cx);
let is_loading_code = matches!(create_device_code_query, QueryStatus::Loading);
let now = Instant::now();
let should_tick = now.duration_since(self.last_tick) >= Duration::from_millis(50);
if is_loading_code {
cx.on_next_frame(window, move |this, _, cx| {
if should_tick {
this.placeholder_code = this.generate_random_code(cx);
this.last_tick = Instant::now();
}
cx.notify();
});
}
let letter_boxes = self
.placeholder_code
.split("")
.filter(|c| !c.is_empty())
.map(|c| {
text(String::from(c))
.bold()
.text_2xl()
.styled(move |it| {
it.p_3()
.font_family("CommitMono")
.border_1()
.border_color(border_color)
.rounded_lg()
.bg(bg_color)
})
.when(is_loading_code, |it| it.opacity(0.5))
})
.collect::<Vec<_>>();
div()
.flex()
.flex_col()
.size_full()
.px_4()
.py_12()
.child(header())
.child(
div()
.flex()
.flex_row()
.flex_1()
.items_center()
.justify_center()
.gap_1p5()
.children(letter_boxes),
)
}
}
fn header(cx: &gpui::Context<GithubStepView>) -> impl gpui::IntoElement {
fn header() -> impl gpui::IntoElement {
div()
.flex()
.flex_col()
.items_center()
.child(text("Connect to GitHub", cx).font_weight(FontWeight(700.)))
.gap_1p5()
.child(text("Connect to GitHub").text_xl().bold())
.child(text(
"You will be redirected to GitHub to authorize access. Copy the device code below into GitHub.",
cx
).opacity(0.8))
"You will be redirected to GitHub to authorize access.\nCopy the device code below into GitHub.",
).leading_tight().centered().opacity(0.8))
}

View File

@@ -1,14 +1,17 @@
use gpui::{AppContext, FontWeight, IntoElement, ParentElement, Styled, div};
use gpui::{
AppContext, BorrowAppContext, InteractiveElement, IntoElement, ParentElement,
StatefulInteractiveElement, Styled, div,
};
use crate::{
api, app,
app,
component::text::text,
query::{self, use_lazy_query},
screen::setup_wizard::{github_step, welcome_step},
screen::setup_wizard::{github_step, welcome_step::welcome_step},
};
pub(crate) struct Screen {
current_step: Step,
github_step_view: gpui::Entity<github_step::GithubStepView>,
}
enum Step {
@@ -16,9 +19,27 @@ enum Step {
ConnectToGithub,
}
pub(crate) fn new(cx: &mut gpui::Context<Screen>) -> Screen {
pub(crate) fn new(window: &mut gpui::Window, cx: &mut gpui::Context<Screen>) -> Screen {
cx.observe_window_appearance(window, |_, window, cx| {
cx.update_global::<app::Global, ()>(|global, cx| {
global.current_theme = window.appearance().into();
cx.notify();
});
})
.detach();
Screen {
current_step: Step::Welcome,
github_step_view: cx.new(|cx| github_step::new(cx)),
}
}
impl Screen {
fn advance_to_next_step(&mut self) {
match self.current_step {
Step::Welcome => self.current_step = Step::ConnectToGithub,
Step::ConnectToGithub => {}
}
}
}
@@ -29,13 +50,17 @@ impl gpui::Render for Screen {
cx: &mut gpui::Context<Self>,
) -> impl gpui::IntoElement {
let step_view = match self.current_step {
Step::Welcome => welcome_step::new(cx).into_any_element(),
Step::ConnectToGithub => cx.new(|cx| github_step::new(cx)).into_any_element(),
Step::Welcome => welcome_step()
.on_next(cx.listener(|this, _, _window, _cx| this.advance_to_next_step()))
.into_any_element(),
Step::ConnectToGithub => self.github_step_view.clone().into_any_element(),
};
let theme = app::current_theme(cx);
div()
.id("awd")
.on_click(cx.listener(|a, b, c, d| {}))
.flex()
.flex_row()
.items_center()
@@ -51,11 +76,9 @@ impl gpui::Render for Screen {
.bg(theme.colors.surface)
.relative()
.child(
text("Novem", cx)
.font_weight(FontWeight(700.))
.absolute()
.top_20()
.left_8(),
text("Novem")
.bold()
.styled(|it| it.absolute().top_20().left_8()),
)
.child(step_list(cx)),
)
@@ -83,9 +106,9 @@ fn step_list(cx: &gpui::Context<impl gpui::Render>) -> impl gpui::IntoElement {
.gap_3()
.text_sm()
.children(vec![
text("Welcome!", cx),
text("Connect to GitHub", cx),
text("Customize Novem", cx),
text("Complete!", cx),
text("Welcome!"),
text("Connect to GitHub"),
text("Customize Novem"),
text("Complete!"),
])
}

View File

@@ -1,47 +1,62 @@
use gpui::{FontWeight, ParentElement, Styled, div};
use gpui::{ParentElement, Styled, div, prelude::FluentBuilder};
use crate::{
app,
component::{button::button, text::text},
};
use crate::component::{button::button, text::text};
struct WelcomeStep {
on_next: Option<FnOnce>,
#[derive(gpui::IntoElement)]
pub(crate) struct WelcomeStep {
on_next: Option<Box<dyn Fn(&(), &mut gpui::Window, &mut gpui::App) + 'static>>,
}
pub(crate) fn new<E>(cx: &gpui::Context<E>) -> impl gpui::IntoElement {
let theme = app::current_theme(cx);
div()
.flex()
.flex_col()
.size_full()
.items_start()
.justify_center()
.child(
div()
.flex()
.flex_col()
.flex_1()
.justify_center()
.w_full()
.p_8()
.child(
text(
"Welcome to Novem!\nThis wizard will guide you through setting up Novem.\n",
cx,
pub(crate) fn welcome_step() -> WelcomeStep {
WelcomeStep { on_next: None }
}
impl WelcomeStep {
pub fn on_next(mut self, f: impl Fn(&(), &mut gpui::Window, &mut gpui::App) + 'static) -> Self {
self.on_next = Some(Box::new(f));
self
}
}
impl gpui::RenderOnce for WelcomeStep {
fn render(self, _window: &mut gpui::Window, _cx: &mut gpui::App) -> impl gpui::IntoElement {
let next_button = button("next")
.label("Next")
.when(self.on_next.is_some(), |b| {
b.on_click(move |_, window, cx| self.on_next.as_ref().unwrap()(&(), window, cx))
});
div()
.flex()
.flex_col()
.size_full()
.items_start()
.justify_center()
.child(
div()
.flex()
.flex_col()
.flex_1()
.justify_center()
.w_full()
.p_8()
.child(
text(
"Welcome to Novem!\nThis wizard will guide you through setting up Novem.\n",
)
.opacity(0.8),
)
.opacity(0.8),
)
.child(text("Press 'Next' to begin setup.", cx).font_weight(FontWeight(500.))),
)
.child(
div()
.flex()
.flex_row()
.justify_end()
.w_full()
.p_4()
.pt_0()
.child(button("next", cx).label("Next")),
)
.child(text("Press 'Next' to begin setup.").medium()),
)
.child(
div()
.flex()
.flex_row()
.justify_end()
.w_full()
.p_4()
.pt_0()
.child(next_button),
)
}
}