# Architecture: marstemedia

marstemedia is a desktop (and Android) application built with **Tauri v2**. The UI is written in Rust compiled to WebAssembly using **Yew**, and the backend logic runs as native Rust code inside the Tauri host process.

---

## Big Picture

```
┌─────────────────────────────────────────────────────────┐
│                    Tauri Desktop App                     │
│                                                         │
│  ┌──────────────────────┐   IPC (invoke)   ┌──────────┐ │
│  │   Frontend (Wasm)    │ ──────────────►  │ Backend  │ │
│  │   Yew / Rust         │ ◄──────────────  │ Rust     │ │
│  │   src/               │   JSON results   │ src-tauri│ │
│  └──────────────────────┘                  └──────────┘ │
└─────────────────────────────────────────────────────────┘
         ▲  renders inside a webview (WebKit/Edge)
```

The frontend never touches the network directly for heavy operations — it sends named commands via `window.__TAURI__.core.invoke()` to the backend, which does the actual networking, file I/O, and cryptography.

---

## Frontend (`src/`)

Built with **Yew 0.21** (React-style component model for Rust/Wasm) and **yew-router** for client-side routing.

### Entry point

| File | Role |
|---|---|
| `src/main.rs` | Bootstraps the Wasm app, mounts the `App` component |
| `src/app.rs` | Defines the `Route` enum and the top-level layout (router + navbar) |

### Routes

| Path | Component | File |
|---|---|---|
| `/` | `Home` | `src/pages/home.rs` |
| `/news` | `News` | `src/pages/news.rs` |
| `/greet` | `Greet` | `src/pages/greet.rs` |

### Components

- **`Navbar`** (`src/navbar/navbar.rs`) — Fixed bottom dock with links to all three routes. Reads the current route via `use_route` to highlight the active link.
- **`Home`** (`src/pages/home.rs`) — Nostr feed. Manages hashtag filter state, invokes `fetch_nostr_posts` on load and on user interaction, renders posts as `PostCard` components.
- **`News`** (`src/pages/news.rs`) — Dual-mode news page. Toggles between AI mode (Groq / OpenRouter) and RSS mode. Has a collapsible config panel for API keys and RSS feed management. Renders articles as `NewsCard` with Markdown support via `pulldown-cmark`.

### How the frontend talks to the backend

Every backend call uses the `invoke` wasm-bindgen extern:

```rust
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"], catch)]
async fn invoke(cmd: &str, args: JsValue) -> Result<JsValue, JsValue>;
```

Arguments are serialized with `serde-wasm-bindgen` and results are deserialized back into typed Rust structs.

---

## Backend (`src-tauri/src/`)

Native Rust code that runs in the Tauri host process. Registered commands are the only API surface the frontend can call.

### Entry point

| File | Role |
|---|---|
| `src-tauri/src/main.rs` | Desktop executable entry point, calls `run()` |
| `src-tauri/src/lib.rs` | Configures Tauri: registers `NewsState`, registers all commands |

### Modules

#### `home.rs` — Nostr

Handles the `fetch_nostr_posts` command.

1. Generates a fresh, random throwaway keypair on every call (privacy by design — no persistent identity).
2. Creates an `EasyNostr` client using the temp key (via the local `easy-nostr` library).
3. Connects to three relays: `relay.damus.io`, `nos.lol`, `relay.snort.social`.
4. Fetches posts by hashtag list, or random posts if no tags are given.
5. Maps results to the serializable `LocalPost` struct and returns JSON.

#### `news.rs` — RSS & AI News

Manages `NewsState` (held in Tauri's managed state, shared across calls):

```
NewsState {
    openrouter_key: Mutex<String>
    groq_key:       Mutex<String>
    rss_config:     Mutex<RssConfig>
}
```

**Commands:**

| Command | What it does |
|---|---|
| `load_rss_config` | Reads `rss.ron` from the app config dir; writes 16 default feeds if the file is missing or has fewer than 3 entries |
| `save_rss_urls` | Serializes the feed list to `rss.ron` and updates in-memory state |
| `save_openrouter_key` | Stores the key in `NewsState.openrouter_key` (in-memory only, not persisted to disk) |
| `save_groq_key` | Same for the Groq key |
| `fetch_ai_news` | POSTs to Groq or OpenRouter chat completions API; asks for a short tech news summary in German Markdown |
| `fetch_rss_news` | Iterates the in-memory feed list, fetches each feed with `reqwest`, parses with `feed-rs`, takes up to 3 articles per feed, returns `Vec<NewsArticle>` |

### Local library: `easy-nostr`

Located at `src-tauri/easy-nostr/` (path dependency). Wraps `nostr-sdk` to provide high-level helpers like `get_random_posts()` and `get_posts_by_hashtags()`. Written by the same author (Bytemalte).

---

## Configuration & Persistence

| What | Where | Format |
|---|---|---|
| RSS feed list | `<app_config_dir>/rss.ron` | RON |
| API keys | In-memory (`NewsState`) — lost on restart | — |
| App window config | `src-tauri/tauri.conf.json` | JSON |

API keys are intentionally not persisted to disk. The user re-enters them each session via the config panel.

---

## Build System

| Tool | Role |
|---|---|
| `trunk` | Builds and bundles the Yew/Wasm frontend (`trunk build` / `trunk serve`) |
| `tauri-cli` | Wraps the full build and produces the native app bundle |
| `Trunk.toml` | Trunk configuration (output dir `dist/`, used by Tauri) |
| `justfile` | Developer shortcuts |

The `tauri.conf.json` wires them together:
```
beforeDevCommand:   trunk serve      (starts dev server on :1420)
beforeBuildCommand: trunk build      (builds Wasm)
frontendDist:       ../dist          (Tauri embeds this)
```

### CI / Android

`.gitea/workflows/android.yaml` runs on push to `main` or version tags. It:
1. Clones `easy-nostr` as a private submodule.
2. Installs Rust with four Android targets + `wasm32-unknown-unknown`.
3. Installs Trunk (binary) and `tauri-cli` (cargo).
4. Sets up Android SDK + NDK 25.
5. Runs `cargo tauri android build`.

---

## Data Flow Summary

```
User action
    │
    ▼
Yew component (state update)
    │  invoke("command_name", args_json)
    ▼
Tauri IPC bridge
    │
    ▼
Backend Rust function
    │  network / file I/O
    ▼
External service (Nostr relay / RSS feed / AI API / disk)
    │  serialized result
    ▼
Yew component re-renders
```
