api deserialization, sidebars

This commit is contained in:
Margret Riegert
2026-03-20 02:43:24 -04:00
parent 505cc110aa
commit bdeda666c1
7 changed files with 265 additions and 21 deletions

147
Cargo.lock generated
View File

@@ -1238,8 +1238,11 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
name = "ermine"
version = "0.1.0"
dependencies = [
"serde",
"serde_json",
"slint",
"slint-build",
"ureq",
]
[[package]]
@@ -1653,6 +1656,17 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "getrandom"
version = "0.3.4"
@@ -1866,6 +1880,22 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48ce8546b993eaf241d69ded33b1be6d205dd9857ec879d9d18bd05d3676e144"
[[package]]
name = "http"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
dependencies = [
"bytes",
"itoa",
]
[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "i-slint-backend-linuxkms"
version = "1.15.1"
@@ -3811,6 +3841,20 @@ dependencies = [
"bytemuck",
]
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.17",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "rowan"
version = "0.16.1"
@@ -3892,6 +3936,41 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "rustls"
version = "0.23.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pki-types"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
dependencies = [
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.103.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.22"
@@ -4362,6 +4441,12 @@ dependencies = [
"syn",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "svgtypes"
version = "0.16.1"
@@ -4782,12 +4867,47 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "unty"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
[[package]]
name = "ureq"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc"
dependencies = [
"base64",
"flate2",
"log",
"percent-encoding",
"rustls",
"rustls-pki-types",
"ureq-proto",
"utf-8",
"webpki-roots",
]
[[package]]
name = "ureq-proto"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f"
dependencies = [
"base64",
"http",
"httparse",
"log",
]
[[package]]
name = "url"
version = "2.5.8"
@@ -4827,6 +4947,12 @@ dependencies = [
"xmlwriter",
]
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "utf8_iter"
version = "1.0.4"
@@ -4894,6 +5020,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.2+wasi-0.2.9"
@@ -5160,6 +5292,15 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki-roots"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "weezl"
version = "0.1.12"
@@ -6215,6 +6356,12 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
[[package]]
name = "zerotrie"
version = "0.2.3"

View File

@@ -6,7 +6,10 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = {version = "1.0.149", features = ["derive"]}
serde_json = "1.0.149"
slint = "1.14.1"
ureq = "3.2.0"
[build-dependencies]
slint-build = "1.14.1"

View File

@@ -61,3 +61,10 @@ Notes:
- Trying to use progenitor + openAPI spec to generate rust code, not going that well, and Gwen said it may be better to write these by hand
- progenitor didn't work due to it operating under the assumption that endpoints only return one type, which this spec violates (https://github.com/oxidecomputer/progenitor/issues/950)
- Read through documentation mostly, taking it slow and making sure I understand how things are working, being patient with myself
# 2026-03-20
## 0220
Figured the API endpoint (`/api`) would be a good place to start. Now have successful deserialization.
Sidebars are important, so I added two, one for channel list and another for server list. I'm still trying to figure out the best way to integrate different instances seamlessly.

32
src/api.rs Normal file
View File

@@ -0,0 +1,32 @@
#[derive(serde::Deserialize, Debug)]
pub struct ServerInfo {
// version number
revolt: String,
// main websocket
ws: String,
// main app URL
app: String,
vapid: String,
features: ServerFeatures,
}
#[derive(serde::Deserialize, Debug)]
struct ServerFeatures {
email: bool,
invite_only: bool,
// file server
autumn: Option<ServerFeatureEndpoint>,
// media server
january: Option<ServerFeatureEndpoint>,
//
voso: Option<ServerFeatureEndpoint>,
// voip server
livekit: Option<ServerFeatureEndpoint>,
}
#[derive(serde::Deserialize, Debug)]
struct ServerFeatureEndpoint {
enabled: bool,
url: Option<String>,
ws: Option<String>,
}

View File

@@ -2,10 +2,20 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::error::Error;
mod api;
slint::include_modules!();
fn main() -> Result<(), Box<dyn Error>> {
let response = ureq::get("https://revolt.handmadecities.com/api")
.call()?
.body_mut()
.read_to_string()?;
let server_info: api::ServerInfo = serde_json::from_str(&response).unwrap();
println!("{:#?}", server_info);
let ui = AppWindow::new()?;
// ui.on_request_increase_value({

View File

@@ -1,32 +1,50 @@
import { Button, VerticalBox, TextEdit } from "std-widgets.slint";
import { ChatView } from "chat-view.slint";
import { ChannelSidebar, ServerSidebar } from "sidebar.slint";
export component AppWindow inherits Window {
title: "ermine";
VerticalLayout {
property <bool> sidebar-open: true;
ChatView {
messages: [
{ text: "hello", sender: "Bob", time: "10:00" },
{ text: "hey!", sender: "Joe", time: "10:01" },
{ text: "i like cats c:", sender: "Bob", time: "10:03" },
{ text: "me too c:", sender: "Joe", time: "10:08" },
{
text: "meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow ",
sender: "Joe",
time: "10:08"
},
{
text: "meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow ",
sender: "Bob",
time: "10:12"
},
];
HorizontalLayout {
ServerSidebar {
open: root.width > 600px;
server_list: [{ icon: "😳" }, { icon: "😍" }];
background: yellow;
}
TextEdit {
height: 64px;
font-size: 16px;
ChannelSidebar {
open: root.width > 600px ? sidebar-open : false;
channel_list: [{ title: "Foo" }, { title: "Bar" }];
background: blue;
}
VerticalLayout {
ChatView {
messages: [
{ text: "hello", sender: "Bob", time: "10:00" },
{ text: "hey!", sender: "Joe", time: "10:01" },
{ text: "i like cats c:", sender: "Bob", time: "10:03" },
{ text: "me too c:", sender: "Joe", time: "10:08" },
{
text: "meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow ",
sender: "Joe",
time: "10:08"
},
{
text: "meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow ",
sender: "Bob",
time: "10:12"
},
];
}
TextEdit {
height: 64px;
font-size: 16px;
}
}
}
}

27
ui/sidebar.slint Normal file
View File

@@ -0,0 +1,27 @@
export component ChannelSidebar inherits Rectangle {
in property <bool> open;
in property <[{title: string}]> channel_list;
width: open ? 200px : 0px;
clip: true;
VerticalLayout {
width: 200px;
for channel in channel_list: Text {
text: channel.title;
}
}
}
export component ServerSidebar inherits Rectangle {
in property <bool> open;
in property <[{icon: string}]> server_list;
width: open ? 48px : 0px;
VerticalLayout {
width: 48px;
for server in server_list: Text {
text: server.icon;
}
}
}