mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-08-11 05:30:29 +02:00
refactor: cleanup
This commit is contained in:
@@ -1,54 +0,0 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { useEffect, useState } from "react";
|
||||
import i18n from "@/i18n";
|
||||
|
||||
export function useBrowserSupport() {
|
||||
const [supportedBrowsers, setSupportedBrowsers] = useState<string[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const loadSupportedBrowsers = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
const browsers = await invoke<string[]>("get_supported_browsers");
|
||||
setSupportedBrowsers(browsers);
|
||||
} catch (err) {
|
||||
console.error("Failed to load supported browsers:", err);
|
||||
setError(
|
||||
err instanceof Error
|
||||
? err.message
|
||||
: i18n.t("errors.loadSupportedBrowsersFailed"),
|
||||
);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
void loadSupportedBrowsers();
|
||||
}, []);
|
||||
|
||||
const isBrowserSupported = (browser: string): boolean => {
|
||||
return supportedBrowsers.includes(browser);
|
||||
};
|
||||
|
||||
const checkBrowserSupport = async (browser: string): Promise<boolean> => {
|
||||
try {
|
||||
return await invoke<boolean>("is_browser_supported_on_platform", {
|
||||
browserStr: browser,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(`Failed to check support for browser ${browser}:`, err);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
supportedBrowsers,
|
||||
isLoading,
|
||||
error,
|
||||
isBrowserSupported,
|
||||
checkBrowserSupport,
|
||||
};
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import i18n from "@/i18n";
|
||||
import type { Extension, ExtensionGroup } from "@/types";
|
||||
|
||||
export function useExtensionEvents() {
|
||||
const [extensions, setExtensions] = useState<Extension[]>([]);
|
||||
const [extensionGroups, setExtensionGroups] = useState<ExtensionGroup[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const loadExtensions = useCallback(async () => {
|
||||
try {
|
||||
const exts = await invoke<Extension[]>("list_extensions");
|
||||
setExtensions(exts);
|
||||
setError(null);
|
||||
} catch (err: unknown) {
|
||||
console.error("Failed to load extensions:", err);
|
||||
setExtensions([]);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const loadExtensionGroups = useCallback(async () => {
|
||||
try {
|
||||
const groups = await invoke<ExtensionGroup[]>("list_extension_groups");
|
||||
setExtensionGroups(groups);
|
||||
setError(null);
|
||||
} catch (err: unknown) {
|
||||
console.error("Failed to load extension groups:", err);
|
||||
setExtensionGroups([]);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const loadAll = useCallback(async () => {
|
||||
await Promise.all([loadExtensions(), loadExtensionGroups()]);
|
||||
}, [loadExtensions, loadExtensionGroups]);
|
||||
|
||||
useEffect(() => {
|
||||
let unlisten: (() => void) | undefined;
|
||||
|
||||
const setup = async () => {
|
||||
try {
|
||||
await loadAll();
|
||||
unlisten = await listen("extensions-changed", () => {
|
||||
void loadAll();
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Failed to setup extension event listeners:", err);
|
||||
setError(
|
||||
i18n.t("errors.setupExtensionListenersFailed", {
|
||||
error: JSON.stringify(err),
|
||||
}),
|
||||
);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
void setup();
|
||||
|
||||
return () => {
|
||||
if (unlisten) unlisten();
|
||||
};
|
||||
}, [loadAll]);
|
||||
|
||||
return {
|
||||
extensions,
|
||||
extensionGroups,
|
||||
isLoading,
|
||||
error,
|
||||
loadExtensions,
|
||||
loadExtensionGroups,
|
||||
loadAll,
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { getCurrentOS, type OperatingSystem } from "@/lib/platform";
|
||||
|
||||
// Platform-specific imports
|
||||
let macOSPermissions:
|
||||
@@ -25,21 +26,23 @@ interface UsePermissionsReturn {
|
||||
isMicrophoneAccessGranted: boolean;
|
||||
isCameraAccessGranted: boolean;
|
||||
isInitialized: boolean;
|
||||
currentOS: OperatingSystem | null;
|
||||
requiresSystemPermissions: boolean;
|
||||
}
|
||||
|
||||
export function usePermissions(): UsePermissionsReturn {
|
||||
export function usePermissions(active = true): UsePermissionsReturn {
|
||||
const [isMicrophoneAccessGranted, setIsMicrophoneAccessGranted] =
|
||||
useState(false);
|
||||
const [isCameraAccessGranted, setIsCameraAccessGranted] = useState(false);
|
||||
const [currentPlatform, setCurrentPlatform] = useState<string | null>(null);
|
||||
const [currentOS, setCurrentOS] = useState<OperatingSystem | null>(null);
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
|
||||
// Check permissions status
|
||||
const checkPermissions = useCallback(async () => {
|
||||
if (!currentPlatform) return;
|
||||
if (!currentOS) return;
|
||||
|
||||
if (currentPlatform !== "macos") {
|
||||
if (currentOS !== "macos") {
|
||||
// Windows/Linux - assume permissions are granted
|
||||
setIsMicrophoneAccessGranted(true);
|
||||
setIsCameraAccessGranted(true);
|
||||
@@ -58,19 +61,19 @@ export function usePermissions(): UsePermissionsReturn {
|
||||
|
||||
setIsMicrophoneAccessGranted(micGranted);
|
||||
setIsCameraAccessGranted(camGranted);
|
||||
setIsInitialized(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to check permissions on macOS:", error);
|
||||
} finally {
|
||||
setIsInitialized(true);
|
||||
}
|
||||
}, [currentPlatform]);
|
||||
}, [currentOS]);
|
||||
|
||||
// Request permission
|
||||
const requestPermission = useCallback(
|
||||
async (type: PermissionType): Promise<boolean> => {
|
||||
// Non-macOS platforms do not require this permission gate.
|
||||
if (!currentPlatform || currentPlatform !== "macos") return true;
|
||||
if ((currentOS ?? getCurrentOS()) !== "macos") return true;
|
||||
|
||||
// macOS - use the permissions API
|
||||
try {
|
||||
@@ -96,59 +99,34 @@ export function usePermissions(): UsePermissionsReturn {
|
||||
await permissions.requestCameraPermission();
|
||||
}
|
||||
|
||||
for (let attempt = 0; attempt < 8; attempt += 1) {
|
||||
const granted = await readPermission();
|
||||
if (granted) return true;
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
// Read once immediately. The hook's macOS poll keeps watching for
|
||||
// delayed TCC propagation without holding this request (and any next
|
||||
// system prompt) open for several extra seconds.
|
||||
return readPermission();
|
||||
} catch (error) {
|
||||
console.error(`Failed to request ${type} permission on macOS:`, error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[currentPlatform],
|
||||
[currentOS],
|
||||
);
|
||||
|
||||
// Initialize platform detection and start interval checking
|
||||
useEffect(() => {
|
||||
const initializePlatform = () => {
|
||||
try {
|
||||
// Detect platform - on macOS we need permissions, on others we don't
|
||||
const userAgent = navigator.userAgent;
|
||||
let platformName = "unknown";
|
||||
|
||||
if (userAgent.includes("Mac")) {
|
||||
platformName = "macos";
|
||||
} else if (userAgent.includes("Win")) {
|
||||
platformName = "windows";
|
||||
} else if (userAgent.includes("Linux")) {
|
||||
platformName = "linux";
|
||||
}
|
||||
|
||||
setCurrentPlatform(platformName);
|
||||
} catch (error) {
|
||||
console.error("Failed to detect platform:", error);
|
||||
// Fallback - assume non-macOS
|
||||
setCurrentPlatform("unknown");
|
||||
}
|
||||
};
|
||||
|
||||
initializePlatform();
|
||||
setCurrentOS(getCurrentOS());
|
||||
}, []);
|
||||
|
||||
// Set up interval checking when platform is determined.
|
||||
// On non-macOS platforms, permissions are always granted — a single check
|
||||
// is enough and we skip the interval entirely to avoid burning CPU.
|
||||
useEffect(() => {
|
||||
if (!currentPlatform) return;
|
||||
if (!currentOS || !active) return;
|
||||
|
||||
// Initial check
|
||||
void checkPermissions();
|
||||
|
||||
// Only poll on macOS where permissions can change at runtime
|
||||
if (currentPlatform !== "macos") return;
|
||||
if (currentOS !== "macos") return;
|
||||
|
||||
intervalRef.current = setInterval(() => {
|
||||
void checkPermissions();
|
||||
@@ -160,12 +138,14 @@ export function usePermissions(): UsePermissionsReturn {
|
||||
intervalRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [currentPlatform, checkPermissions]);
|
||||
}, [active, currentOS, checkPermissions]);
|
||||
|
||||
return {
|
||||
requestPermission,
|
||||
isMicrophoneAccessGranted,
|
||||
isCameraAccessGranted,
|
||||
isInitialized,
|
||||
currentOS,
|
||||
requiresSystemPermissions: currentOS === "macos",
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user