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
269 changes: 152 additions & 117 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@tauri-apps/plugin-dialog": "^2.2.0",
"@tauri-apps/plugin-fs": "^2.0.3",
"@tauri-apps/plugin-log": "^2.2.0",
"@tauri-apps/plugin-shell": "^2.2.0",
"@tauri-apps/plugin-updater": "^2.0.0",
"@tiptap/extension-highlight": "^2.10.3",
"@tiptap/extension-placeholder": "^2.10.3",
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ flume = "0.11.1"
objc = "0.2.7"
cap-media = { workspace = true }
tauri-plugin-stronghold = "2.2.0"
tauri-plugin-shell = "2.2.0"
3 changes: 2 additions & 1 deletion apps/desktop/src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"sql:default",
"sql:allow-execute",
"store:default",
"stronghold:default"
"stronghold:default",
"shell:allow-open"
]
}
34 changes: 34 additions & 0 deletions apps/desktop/src-tauri/src/auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// https://github.com/CapSoftware/Cap/blob/8671050aaff780f658507579e7d1d75e7ee25d59/apps/desktop/src-tauri/src/auth.rs

use serde::{Deserialize, Serialize};
use specta::Type;

use tauri::{AppHandle, Runtime};
use tauri_plugin_store::StoreExt;

#[derive(Debug, Serialize, Deserialize, Type)]
pub struct AuthStore {
pub token: String,
}

impl AuthStore {
pub fn load<R: Runtime>(app: &AppHandle<R>) -> Result<Option<Self>, String> {
let Some(store) = app
.store("store")
.map(|s| s.get("auth"))
.map_err(|e| e.to_string())?
else {
return Ok(None);
};

serde_json::from_value(store).map_err(|e| e.to_string())
}

pub fn get<R: Runtime>(app: &AppHandle<R>) -> Result<Option<Self>, String> {
let Some(Some(store)) = app.get_store("store").map(|s| s.get("auth")) else {
return Ok(None);
};

serde_json::from_value(store).map_err(|e| e.to_string())
}
}
40 changes: 27 additions & 13 deletions apps/desktop/src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use crate::{audio, config, permissions};
use crate::{audio, auth::AuthStore, config::ConfigStore, permissions, App};
use anyhow::Result;
use cap_media::feeds::AudioInputFeed;
use std::path::PathBuf;
use tauri::{AppHandle, Manager};
use tauri_plugin_store::StoreExt;
use std::{path::PathBuf, sync::Arc};
use tauri::{AppHandle, Manager, State};
use tokio::sync::RwLock;

type MutableState<'a, T> = State<'a, Arc<RwLock<T>>>;

#[tauri::command]
#[specta::specta]
Expand Down Expand Up @@ -68,6 +71,18 @@ pub fn stop_recording() {
audio::AppSounds::StopRecording.play();
}

#[tauri::command]
#[specta::specta]
pub async fn auth_url(state: MutableState<'_, App>) -> Result<String, ()> {
let state = state.read().await;
let client = hypr_cloud::Client::new(state.cloud_config.clone());

let url = client
.get_authentication_url(hypr_cloud::AuthKind::GoogleOAuth)
.to_string();
Ok(url)
}

