Skip to content

Commit

Permalink
example: add dioxus example
Browse files Browse the repository at this point in the history
  • Loading branch information
louis030195 committed Jul 18, 2024
1 parent ff0cfe1 commit 552dd6b
Show file tree
Hide file tree
Showing 12 changed files with 667 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["screenpipe-server", "screenpipe-vision", "screenpipe-audio", "examples/apps/screen-pipe-app/src-tauri"]
members = ["screenpipe-server", "screenpipe-vision", "screenpipe-audio", "examples/apps/screen-pipe-app/src-tauri", "examples/apps/screenpipe-app-dioxus"]
exclude = []
resolver = "2"

Expand Down
9 changes: 9 additions & 0 deletions examples/apps/screenpipe-app-dioxus/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Generated by Cargo
# will have compiled files and executables
/target/
/dist/
/static/
/.dioxus/

# These are backup files generated by rustfmt
**/*.rs.bk
66 changes: 66 additions & 0 deletions examples/apps/screenpipe-app-dioxus/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
[package]
name = "screenpipe-app"
version = "0.1.0"
authors = ["Louis Beaumont <[email protected]>"]
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

dioxus = { version = "0.5.1", features = ["desktop", "router"] }

# Debug
tracing = "0.1.40"
dioxus-logger = "0.5.0"

serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }

screenpipe-vision = { path = "../../../screenpipe-vision" }

axum = "0.7.5"

# Logging
log = "0.4.17"
env_logger = "0.11.3"

# Tokio
tokio = { version = "1.36.0", features = ["full"] }

# Directory management
dirs = "5.0"

# HTTP client
reqwest = { version = "0.12", features = ["json"] }

# Uuid
uuid = { version = "1.10.0", features = ["v4"] }

# Sentry
sentry = "0.34.0"

# Chrono
chrono = "0.4.3"

anyhow = "1.0"

# M series MacOS
[target.'cfg(target_os = "macos")'.dependencies]
screenpipe-server = { path = "../../../screenpipe-server", features = ["metal"] }
screenpipe-audio = { path = "../../../screenpipe-audio", features = ["metal"] }

# Linux
[target.'cfg(target_os = "linux")'.dependencies]
screenpipe-server = { path = "../../../screenpipe-server", features = [] }
screenpipe-audio = { path = "../../../screenpipe-audio", features = [] }

# Windows
[target.'cfg(target_os = "windows")'.dependencies]
screenpipe-server = { path = "../../../screenpipe-server", features = [] }
screenpipe-audio = { path = "../../../screenpipe-audio", features = [] }

[features]
cuda = ["screenpipe-server/cuda", "screenpipe-audio/cuda"]
metal = ["screenpipe-server/metal", "screenpipe-audio/metal"]
bundle = []
43 changes: 43 additions & 0 deletions examples/apps/screenpipe-app-dioxus/Dioxus.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[application]

# App (Project) Name
name = "screenpipe-app"

# Dioxus App Default Platform
# desktop, web
default_platform = "desktop"

# `build` & `serve` dist path
out_dir = "dist"

# assets file folder
asset_dir = "assets"

[web.app]

# HTML title tag content
title = "screenpipe-app"

[web.watcher]

# when watcher trigger, regenerate the `index.html`
reload_html = true

# which files or dirs will be watcher monitoring
watch_path = ["src", "assets"]

# include `assets` in web platform
[web.resource]

# CSS style file

style = ["tailwind.css"]

# Javascript code file
script = []

[web.resource.dev]

# Javascript code file
# serve: [dev-server] only
script = []
15 changes: 15 additions & 0 deletions examples/apps/screenpipe-app-dioxus/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Development

1. Install npm: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm
2. Install the tailwind css cli: https://tailwindcss.com/docs/installation
3. Run the following command in the root of the project to start the tailwind CSS compiler:

```bash
npx tailwindcss -i ./input.css -o ./assets/tailwind.css --watch
```

Run the following command in the root of the project to start the Dioxus dev server:

```bash
dx serve --hot-reload --platform desktop
```
20 changes: 20 additions & 0 deletions examples/apps/screenpipe-app-dioxus/assets/header.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions examples/apps/screenpipe-app-dioxus/assets/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
body {
background-color: #111216;
}

#main {
margin: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif
}

#links {
width: 400px;
text-align: left;
font-size: x-large;
color: white;
display: flex;
flex-direction: column;
}

#links a {
color: white;
text-decoration: none;
margin-top: 20px;
margin: 10px;
border: white 1px solid;
border-radius: 5px;
padding: 10px;
}

#links a:hover {
background-color: #1f1f1f;
cursor: pointer;
}

#header {
max-width: 1200px;
}
3 changes: 3 additions & 0 deletions examples/apps/screenpipe-app-dioxus/input.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
145 changes: 145 additions & 0 deletions examples/apps/screenpipe-app-dioxus/src/analytics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use log::{error, info};
use reqwest::Client;
use serde_json::json;
use std::fs;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tokio::time::interval;
use uuid::Uuid;

pub struct AnalyticsManager {
client: Client,
posthog_api_key: String,
distinct_id: String,
interval: Duration,
enabled: Arc<Mutex<bool>>,
api_host: String,
}

impl AnalyticsManager {
pub fn new(posthog_api_key: String, distinct_id: String, interval_hours: u64) -> Self {
Self {
client: Client::new(),
posthog_api_key,
distinct_id,
interval: Duration::from_secs(interval_hours * 3600),
enabled: Arc::new(Mutex::new(true)),
api_host: "https://eu.i.posthog.com".to_string(),
}
}

pub async fn send_event(
&self,
event: &str,
properties: Option<serde_json::Value>,
) -> Result<(), Box<dyn std::error::Error>> {
if !*self.enabled.lock().unwrap() {
return Ok(());
}

let posthog_url = format!("{}/capture/", self.api_host);

let mut payload = json!({
"api_key": self.posthog_api_key,
"event": event,
"properties": {
"distinct_id": self.distinct_id,
"$lib": "rust-reqwest",
"timestamp": chrono::Utc::now().to_rfc3339(),
},
});

if let Some(props) = properties {
if let Some(payload_props) = payload["properties"].as_object_mut() {
payload_props.extend(props.as_object().unwrap_or(&serde_json::Map::new()).clone());
}
}

let response = self.client.post(posthog_url).json(&payload).send().await?;

if !response.status().is_success() {
return Err(format!("PostHog API error: {}", response.status()).into());
}

Ok(())
}

pub async fn start_periodic_event(&self) {
let mut interval = interval(self.interval);

loop {
interval.tick().await;
if *self.enabled.lock().unwrap() {
if let Err(e) = self.send_event("app_still_running", None).await {
error!("Failed to send periodic PostHog event: {}", e);
}
}
}
}

pub fn toggle_analytics(&self) -> bool {
let mut enabled = self.enabled.lock().unwrap();
*enabled = !*enabled;
*enabled
}

pub async fn track_search(&self) -> Result<(), Box<dyn std::error::Error>> {
if !*self.enabled.lock().unwrap() {
return Ok(());
}

self.send_event("search_request", None).await
}
}

pub fn get_or_create_unique_id(app_name: &str) -> Result<String, Box<dyn std::error::Error>> {
let home_dir = dirs::home_dir().ok_or("Failed to get home directory")?;
let app_dir = home_dir.join(format!(".{}", app_name));
let id_file = app_dir.join("unique_id");

if !app_dir.exists() {
fs::create_dir_all(&app_dir)?;
}

if id_file.exists() {
Ok(fs::read_to_string(id_file)?)
} else {
let new_id = Uuid::new_v4().to_string();
fs::write(id_file, &new_id)?;
Ok(new_id)
}
}

pub fn start_analytics(
posthog_api_key: String,
app_name: &str,
interval_hours: u64,
) -> Result<Arc<AnalyticsManager>, Box<dyn std::error::Error>> {
let distinct_id = get_or_create_unique_id(app_name)?;
let analytics_manager = Arc::new(AnalyticsManager::new(
posthog_api_key,
distinct_id,
interval_hours,
));

// Send initial event at boot
tokio::spawn({
let analytics_manager = analytics_manager.clone();
async move {
if let Err(e) = analytics_manager.send_event("app_started", None).await {
error!("Failed to send initial PostHog event: {}", e);
}
info!("Analytics started");
}
});

// Start periodic events
tokio::spawn({
let analytics_manager = analytics_manager.clone();
async move {
analytics_manager.start_periodic_event().await;
}
});

Ok(analytics_manager)
}
26 changes: 26 additions & 0 deletions examples/apps/screenpipe-app-dioxus/src/logs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::io::Write;
pub struct MultiWriter {
writers: Vec<Box<dyn Write + Send>>,
}

impl MultiWriter {
pub fn new(writers: Vec<Box<dyn Write + Send>>) -> Self {
MultiWriter { writers }
}
}

impl Write for MultiWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
for writer in &mut self.writers {
writer.write_all(buf)?;
}
Ok(buf.len())
}

fn flush(&mut self) -> std::io::Result<()> {
for writer in &mut self.writers {
writer.flush()?;
}
Ok(())
}
}
Loading

0 comments on commit 552dd6b

Please sign in to comment.