Skip to content
Open
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
10 changes: 10 additions & 0 deletions apps/desktop/src/components/editor-area/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { commands as analyticsCommands } from "@hypr/plugin-analytics";
import { commands as connectorCommands } from "@hypr/plugin-connector";
import { commands as dbCommands } from "@hypr/plugin-db";
import { commands as miscCommands } from "@hypr/plugin-misc";
import { commands as notificationCommands } from "@hypr/plugin-notification";
import { commands as templateCommands, type Grammar } from "@hypr/plugin-template";
import Editor, { type TiptapEditor } from "@hypr/tiptap/editor";
import Renderer from "@hypr/tiptap/renderer";
Expand Down Expand Up @@ -458,6 +459,15 @@ export function useEnhanceMutation({

if (actualIsLocalLlm) {
setProgress(0);

// Show native notification for local AI model summarization completion
if (sessionId !== onboardingSessionId) {
notificationCommands.getSummarizationNotification().then(enabled => {
if (enabled) {
notificationCommands.showSummarizationCompleteNotification();
}
});
}
}

setEnhanceController(null);
Expand Down
49 changes: 48 additions & 1 deletion apps/desktop/src/components/settings/views/notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Switch } from "@hypr/ui/components/ui/switch";
const schema = z.object({
detect: z.boolean().optional(),
event: z.boolean().optional(),
summarization: z.boolean().optional(),
});

type Schema = z.infer<typeof schema>;
Expand All @@ -27,11 +28,17 @@ export default function NotificationsComponent() {
queryFn: () => notificationCommands.getDetectNotification(),
});

const summarizationNotification = useQuery({
queryKey: ["notification", "summarization"],
queryFn: () => notificationCommands.getSummarizationNotification(),
});

const form = useForm<Schema>({
resolver: zodResolver(schema),
values: {
detect: detectNotification.data ?? false,
event: eventNotification.data ?? false,
summarization: summarizationNotification.data ?? true,
},
});

Expand Down Expand Up @@ -77,6 +84,16 @@ export default function NotificationsComponent() {
},
});

const summarizationMutation = useMutation({
mutationFn: async (v: Schema) => {
notificationCommands.setSummarizationNotification(v.summarization ?? true);
return v.summarization;
},
onSuccess: () => {
summarizationNotification.refetch();
},
});

useEffect(() => {
const subscription = form.watch((value, { name }) => {
if (name === "detect") {
Expand All @@ -85,10 +102,13 @@ export default function NotificationsComponent() {
if (name === "event") {
eventMutation.mutate(value);
}
if (name === "summarization") {
summarizationMutation.mutate(value);
}
});

return () => subscription.unsubscribe();
}, [eventMutation, detectMutation]);
}, [eventMutation, detectMutation, summarizationMutation]);

