diff --git a/apps/desktop/src-tauri/capabilities/default.json b/apps/desktop/src-tauri/capabilities/default.json index ffad27e2..0dd6e469 100644 --- a/apps/desktop/src-tauri/capabilities/default.json +++ b/apps/desktop/src-tauri/capabilities/default.json @@ -17,6 +17,7 @@ "core:window:allow-set-focus", "core:window:allow-start-dragging", "core:window:allow-set-position", + "core:webview:default", "core:webview:allow-create-webview-window", "core:app:allow-version", "shell:default", diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index 79581574..c5dad46f 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -2232,6 +2232,12 @@ async fn reset_microphone_permissions(app: AppHandle) -> Result<(), ()> { Ok(()) } +#[tauri::command] +#[specta::specta] +async fn is_camera_window_open(app: AppHandle) -> bool { + CapWindow::Camera { ws_port: 0 }.get(&app).is_some() +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub async fn run() { let specta_builder = tauri_specta::Builder::new() @@ -2286,7 +2292,8 @@ pub async fn run() { set_general_settings, delete_auth_open_signin, reset_camera_permissions, - reset_microphone_permissions + reset_microphone_permissions, + is_camera_window_open ]) .events(tauri_specta::collect_events![ RecordingOptionsChanged, diff --git a/apps/desktop/src/routes/(window-chrome)/index.tsx b/apps/desktop/src/routes/(window-chrome)/index.tsx index a41452ad..a1d31348 100644 --- a/apps/desktop/src/routes/(window-chrome)/index.tsx +++ b/apps/desktop/src/routes/(window-chrome)/index.tsx @@ -4,7 +4,6 @@ import { createEventListener } from "@solid-primitives/event-listener"; import { cache, createAsync, redirect, useNavigate } from "@solidjs/router"; import { createMutation, createQuery } from "@tanstack/solid-query"; import { getVersion } from "@tauri-apps/api/app"; -import { Window } from "@tauri-apps/api/window"; import { cx } from "cva"; import { Show, @@ -47,7 +46,7 @@ export const route = { }; export default function () { - const options = createOptionsQuery(); + const { options, setOptions } = createOptionsQuery(); const windows = createQuery(() => listWindows); const videoDevices = createVideoDevicesQuery(); const audioDevices = createQuery(() => listAudioDevices); @@ -87,7 +86,6 @@ export default function () { } }); - // important for sign in redirect, trust me createAsync(() => getAuth()); createUpdateCheck(); @@ -179,12 +177,25 @@ export default function () { if (!item || !options.data) return; - commands.setRecordingOptions({ + setOptions({ ...options.data, audioInputName: item.name !== "No Audio" ? item.name : null, }); }; + onMount(async () => { + if (options.data?.cameraLabel && options.data.cameraLabel !== "No Camera") { + const cameraWindowActive = await commands.isCameraWindowOpen(); + + if (!cameraWindowActive) { + console.log("cameraWindow not found"); + setOptions({ + ...options.data, + }); + } + } + }); + return (
@@ -247,7 +258,7 @@ export default function () { value={selectedWindow() ?? null} onChange={(d: CaptureWindow | null) => { if (!d || !options.data) return; - commands.setRecordingOptions({ + setOptions({ ...options.data, captureTarget: { variant: "window", ...d }, }); @@ -265,7 +276,7 @@ export default function () { return; } if (s === "screen") { - commands.setRecordingOptions({ + setOptions({ ...options.data, captureTarget: { variant: "screen" }, }); @@ -330,12 +341,12 @@ export default function () { if (!options.data) return; if (!item || !item.isCamera) { - await commands.setRecordingOptions({ + await setOptions({ ...options.data, cameraLabel: null, }); } else { - await commands.setRecordingOptions({ + await setOptions({ ...options.data, cameraLabel: item.name, }); @@ -401,7 +412,7 @@ export default function () { return requestPermission("camera"); } if (!options.data?.cameraLabel) return; - commands.setRecordingOptions({ + setOptions({ ...options.data, cameraLabel: null, }); @@ -492,14 +503,14 @@ export default function () { if (permissions?.data?.microphone !== "granted") { await requestPermission("microphone"); if (permissions?.data?.microphone === "granted") { - commands.setRecordingOptions({ + setOptions({ ...options.data!, audioInputName: audioDevice().name, }); } } else { if (!options.data?.audioInputName) return; - commands.setRecordingOptions({ + setOptions({ ...options.data, audioInputName: null, }); diff --git a/apps/desktop/src/routes/(window-chrome)/update.tsx b/apps/desktop/src/routes/(window-chrome)/update.tsx index 7f26f140..c5e90ca3 100644 --- a/apps/desktop/src/routes/(window-chrome)/update.tsx +++ b/apps/desktop/src/routes/(window-chrome)/update.tsx @@ -59,12 +59,13 @@ export default function () {
}> - Update has been installed. Restart Cap to finish updating. -
- - +
+

+ Update has been installed. Restart Cap to finish updating. +

+
+ +
({ @@ -133,7 +132,7 @@ export default function () {
{ - commands.setRecordingOptions({ + setOptions({ ...options(), cameraLabel: null, }); diff --git a/apps/desktop/src/utils/queries.ts b/apps/desktop/src/utils/queries.ts index b6d9ec1f..2c5f48fa 100644 --- a/apps/desktop/src/utils/queries.ts +++ b/apps/desktop/src/utils/queries.ts @@ -1,10 +1,10 @@ import { createQuery, queryOptions } from "@tanstack/solid-query"; -import { createTimer } from "@solid-primitives/timer"; -import { commands } from "./tauri"; +import { commands, RecordingOptions } from "./tauri"; import { createQueryInvalidate } from "./events"; import { createStore, reconcile } from "solid-js/store"; -import { createMemo } from "solid-js"; +import { createEffect, createMemo } from "solid-js"; +import { makePersisted } from "@solid-primitives/storage"; export const listWindows = queryOptions({ queryKey: ["capture", "windows"] as const, @@ -64,15 +64,48 @@ export const getPermissions = queryOptions({ refetchInterval: 1000, }); +type PartialRecordingOptions = Omit; export function createOptionsQuery() { - const options = createQuery(() => getOptions); + const KEY = "recordingOptionsQuery"; + const localState = localStorage.getItem(KEY); + const [state, setState, _init] = makePersisted( + createStore( + localState + ? JSON.parse(localState) + : { + cameraLabel: null, + audioInputName: null, + } + ) + ); + + const setOptions = (newOptions: RecordingOptions) => { + commands.setRecordingOptions(newOptions); + const { captureTarget: _, ...partialOptions } = newOptions; + setState(partialOptions); + }; + + createEffect(() => { + localStorage.setItem(KEY, JSON.stringify(state)); + }); + + const options = createQuery(() => ({ + ...getOptions, + select: (data) => { + if (data && state) { + return { ...data, ...state }; + } + }, + })); + createQueryInvalidate(options, "recordingOptionsChanged"); - return options; + return { options, setOptions }; } export function createCurrentRecordingQuery() { const currentRecording = createQuery(() => getCurrentRecording); + createQueryInvalidate(currentRecording, "currentRecordingChanged"); return currentRecording; diff --git a/apps/desktop/src/utils/tauri.ts b/apps/desktop/src/utils/tauri.ts index 49b0fab3..f02b8f20 100644 --- a/apps/desktop/src/utils/tauri.ts +++ b/apps/desktop/src/utils/tauri.ts @@ -307,6 +307,9 @@ async resetMicrophonePermissions() : Promise> { if(e instanceof Error) throw e; else return { status: "error", error: e as any }; } +}, +async isCameraWindowOpen() : Promise { + return await TAURI_INVOKE("is_camera_window_open"); } }