Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
9 changes: 9 additions & 0 deletions desktop/src-tauri/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ fn main() {
println!("cargo:rerun-if-env-changed=BUZZ_BUILD_BUZZ_AGENT_MODEL");
println!("cargo:rerun-if-env-changed=BUZZ_BUILD_AGENT_ENV");
println!("cargo:rerun-if-env-changed=BUZZ_BUILD_RELAY_RECONNECT_CMD");
println!("cargo:rerun-if-env-changed=BUZZ_BUILD_OBSERVER_ARCHIVE_DEFAULT");
println!("cargo:rustc-check-cfg=cfg(buzz_updater_enabled)");

if let Ok(relay_url) = std::env::var("BUZZ_RELAY_URL") {
Expand Down Expand Up @@ -72,6 +73,14 @@ fn main() {
println!("cargo:rustc-env=BUZZ_DESKTOP_BUILD_RELAY_RECONNECT_CMD={val}");
}

// Presence-only flag: when set (any non-empty value), observer-feed archive
// defaults to ON for the current identity on first run. OSS builds leave
// this unset → default OFF. No JSON validation needed — the command only
// checks `.is_some()`.
if std::env::var("BUZZ_BUILD_OBSERVER_ARCHIVE_DEFAULT").is_ok() {
println!("cargo:rustc-env=BUZZ_DESKTOP_BUILD_OBSERVER_ARCHIVE_DEFAULT=1");
}

let updater_public_key = std::env::var("BUZZ_UPDATER_PUBLIC_KEY")
.ok()
.map(|value| value.trim().to_string())
Expand Down
2 changes: 2 additions & 0 deletions desktop/src-tauri/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ mod project_repo_paths;
mod project_terminal;
mod relay_members;
mod relay_reconnect;
mod observer_archive;
mod social;
mod teams;
mod updater;
Expand Down Expand Up @@ -70,6 +71,7 @@ pub use project_git_diff::*;
pub use project_terminal::*;
pub use relay_members::*;
pub use relay_reconnect::*;
pub use observer_archive::*;
pub use social::*;
pub use teams::*;
pub use updater::*;
Expand Down
36 changes: 36 additions & 0 deletions desktop/src-tauri/src/commands/observer_archive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! Build-time flag for observer-feed archive default.
//!
//! When `BUZZ_BUILD_OBSERVER_ARCHIVE_DEFAULT` is set at build time (internal
//! builds), `observer_archive_default_enabled()` returns `true` and the
//! frontend auto-seeds an `owner_p` save subscription for the current identity
//! on first run.
//!
//! OSS builds (env var unset) return `false` — no auto-seeding, user opts in
//! manually via the Local Archive settings card.

/// Returns `true` when an internal build has observer-feed archive default-on.
///
/// The frontend calls this once at startup to decide whether to seed the
/// `owner_p` save subscription. The result is stable for the lifetime of the
/// binary — it is baked at compile time.
#[tauri::command]
pub fn observer_archive_default_enabled() -> bool {
option_env!("BUZZ_DESKTOP_BUILD_OBSERVER_ARCHIVE_DEFAULT").is_some()
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_observer_archive_default_enabled_returns_bool() {
// The command must return a plain bool without panicking.
// Whether it's true or false depends on the build environment;
// what we assert here is just that the return type is correct and
// the function is callable.
let result = observer_archive_default_enabled();
// In a standard OSS/test build (no BUZZ_DESKTOP_BUILD_OBSERVER_ARCHIVE_DEFAULT
// baked in), this should be false.
assert!(!result, "expected false in OSS/test build");
}
}
1 change: 1 addition & 0 deletions desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ pub fn run() {
get_agent_memory,
relay_reconnect_hook,
relay_reconnect_hook_configured,
observer_archive_default_enabled,
archive::archive_events,
archive::create_save_subscription,
archive::list_save_subscriptions,
Expand Down
2 changes: 2 additions & 0 deletions desktop/src/app/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
} from "@/features/user-status/hooks";
import { useWorkspaceEmojiLiveUpdates } from "@/features/custom-emoji/hooks";
import { useArchiveSync } from "@/features/local-archive/archiveSyncManager";
import { useObserverArchiveSeed } from "@/features/local-archive/useObserverArchiveSeed";
import { useProfileQuery } from "@/features/profile/hooks";
import {
DEFAULT_SETTINGS_SECTION,
Expand Down Expand Up @@ -149,6 +150,7 @@ export function AppShell() {
usePersonaSync(identityQuery.data?.pubkey);
useAgentsDataRefresh();
useArchiveSync();
useObserverArchiveSeed(identityQuery.data?.pubkey);
const profileQuery = useProfileQuery();
const deferredPubkey = startupReady ? identityQuery.data?.pubkey : undefined;
useRelayAutoHeal();
Expand Down
71 changes: 71 additions & 0 deletions desktop/src/features/local-archive/observerArchivePreference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* Persists whether the user has made an explicit choice about the
* observer-feed archive default-on feature.
*
* The key is identity-scoped so toggling off on one identity doesn't suppress
* the default-on for another identity. The value is:
* "1" → user explicitly enabled (or accepted the default)
* "0" → user explicitly disabled
* null → no explicit choice yet (default-on seeding may still fire)
*
* Device-level localStorage — intentionally not reset on workspace switch
* (the archive subscription itself is identity-scoped in SQLite; this flag
* is just the UI gate that prevents re-seeding after an explicit opt-out).
*/

const KEY_PREFIX = "buzz:observer-archive-default-seeded";

function storageKey(identityPubkey: string): string {
return `${KEY_PREFIX}:${identityPubkey}`;
}

/**
* Returns `true` if the user has already made an explicit choice for this
* identity (either opted in or opted out). When `false`, the seeding path
* may fire.
*/
export function hasExplicitObserverArchiceChoice(
identityPubkey: string,
): boolean {
if (typeof window === "undefined") return true; // SSR/test: treat as set
try {
return window.localStorage.getItem(storageKey(identityPubkey)) !== null;
} catch {
return true; // storage error → treat as set, never auto-seed
}
}

/**
* Mark that the user has made an explicit choice for this identity.
* `enabled` should reflect whether the `owner_p` subscription exists after
* the action (true = seeded/enabled, false = opted out).
*/
export function setExplicitObserverArchiveChoice(
identityPubkey: string,
enabled: boolean,
): void {
if (typeof window === "undefined") return;
try {
window.localStorage.setItem(
storageKey(identityPubkey),
enabled ? "1" : "0",
);
} catch {
// Best-effort — the seeding guard will re-fire on next startup if storage
// is unavailable, but that is safe (create_save_subscription is idempotent).
}
}

/**
* Clear the explicit choice for this identity (for testing / reset flows).
*/
export function clearExplicitObserverArchiveChoice(
identityPubkey: string,
): void {
if (typeof window === "undefined") return;
try {
window.localStorage.removeItem(storageKey(identityPubkey));
} catch {
// ignore
}
}
Loading
Loading