return (
<div>
Expand Down Expand Up @@ -138,6 +158,33 @@ export default function NotificationsComponent() {
</FormDescription>
</div>

<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="summarization"
render={({ field }) => (
<FormItem className="space-y-6">
<div className="flex flex-row items-center justify-between">
<div>
<FormLabel>
<Trans>Summarization complete notifications</Trans>
</FormLabel>
<FormDescription>
<Trans>
Show notifications when local AI finishes summarizing your meetings.
</Trans>
</FormDescription>
</div>

<FormControl>
<Switch
checked={field.value}
Expand Down
16 changes: 12 additions & 4 deletions apps/desktop/src/locales/en/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,11 @@ msgstr "{weeks} weeks later"
#~ msgid "in {hours} hours"
#~ msgstr "in {hours} hours"

#: src/components/settings/views/notifications.tsx:105
#: src/components/settings/views/notifications.tsx:125
msgid "(Beta) Detect meetings automatically"
msgstr "(Beta) Detect meetings automatically"

#: src/components/settings/views/notifications.tsx:132
#: src/components/settings/views/notifications.tsx:152
msgid "(Beta) Upcoming meeting notifications"
msgstr "(Beta) Upcoming meeting notifications"

Expand Down Expand Up @@ -1209,11 +1209,15 @@ msgstr "Settings"
msgid "Share usage data"
msgstr "Share usage data"

#: src/components/settings/views/notifications.tsx:135
#: src/components/settings/views/notifications.tsx:182
msgid "Show notifications when local AI finishes summarizing your meetings."
msgstr "Show notifications when local AI finishes summarizing your meetings."

#: src/components/settings/views/notifications.tsx:155
msgid "Show notifications when you have meetings starting soon in your calendar."
msgstr "Show notifications when you have meetings starting soon in your calendar."

#: src/components/settings/views/notifications.tsx:108
#: src/components/settings/views/notifications.tsx:128
msgid "Show notifications when you join a meeting."
msgstr "Show notifications when you join a meeting."

Expand Down Expand Up @@ -1253,6 +1257,10 @@ msgstr "Stop"
#~ msgid "Submit Feedback"
#~ msgstr "Submit Feedback"

#: src/components/settings/views/notifications.tsx:179
msgid "Summarization complete notifications"
msgstr "Summarization complete notifications"

#: src/components/right-panel/components/chat/empty-chat-state.tsx:61
msgid "Summarize meeting"
msgstr "Summarize meeting"
Expand Down
16 changes: 12 additions & 4 deletions apps/desktop/src/locales/ko/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,11 @@ msgstr ""
#~ msgid "in {hours} hours"
#~ msgstr ""

#: src/components/settings/views/notifications.tsx:105
#: src/components/settings/views/notifications.tsx:125
msgid "(Beta) Detect meetings automatically"
msgstr ""

#: src/components/settings/views/notifications.tsx:132
#: src/components/settings/views/notifications.tsx:152
msgid "(Beta) Upcoming meeting notifications"
msgstr ""

Expand Down Expand Up @@ -1209,11 +1209,15 @@ msgstr ""
msgid "Share usage data"
msgstr ""

#: src/components/settings/views/notifications.tsx:135
#: src/components/settings/views/notifications.tsx:182
msgid "Show notifications when local AI finishes summarizing your meetings."
msgstr ""

#: src/components/settings/views/notifications.tsx:155
msgid "Show notifications when you have meetings starting soon in your calendar."
msgstr ""

#: src/components/settings/views/notifications.tsx:108
#: src/components/settings/views/notifications.tsx:128
msgid "Show notifications when you join a meeting."
msgstr ""

Expand Down Expand Up @@ -1253,6 +1257,10 @@ msgstr ""
#~ msgid "Submit Feedback"
#~ msgstr ""

#: src/components/settings/views/notifications.tsx:179
msgid "Summarization complete notifications"
msgstr ""

#: src/components/right-panel/components/chat/empty-chat-state.tsx:61
msgid "Summarize meeting"
msgstr ""
Expand Down
9 changes: 9 additions & 0 deletions plugins/notification/js/bindings.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ async startEventNotification() : Promise<null> {
},
async stopEventNotification() : Promise<null> {
return await TAURI_INVOKE("plugin:notification|stop_event_notification");
},
async showSummarizationCompleteNotification() : Promise<null> {
return await TAURI_INVOKE("plugin:notification|show_summarization_complete_notification");
},
async getSummarizationNotification() : Promise<boolean> {
return await TAURI_INVOKE("plugin:notification|get_summarization_notification");
},
async setSummarizationNotification(enabled: boolean) : Promise<null> {
return await TAURI_INVOKE("plugin:notification|set_summarization_notification", { enabled });
}
}

Expand Down
28 changes: 28 additions & 0 deletions plugins/notification/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,31 @@ pub(crate) async fn stop_event_notification<R: tauri::Runtime>(
) -> Result<(), String> {
app.stop_event_notification().map_err(|e| e.to_string())
}

#[tauri::command]
#[specta::specta]
pub(crate) async fn show_summarization_complete_notification<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
) -> Result<(), String> {
app.show_summarization_complete_notification()
.map_err(|e| e.to_string())
}

#[tauri::command]
#[specta::specta]
pub(crate) async fn get_summarization_notification<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
) -> Result<bool, String> {
app.get_summarization_notification()
.map_err(|e| e.to_string())
}

#[tauri::command]
#[specta::specta]
pub(crate) async fn set_summarization_notification<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
enabled: bool,
) -> Result<(), String> {
app.set_summarization_notification(enabled)
.map_err(|e| e.to_string())
}
35 changes: 35 additions & 0 deletions plugins/notification/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub trait NotificationPluginExt<R: tauri::Runtime> {
fn get_detect_notification(&self) -> Result<bool, Error>;
fn set_detect_notification(&self, enabled: bool) -> Result<(), Error>;

fn get_summarization_notification(&self) -> Result<bool, Error>;
fn set_summarization_notification(&self, enabled: bool) -> Result<(), Error>;

fn start_event_notification(&self) -> impl Future<Output = Result<(), Error>>;
fn stop_event_notification(&self) -> Result<(), Error>;

Expand All @@ -24,6 +27,8 @@ pub trait NotificationPluginExt<R: tauri::Runtime> {
fn check_notification_permission(
&self,
) -> impl Future<Output = Result<hypr_notification2::NotificationPermission, Error>>;

fn show_summarization_complete_notification(&self) -> Result<(), Error>;
}

impl<R: tauri::Runtime, T: tauri::Manager<R>> NotificationPluginExt<R> for T {
Expand Down Expand Up @@ -65,6 +70,23 @@ impl<R: tauri::Runtime, T: tauri::Manager<R>> NotificationPluginExt<R> for T {
.map_err(Error::Store)
}

#[tracing::instrument(skip(self))]
fn get_summarization_notification(&self) -> Result<bool, Error> {
let store = self.notification_store();
store
.get(crate::StoreKey::SummarizationNotification)
.map_err(Error::Store)
.map(|v| v.unwrap_or(true)) // Default to enabled
}

#[tracing::instrument(skip(self))]
fn set_summarization_notification(&self, enabled: bool) -> Result<(), Error> {
let store = self.notification_store();
store
.set(crate::StoreKey::SummarizationNotification, enabled)
.map_err(Error::Store)
}

#[tracing::instrument(skip(self))]
async fn start_event_notification(&self) -> Result<(), Error> {
let db_state = self.state::<tauri_plugin_db::ManagedState>();
Expand Down Expand Up @@ -162,4 +184,17 @@ impl<R: tauri::Runtime, T: tauri::Manager<R>> NotificationPluginExt<R> for T {
.await
.map_err(|_| Error::PermissionTimeout)?
}

#[tracing::instrument(skip(self))]
fn show_summarization_complete_notification(&self) -> Result<(), Error> {
let notif = hypr_notification2::Notification {
title: "Meeting Summary Complete".to_string(),
message: "Your meeting has been summarized by local AI".to_string(),
url: Some("hypr://hyprnote.com/notification".to_string()),
timeout: Some(std::time::Duration::from_secs(5)),
};

hypr_notification2::show(notif);
Ok(())
}
}
3 changes: 3 additions & 0 deletions plugins/notification/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ fn make_specta_builder<R: tauri::Runtime>() -> tauri_specta::Builder<R> {
commands::stop_detect_notification::<tauri::Wry>,
commands::start_event_notification::<tauri::Wry>,
commands::stop_event_notification::<tauri::Wry>,
commands::show_summarization_complete_notification::<tauri::Wry>,
commands::get_summarization_notification::<tauri::Wry>,
commands::set_summarization_notification::<tauri::Wry>,
])
.error_handling(tauri_specta::ErrorHandlingMode::Throw)
}
Expand Down
1 change: 1 addition & 0 deletions plugins/notification/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use tauri_plugin_store2::ScopedStoreKey;
pub enum StoreKey {
EventNotification,
DetectNotification,
SummarizationNotification,
}

impl ScopedStoreKey for StoreKey {}
Loading