168 lines
4.3 KiB
Rust
168 lines
4.3 KiB
Rust
//! Aspirational RUIN app API for composition, typed context providers, and app-wide services.
|
|
//!
|
|
//! Intentionally non-compiling; this is a design sketch.
|
|
|
|
use ruin::prelude::*;
|
|
|
|
#[ruin_runtime::async_main]
|
|
async fn main() -> ruin::Result<()> {
|
|
App::new()
|
|
.window(Window::new().title("RUIN Workspace").size(1400.0, 900.0))
|
|
.mount::<WorkspaceRoot>()
|
|
.run()
|
|
.await
|
|
}
|
|
|
|
#[component]
|
|
fn WorkspaceRoot() -> impl View {
|
|
let route = use_router(Route::Home);
|
|
let session = use_resource(|| async { Session::restore().await });
|
|
let notifications = use_service::<Notifications>();
|
|
|
|
view! {
|
|
provide::<NotificationsContext>(notifications.clone()) {
|
|
provide::<ThemeContext>(Theme::dark()) {
|
|
match session.read() {
|
|
Pending => view! { ProgressSpinner(label = "Restoring session...") {} },
|
|
Ready(session) => view! {
|
|
provide::<SessionContext>(session?) {
|
|
WorkspaceShell(route = route) {}
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
fn WorkspaceShell(route: Signal<Route>) -> impl View {
|
|
let session = use_context::<SessionContext>();
|
|
let notifications = use_context::<NotificationsContext>();
|
|
|
|
use_effect(move || {
|
|
if !session.is_authenticated() {
|
|
notifications.info("Signed in as guest");
|
|
}
|
|
});
|
|
|
|
view! {
|
|
row(fill = true) {
|
|
block(
|
|
role = LandmarkRole::Navigation,
|
|
width = 280,
|
|
gap = 12,
|
|
padding = 20,
|
|
) {
|
|
BrandMark() {}
|
|
NavLink(route = Route::Home, active = route.is(Route::Home)) { "Home" }
|
|
NavLink(route = Route::Projects, active = route.is(Route::Projects)) { "Projects" }
|
|
NavLink(route = Route::Settings, active = route.is(Route::Settings)) { "Settings" }
|
|
}
|
|
|
|
block(fill = true, padding = 20) {
|
|
match route.get() {
|
|
Route::Home => view! { HomeScreen() {} },
|
|
Route::Projects => view! { ProjectsScreen() {} },
|
|
Route::Settings => view! { SettingsScreen() {} },
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
fn HomeScreen() -> impl View {
|
|
let theme = use_context::<ThemeContext>();
|
|
|
|
view! {
|
|
column(fill = true, gap = 16) {
|
|
text(role = TextRole::Heading(1), size = 30, weight = FontWeight::Semibold) {
|
|
"Workspace"
|
|
}
|
|
text(color = theme.muted_text()) {
|
|
"Context should be keyed by provider identity, not only by value type. That allows \
|
|
multiple providers for the same `T` to coexist in the tree while descendants borrow \
|
|
the correct value by asking for a specific provider marker."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
fn ProjectsScreen() -> impl View {
|
|
view! {
|
|
EmptyState(title = "Projects go here") {
|
|
"This screen exists only to show route switching and app-level composition."
|
|
}
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
fn SettingsScreen() -> impl View {
|
|
view! {
|
|
column(gap = 12) {
|
|
text(role = TextRole::Heading(2), size = 24, weight = FontWeight::Semibold) {
|
|
"Settings"
|
|
}
|
|
toggle(label = "Use reduced motion", value = Preferences::reduced_motion()) {}
|
|
toggle(label = "Use system theme", value = Preferences::system_theme()) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[context_provider(Theme)]
|
|
struct ThemeContext;
|
|
|
|
#[context_provider(Session)]
|
|
struct SessionContext;
|
|
|
|
#[context_provider(Notifications)]
|
|
struct NotificationsContext;
|
|
|
|
struct Session;
|
|
struct Notifications;
|
|
struct Theme;
|
|
struct Preferences;
|
|
|
|
#[derive(Clone, Copy)]
|
|
enum Route {
|
|
Home,
|
|
Projects,
|
|
Settings,
|
|
}
|
|
|
|
impl Session {
|
|
async fn restore() -> ruin::Result<Self> {
|
|
Ok(Self)
|
|
}
|
|
|
|
fn is_authenticated(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl Notifications {
|
|
fn info(&self, _message: &str) {}
|
|
}
|
|
|
|
impl Theme {
|
|
fn dark() -> Self {
|
|
Self
|
|
}
|
|
|
|
fn muted_text(&self) -> Color {
|
|
Color::rgb(0x94, 0xA3, 0xB8)
|
|
}
|
|
}
|
|
|
|
impl Preferences {
|
|
fn reduced_motion() -> bool {
|
|
false
|
|
}
|
|
|
|
fn system_theme() -> bool {
|
|
true
|
|
}
|
|
}
|