#[tauri::command]
#[specta::specta]
pub fn list_recordings(app: AppHandle) -> Result<Vec<(String, PathBuf)>, String> {
Expand All @@ -82,17 +97,16 @@ pub fn list_recordings(app: AppHandle) -> Result<Vec<(String, PathBuf)>, String>

#[tauri::command]
#[specta::specta]
pub fn set_config(app: AppHandle, config: config::Config) {
let store = app.store("store.json").unwrap();
store.set("config", serde_json::json!(config));
pub fn is_authenticated(app: AppHandle) -> bool {
AuthStore::get(&app).is_ok()
}

#[tauri::command]
#[specta::specta]
pub fn get_config(app: AppHandle) -> config::Config {
let store = app.store("store.json").unwrap();
let value = store.get("config").unwrap();
serde_json::from_value(value).unwrap()
pub enum AuthProvider {
Google,
}

pub fn login(provider: AuthProvider) -> Result<(), String> {
Ok(())
}

fn recordings_path(app: &AppHandle) -> PathBuf {
Expand Down
43 changes: 34 additions & 9 deletions apps/desktop/src-tauri/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,50 @@
use serde::{Deserialize, Serialize};
use specta::Type;

use tauri::{AppHandle, Runtime};
use tauri_plugin_store::StoreExt;

#[derive(Debug, Serialize, Deserialize, Type)]
#[serde(untagged)]
pub enum Config {
pub enum ConfigStore {
V0(ConfigV0),
}

#[derive(Debug, Serialize, Deserialize, Type)]
pub struct ConfigV0 {
pub version: u8,
pub language: Language,
pub user_name: String,
impl ConfigStore {
pub fn load<R: Runtime>(app: &AppHandle<R>) -> Result<Option<Self>, String> {
let Some(store) = app
.store("store")
.map(|s: std::sync::Arc<tauri_plugin_store::Store<R>>| s.get("config"))
.map_err(|e| e.to_string())?
else {
return Ok(None);
};

serde_json::from_value(store).map_err(|e| e.to_string())
}

pub fn get<R: Runtime>(app: &AppHandle<R>) -> Result<Option<Self>, String> {
let Some(Some(store)) = app.get_store("store").map(|s| s.get("config")) else {
return Ok(None);
};

serde_json::from_value(store).map_err(|e| e.to_string())
}
}

impl Default for Config {
impl Default for ConfigStore {
fn default() -> Self {
Self::V0(ConfigV0::default())
}
}

#[derive(Debug, Serialize, Deserialize, Type)]
pub struct ConfigV0 {
pub version: u8,
pub language: Language,
pub user_name: String,
}

impl Default for ConfigV0 {
fn default() -> Self {
Self {
Expand Down Expand Up @@ -48,9 +73,9 @@ mod tests {

#[test]
fn test_default_config() {
let config = Config::default();
let config = ConfigStore::default();
match config {
Config::V0(cfg_v0) => {
ConfigStore::V0(cfg_v0) => {
assert_eq!(cfg_v0.version, 0);
}
}
Expand Down
11 changes: 9 additions & 2 deletions apps/desktop/src-tauri/src/events.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
use serde::{Deserialize, Serialize};
use specta::Type;
use tauri_specta::Event;

#[derive(Type, Event)]
pub struct Transcript {}
#[derive(Debug, Clone, Serialize, Deserialize, Type, Event)]
pub struct Transcript;

#[derive(Debug, Clone, Serialize, Deserialize, Type, Event)]
pub struct NotAuthenticated;

#[derive(Debug, Clone, Serialize, Deserialize, Type, Event)]
pub struct JustAuthenticated;
84 changes: 62 additions & 22 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
use cap_media::feeds::{AudioInputFeed, AudioInputSamplesSender};
use tauri::{AppHandle, Manager};
use tokio::sync::RwLock;

use tauri::{AppHandle, Manager};
use tauri_plugin_deep_link::DeepLinkExt;
use tauri_specta::Event;

mod audio;
mod auth;
mod commands;
mod config;
mod db;
mod events;
mod permissions;
mod session;

#[derive(specta::Type)]
#[serde(rename_all = "camelCase")]
pub struct App {
#[serde(skip)]
handle: AppHandle,
#[serde(skip)]
audio_input_feed: Option<AudioInputFeed>,
#[serde(skip)]
audio_input_tx: AudioInputSamplesSender,
cloud_config: hypr_cloud::ClientConfig,
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let specta_builder = tauri_specta::Builder::new()
.commands(tauri_specta::collect_commands![
commands::set_config,
commands::get_config,
commands::list_audio_devices,
commands::start_recording,
commands::stop_recording,
Expand All @@ -35,8 +33,13 @@ pub fn run() {
commands::list_apple_calendars,
commands::list_apple_events,
permissions::open_permission_settings,
commands::auth_url,
])
.events(tauri_specta::collect_events![
events::Transcript,
events::NotAuthenticated,
events::JustAuthenticated,
])
.events(tauri_specta::collect_events![events::Transcript])
.typ::<hypr_calendar::apple::Calendar>()
.typ::<hypr_calendar::apple::Event>()
.error_handling(tauri_specta::ErrorHandlingMode::Throw);
Expand Down Expand Up @@ -84,14 +87,36 @@ pub fn run() {
);
}

builder = builder.plugin(tauri_plugin_deep_link::init());
builder = builder.plugin(tauri_plugin_deep_link::init()).setup(|app| {
let app_handle = app.handle().clone();

app.deep_link().on_open_url(move |event| {
let urls = event.urls();
let url = urls.first().unwrap();

if url.path() == "/auth" {
let query_pairs: std::collections::HashMap<String, String> = url
.query_pairs()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();

let local_data_dir = app_handle.path().app_local_data_dir().unwrap();
let file_path = local_data_dir.join("api_key.txt");
let key = query_pairs.get("key").unwrap().clone();

std::fs::write(&file_path, key).unwrap();
let _ = events::JustAuthenticated.emit(&app_handle);
}
});

Ok(())
});

// https://v2.tauri.app/plugin/deep-linking/#registering-desktop-deep-links-at-runtime
#[cfg(any(windows, target_os = "linux"))]
{
builder = builder.setup(|app| {
{
use tauri_plugin_deep_link::DeepLinkExt;
app.deep_link().register_all()?;
}

Expand All @@ -104,10 +129,36 @@ pub fn run() {
builder
// TODO: https://v2.tauri.app/plugin/updater/#building
// .plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_shell::init())
.invoke_handler({
let handler = specta_builder.invoke_handler();
move |invoke| handler(invoke)
})
.setup(move |app| {
let app = app.handle().clone();

let mut cloud_config = hypr_cloud::ClientConfig {
base_url: if cfg!(debug_assertions) {
"http://localhost:4000".parse().unwrap()
} else {
"https://server.hyprnote.com".parse().unwrap()
},
auth_token: None,
};

if let Ok(Some(auth)) = auth::AuthStore::load(&app) {
cloud_config.auth_token = Some(auth.token);
}

app.manage(RwLock::new(App {
handle: app.clone(),
audio_input_tx,
audio_input_feed: None,
cloud_config,
}));

Ok(())
})
.setup(|app| {
let salt_path = app.path().app_local_data_dir()?.join("salt.txt");
app.handle()
Expand Down Expand Up @@ -141,17 +192,6 @@ pub fn run() {
}
Ok(())
})
.setup(move |app| {
let app = app.handle().clone();

app.manage(RwLock::new(App {
handle: app.clone(),
audio_input_tx,
audio_input_feed: None,
}));

Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
11 changes: 7 additions & 4 deletions apps/desktop/src/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { mockNotes } from "../mocks/data";
import { UpcomingEvents } from "../components/home/UpcomingEvents";
import { PastNotes } from "../components/home/PastNotes";
import { NewUserBanner } from "../components/home/NewUserBanner";
import { invoke } from "@tauri-apps/api/core";

import { open } from "@tauri-apps/plugin-shell";
import { commands } from "../types";

export default function Home() {
const [isNewUser] = useState(true);
Expand Down Expand Up @@ -54,12 +56,13 @@ export default function Home() {
<main className="mx-auto max-w-4xl space-y-8 p-6">
<button
onClick={() => {
invoke("list_calendars").then((calendars) => {
console.log(calendars);
commands.authUrl().then((url) => {
console.log(url);
open(url);
});
}}
>
List Calendars
open auth url
</button>
{isNewUser && <NewUserBanner onDemoClick={handleDemoClick} />}
<UpcomingEvents futureNotes={futureNotes} onNoteClick={handleNoteClick} />
Expand Down
Loading