Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Upgrade to Leptos 0.5 #78

Closed
wants to merge 2 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Upgrade to Leptos 0.5
srid committed Oct 2, 2023
commit bf904333f6ea962ed30ccc184be1dfb265160045
699 changes: 261 additions & 438 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -10,10 +10,10 @@ repository = "https://github.com/juspay/nix-browser"
cfg-if = "1"
clap = { version = "4.3", features = ["derive", "env"] }
human-panic = "1.1.5"
leptos = { version = "0.4", features = ["serde", "nightly"] }
leptos_meta = { version = "0.4", features = ["nightly"] }
leptos_router = { version = "0.4", features = ["nightly"] }
leptos_query = "0.2"
leptos = { version = "0.5", features = ["serde", "nightly"] }
leptos_meta = { version = "0.5", features = ["nightly"] }
leptos_router = { version = "0.5", features = ["nightly"] }
leptos_query = "0.3"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-subscriber-wasm = "0.1"
@@ -53,7 +53,7 @@ http = { version = "0.2", optional = true }
human-panic.workspace = true
hyper = { version = "0.14", features = ["server"], optional = true }
leptos.workspace = true
leptos_axum = { version = "0.4", optional = true }
leptos_axum = { version = "0.5", optional = true }
leptos_meta.workspace = true
leptos_router.workspace = true
leptos_query.workspace = true
9 changes: 3 additions & 6 deletions crates/leptos_extra/src/query.rs
Original file line number Diff line number Diff line change
@@ -29,14 +29,13 @@ pub fn query_options<V>() -> QueryOptions<V> {
/// * `fetcher`: The server fn to call
#[instrument(
name = "use_server_query",
skip(cx, k, fetcher),
skip(k, fetcher),
fields(
fetcher = std::any::type_name::<F>(),
render_mode=LEPTOS_MODE
)
)]
pub fn use_server_query<K, V, F, Fu>(
cx: Scope,
k: impl Fn() -> K + 'static,
fetcher: F,
) -> QueryResult<ServerFnResult<V>, impl RefetchFn>
@@ -49,7 +48,6 @@ where
let span = tracing::Span::current();
tracing::debug!("use_query");
leptos_query::use_query(
cx,
k,
move |k| {
let _enter = span.enter();
@@ -77,7 +75,6 @@ const LEPTOS_MODE: &str = {
/// * `query`: The value to pass to [invalidate_query]
#[component]
pub fn RefetchQueryButton<K, V, R, F>(
cx: Scope,
result: QueryResult<ServerFnResult<V>, R>,
query: F,
) -> impl IntoView
@@ -87,14 +84,14 @@ where
R: RefetchFn,
F: Fn() -> K + 'static,
{
view! { cx,
view! {
<button
class="p-1 text-white shadow border-1 bg-primary-700 disabled:bg-base-400 disabled:text-black"
disabled=move || result.is_fetching.get()
on:click=move |_| {
let k = query();
tracing::debug!("Invalidating query");
use_query_client(cx).invalidate_query::<K, ServerFnResult<V>>(k);
use_query_client().invalidate_query::<K, ServerFnResult<V>>(k);
}
>

12 changes: 6 additions & 6 deletions crates/leptos_extra/src/signal.rs
Original file line number Diff line number Diff line change
@@ -3,18 +3,18 @@ use leptos::*;
use tracing::instrument;

/// [provide_context] a new signal of type `T` in the current scope
pub fn provide_signal<T: 'static>(cx: Scope, default: T) {
let sig = create_rw_signal(cx, default);
provide_context(cx, sig);
pub fn provide_signal<T: 'static>(default: T) {
let sig = create_rw_signal(default);
provide_context(sig);
}

/// [use_context] the signal of type `T` in the current scope
///
/// If the signal was not provided in a top-level scope (via [provide_signal])
/// this method will panic after tracing an error.
#[instrument(name = "use_signal")]
pub fn use_signal<T>(cx: Scope) -> RwSignal<T> {
use_context(cx)
pub fn use_signal<T>() -> RwSignal<T> {
use_context()
.ok_or_else(|| {
// This happens if the dev forgets to call `provide_signal::<T>` in
// the parent scope
@@ -30,7 +30,7 @@ pub fn use_signal<T>(cx: Scope) -> RwSignal<T> {

/// Extends [SignalWith] to add a `with_result` method that operates on the
/// inner value, avoiding the need to clone it.
pub trait SignalWithResult<T, E>: SignalWith<Option<Result<T, E>>> {
pub trait SignalWithResult<T, E>: SignalWith<Value = Option<Result<T, E>>> {
/// Like [SignalWith::with] but operates on the inner [Result] value without cloning it.
fn with_result<U>(&self, f: impl Fn(&T) -> U + 'static) -> Option<Result<U, E>>
where
98 changes: 48 additions & 50 deletions src/app/flake.rs
Original file line number Diff line number Diff line change
@@ -23,13 +23,13 @@ use crate::widget::*;

/// Nix flake dashboard
#[component]
pub fn NixFlakeRoute(cx: Scope) -> impl IntoView {
pub fn NixFlakeRoute() -> impl IntoView {
let suggestions = FlakeUrl::suggestions();
let url = use_signal::<FlakeUrl>(cx);
let refresh = use_signal::<Refresh>(cx);
let url = use_signal::<FlakeUrl>();
let refresh = use_signal::<Refresh>();
let query = move || (url(), refresh());
let result = query::use_server_query(cx, query, get_flake);
view! { cx,
let result = query::use_server_query(query, get_flake);
view! {
<Title text="Nix Flake"/>
<h1 class="text-5xl font-bold">{"Nix Flake"}</h1>
<TextInput id="nix-flake-input" label="Load a Nix Flake" val=url suggestions/>
@@ -39,18 +39,18 @@ pub fn NixFlakeRoute(cx: Scope) -> impl IntoView {
}

#[component]
pub fn NixFlakeHomeRoute(cx: Scope) -> impl IntoView {
let url = use_signal::<FlakeUrl>(cx);
let refresh = use_signal::<Refresh>(cx);
pub fn NixFlakeHomeRoute() -> impl IntoView {
let url = use_signal::<FlakeUrl>();
let refresh = use_signal::<Refresh>();
let query = move || (url(), refresh());
let result = query::use_server_query(cx, query, get_flake);
let result = query::use_server_query(query, get_flake);
let data = result.data;
view! { cx,
view! {
<div class="p-2 my-1">
<SuspenseWithErrorHandling>
{move || {
data.with_result(move |flake| {
view! { cx, <FlakeView flake/> }
view! { <FlakeView flake/> }
})
}}

@@ -60,21 +60,21 @@ pub fn NixFlakeHomeRoute(cx: Scope) -> impl IntoView {
}

#[component]
pub fn NixFlakeRawRoute(cx: Scope) -> impl IntoView {
let url = use_signal::<FlakeUrl>(cx);
let refresh = use_signal::<Refresh>(cx);
pub fn NixFlakeRawRoute() -> impl IntoView {
let url = use_signal::<FlakeUrl>();
let refresh = use_signal::<Refresh>();
let query = move || (url(), refresh());
let result = query::use_server_query(cx, query, get_flake);
let result = query::use_server_query(query, get_flake);
let data = result.data;
view! { cx,
view! {
<div>
<A href="/flake">"< Back"</A>
</div>
<div class="px-4 py-2 font-mono text-xs text-left text-gray-500 border-2 border-black">
<SuspenseWithErrorHandling>
{move || {
data.with_result(move |r| {
view! { cx, <FlakeOutputsRawView outs=&r.output/> }
view! { <FlakeOutputsRawView outs=&r.output/> }
})
}}

@@ -84,8 +84,8 @@ pub fn NixFlakeRawRoute(cx: Scope) -> impl IntoView {
}

#[component]
fn FlakeView<'a>(cx: Scope, flake: &'a Flake) -> impl IntoView {
view! { cx,
fn FlakeView<'a>(flake: &'a Flake) -> impl IntoView {
view! {
<div class="flex flex-col my-4">
<h3 class="text-lg font-bold">{flake.url.to_string()}</h3>
<div class="text-sm italic text-gray-600">
@@ -101,18 +101,18 @@ fn FlakeView<'a>(cx: Scope, flake: &'a Flake) -> impl IntoView {
}

#[component]
fn SectionHeading(cx: Scope, title: &'static str) -> impl IntoView {
view! { cx,
fn SectionHeading(title: &'static str) -> impl IntoView {
view! {
<h3 class="p-2 mt-4 mb-2 font-bold bg-gray-300 border-b-2 border-l-2 border-black text-l">
{title}
</h3>
}
}

#[component]
fn FlakeSchemaView<'a>(cx: Scope, schema: &'a FlakeSchema) -> impl IntoView {
fn FlakeSchemaView<'a>(schema: &'a FlakeSchema) -> impl IntoView {
let system = &schema.system.clone();
view! { cx,
view! {
<div>
<h2 class="my-2 ">
<div class="text-xl font-bold text-primary-600">{system.human_readable()}</div>
@@ -133,16 +133,18 @@ fn FlakeSchemaView<'a>(cx: Scope, schema: &'a FlakeSchema) -> impl IntoView {
.map(|v| {
let default = "formatter".to_string();
let k = v.name.as_ref().unwrap_or(&default);
view! { cx, <FlakeValView k v/> }
view! { <FlakeValView k v/> }
})}

<SectionHeading title="Other"/>
{schema
.other
.as_ref()
.map(|v| {
// TODO: Use a non-recursive rendering component?
view! { cx, <FlakeOutputsRawView outs=&FlakeOutputs::Attrset(v.clone())/> }
view! {
// TODO: Use a non-recursive rendering component?
<FlakeOutputsRawView outs=&FlakeOutputs::Attrset(v.clone())/>
}
})}

</div>
@@ -151,31 +153,27 @@ fn FlakeSchemaView<'a>(cx: Scope, schema: &'a FlakeSchema) -> impl IntoView {
}

#[component]
fn BTreeMapView<'a>(
cx: Scope,
title: &'static str,
tree: &'a BTreeMap<String, Val>,
) -> impl IntoView {
fn BTreeMapView<'a>(title: &'static str, tree: &'a BTreeMap<String, Val>) -> impl IntoView {
(!tree.is_empty()).then(move || {
view! { cx,
view! {
<SectionHeading title/>
<BTreeMapBodyView tree/>
}
})
}

#[component]
fn BTreeMapBodyView<'a>(cx: Scope, tree: &'a BTreeMap<String, Val>) -> impl IntoView {
view! { cx,
fn BTreeMapBodyView<'a>(tree: &'a BTreeMap<String, Val>) -> impl IntoView {
view! {
<div class="flex flex-wrap justify-start">
{tree.iter().map(|(k, v)| view! { cx, <FlakeValView k v/> }).collect_view(cx)}
{tree.iter().map(|(k, v)| view! { <FlakeValView k v/> }).collect_view()}
</div>
}
}

#[component]
fn FlakeValView<'a>(cx: Scope, k: &'a String, v: &'a Val) -> impl IntoView {
view! { cx,
fn FlakeValView<'a>(k: &'a String, v: &'a Val) -> impl IntoView {
view! {
<div
title=format!("{:?}", v.type_)
class="flex flex-col p-2 my-2 mr-2 space-y-2 bg-white border-4 border-gray-300 rounded hover:border-gray-400"
@@ -188,14 +186,14 @@ fn FlakeValView<'a>(cx: Scope, k: &'a String, v: &'a Val) -> impl IntoView {
.name
.as_ref()
.map(|v| {
view! { cx, <div class="font-mono text-xs text-gray-500">{v}</div> }
view! { <div class="font-mono text-xs text-gray-500">{v}</div> }
})}

{v
.description
.as_ref()
.map(|v| {
view! { cx, <div class="font-light">{v}</div> }
view! { <div class="font-light">{v}</div> }
})}

</div>
@@ -207,9 +205,9 @@ fn FlakeValView<'a>(cx: Scope, k: &'a String, v: &'a Val) -> impl IntoView {
///
/// WARNING: This may cause performance problems if the tree is large.
#[component]
fn FlakeOutputsRawView<'a>(cx: Scope, outs: &'a FlakeOutputs) -> impl IntoView {
fn view_val<'b>(cx: Scope, val: &'b Val) -> View {
view! { cx,
fn FlakeOutputsRawView<'a>(outs: &'a FlakeOutputs) -> impl IntoView {
fn view_val<'b>(val: &'b Val) -> View {
view! {
<span>
<b>{val.name.clone()}</b>
" ("
@@ -218,12 +216,12 @@ fn FlakeOutputsRawView<'a>(cx: Scope, outs: &'a FlakeOutputs) -> impl IntoView {
<em>{val.description.clone()}</em>
</span>
}
.into_view(cx)
.into_view()
}

#[component]
fn TypeView<'b>(cx: Scope, type_: &'b Type) -> impl IntoView {
view! { cx,
fn TypeView<'b>(type_: &'b Type) -> impl IntoView {
view! {
<span>
{match type_ {
Type::NixosModule => "nixosModule ❄️",
@@ -237,23 +235,23 @@ fn FlakeOutputsRawView<'a>(cx: Scope, outs: &'a FlakeOutputs) -> impl IntoView {
}
}
match outs {
FlakeOutputs::Val(v) => view_val(cx, v),
FlakeOutputs::Attrset(v) => view! { cx,
FlakeOutputs::Val(v) => view_val(v),
FlakeOutputs::Attrset(v) => view! {
<ul class="list-disc">
{v
.iter()
.map(|(k, v)| {
view! { cx,
view! {
<li class="ml-4">
<span class="px-2 py-1 font-bold text-primary-500">{k}</span>
<FlakeOutputsRawView outs=v/>
</li>
}
})
.collect_view(cx)}
.collect_view()}
</ul>
}
.into_view(cx),
.into_view(),
}
}

Loading