Replace the default workspace with a home screen (#660)

This commit is contained in:
Gregory Schier
2026-09-15 13:33:57 -07:00
committed by GitHub
parent 8a6e4810fd
commit 06142d007a
19 changed files with 560 additions and 40 deletions
+2 -4
View File
@@ -6,7 +6,6 @@ use sea_query::{IntoColumnRef, IntoIden, SimpleExpr};
use std::cell::RefCell;
use std::fmt::Debug;
use std::ops::Deref;
use std::sync::mpsc;
use yaak_database::DbContext;
/// A read handle. Comes from the reader pool and can only query.
@@ -80,7 +79,6 @@ impl<'a> ClientDb<'a> {
/// discards them along with the rows.
pub struct WriteDb<'a> {
db: ClientDb<'a>,
events_tx: mpsc::Sender<ModelPayload>,
pending_events: RefCell<Vec<ModelPayload>>,
}
@@ -93,8 +91,8 @@ impl<'a> Deref for WriteDb<'a> {
}
impl<'a> WriteDb<'a> {
pub fn new(ctx: DbContext<'a>, events_tx: mpsc::Sender<ModelPayload>) -> Self {
Self { db: ClientDb::new(ctx), events_tx, pending_events: RefCell::new(Vec::new()) }
pub fn new(ctx: DbContext<'a>) -> Self {
Self { db: ClientDb::new(ctx), pending_events: RefCell::new(Vec::new()) }
}
/// The events for everything written so far, to send once the
+2 -2
View File
@@ -171,11 +171,11 @@ pub fn init_in_memory() -> Result<(QueryManager, BlobManager, mpsc::Receiver<Mod
Ok((query_manager, blob_manager, rx))
}
/// The rows every client assumes exist: settings and at least one workspace.
/// The rows every client assumes exist. Workspaces are not among them: a fresh
/// install has none, and the client shows onboarding until the user makes one.
fn bootstrap(query_manager: &QueryManager) -> Result<()> {
query_manager.with_tx(|tx| {
tx.ensure_settings()?;
tx.ensure_default_workspace()?;
Ok(())
})
}
@@ -192,8 +192,10 @@ mod tests {
#[test]
fn request_resolution_preserves_duplicate_request_headers() {
let (query_manager, _blob_manager, _rx) = init_in_memory().expect("Failed to init DB");
let workspace = query_manager
.with_tx(|tx| tx.upsert_workspace(&Workspace::default(), &UpdateSource::Background))
.expect("Failed to create workspace");
let db = query_manager.connect();
let workspace = db.list_workspaces().expect("Failed to list workspaces").remove(0);
let request = HttpRequest {
workspace_id: workspace.id,
headers: vec![
+21 -20
View File
@@ -79,18 +79,6 @@ impl<'a> ClientDb<'a> {
}
impl<'a> WriteDb<'a> {
/// There is always at least one workspace. Called at startup and after a
/// workspace is deleted.
pub fn ensure_default_workspace(&self) -> Result<()> {
if self.find_all::<Workspace>()?.is_empty() {
self.upsert_workspace(
&Workspace { name: "Yaak".to_string(), ..Default::default() },
&UpdateSource::Background,
)?;
}
Ok(())
}
/// Delete a workspace and everything in it.
///
/// Children are bulk-deleted with one statement per table and are NOT
@@ -138,7 +126,6 @@ impl<'a> WriteDb<'a> {
self.delete_many_untracked::<SyncState>(SyncStateIden::WorkspaceId, wid)?;
self.delete_many_untracked::<WorkspaceMeta>(WorkspaceMetaIden::WorkspaceId, wid)?;
let deleted = self.delete(workspace, source)?;
self.ensure_default_workspace()?;
// Best-effort cleanup of response bodies (disk files and blob chunks).
// Failures only orphan unreferenced data, and are logged.
@@ -193,18 +180,32 @@ pub fn default_headers() -> Vec<HttpRequestHeader> {
#[cfg(test)]
mod tests {
use crate::init_in_memory;
use crate::models::Workspace;
use crate::util::UpdateSource;
#[test]
fn bootstraps_first_workspace_with_real_defaults() {
fn fresh_install_has_no_workspaces() {
let (query_manager, _blob_manager, _rx) = init_in_memory().expect("Failed to init DB");
let db = query_manager.connect();
let workspaces = query_manager.connect().list_workspaces().expect("Failed to list");
assert!(workspaces.is_empty());
}
let workspaces = db.list_workspaces().expect("Failed to list workspaces");
let workspace = workspaces.first().expect("No workspace was bootstrapped");
#[test]
fn default_workspace_carries_real_defaults() {
let (query_manager, _blob_manager, _rx) = init_in_memory().expect("Failed to init DB");
let created = query_manager
.with_tx(|tx| {
tx.upsert_workspace(
&Workspace { name: "Yaak".to_string(), ..Default::default() },
&UpdateSource::Background,
)
})
.expect("Failed to create workspace");
let workspace = query_manager.connect().get_workspace(&created.id).expect("get");
// This workspace is built in Rust and never deserialized, so it only gets
// these values if `Workspace::default()` carries them. Asserted through the
// DB round trip, since the column values are what a fresh install lives with.
// A workspace built in Rust and never deserialized only gets these values
// if `Workspace::default()` carries them. Asserted through the DB round
// trip, since the column values are what the user lives with.
assert!(workspace.setting_send_cookies, "setting_send_cookies");
assert!(workspace.setting_store_cookies, "setting_store_cookies");
assert!(workspace.setting_follow_redirects, "setting_follow_redirects");
+1 -2
View File
@@ -62,8 +62,7 @@ impl QueryManager {
let tx = Transaction::new_unchecked(&conn, TransactionBehavior::Immediate)
.map_err(crate::error::Error::SqlError)?;
let db =
WriteDb::new(DbContext::new(ConnectionOrTx::Transaction(&tx)), self.events_tx.clone());
let db = WriteDb::new(DbContext::new(ConnectionOrTx::Transaction(&tx)));
match func(&db) {
Ok(val) => {