Untitled
unknown
plain_text
10 months ago
9.9 kB
18
Indexable
import React, { useEffect, useState, useCallback } from "react";
import { onOpenUrl } from "@tauri-apps/plugin-deep-link";
import { useQuery } from "@tanstack/react-query";
import {
requestInputMonitoringPermission,
requestScreenRecordingPermission,
checkInputMonitoringPermission,
checkScreenRecordingPermission,
} from "tauri-plugin-macos-permissions-api";
import StartPage from "@containers/StartPage";
import { getStoreItem, setStoreItem } from "@helpers/storeUtils";
import "./style.css";
import { getUserState } from "@apis/auth";
import { useAuthStore } from "@store/useAuthStore";
import Welcome from "./containers/Welcome";
import { useUserStore } from "./store/useUserStore";
import { getUserInfo } from "./apis/user";
import { useWorkspaceStore } from "./store/useWorkspaceStore";
type AppProps = object;
const App: React.FC<AppProps> = () => {
const [isLoading, setIsLoading] = useState<boolean>(true);
const [accessToken, setAccessToken] = useState<string | null>(null);
const [isInputPermissionGranted, setIsInputPermissionGranted] = useState<boolean>(true);
const [isScreenPermissionGranted, setIsScreenPermissionGranted] = useState<boolean>(true);
const [isMounted, setIsMounted] = useState<boolean>(false);
const setUserState = useUserStore.use.setUserState();
const userState = useUserStore.use.userState();
const isLoggedIn = useAuthStore.use.isLoggedIn();
const user = useAuthStore.use.user();
const setUserInfo = useAuthStore.use.setUserInfo();
const setWorkspaces = useWorkspaceStore.use.setWorkspaces();
const clearAllAuthState = useAuthStore.use.clearAllAuthState();
const clearAllUserState = useUserStore.use.clearAllUserState();
// Callback để xử lý deep link URL
const handleDeepLinkUrl = useCallback(async (urls: string[]) => {
console.log("🔗 Received deep link URLs:", urls);
setIsLoading(true);
try {
if (!urls || urls.length === 0) {
console.log("⚠️ No URLs received");
return;
}
const url = new URL(urls[0]);
const params = new URLSearchParams(url.search);
const accessTokenValue = params.get("access_token");
const userId = params.get("userId");
console.log("🎯 Extracted from URL:", { accessTokenValue, userId });
if (!accessTokenValue) {
console.log("❌ No access token in URL");
return;
}
// Clear existing auth state trước khi set token mới
clearAllAuthState();
clearAllUserState();
// Lưu token mới vào store
await setStoreItem("accessToken", { accessToken: accessTokenValue });
// Set token vào state để trigger các effect khác
setAccessToken(accessTokenValue);
console.log("✅ Successfully processed deep link authentication");
} catch (error) {
console.error("❌ Error processing deep link URL:", error);
} finally {
// Delay một chút để đảm bảo UI được cập nhật mượt mà
setTimeout(() => {
setIsLoading(false);
}, 1000);
}
}, [clearAllAuthState, clearAllUserState]);
// Setup deep link listener - chạy ngay khi component mount
useEffect(() => {
let unlistenFn: (() => void) | null = null;
const setupDeepLinkListener = async () => {
try {
console.log("🎧 Setting up deep link listener...");
// Register deep link listener
unlistenFn = await onOpenUrl(handleDeepLinkUrl);
console.log("✅ Deep link listener registered successfully");
} catch (error) {
console.error("❌ Error setting up deep link listener:", error);
} finally {
setIsMounted(true);
setIsLoading(false);
}
};
setupDeepLinkListener();
// Cleanup function
return () => {
if (unlistenFn) {
console.log("🧹 Cleaning up deep link listener");
unlistenFn();
}
};
}, [handleDeepLinkUrl]);
// Load existing access token from storage on startup
useEffect(() => {
const loadExistingToken = async () => {
if (!isMounted) return;
try {
const token = await getStoreItem<{ accessToken: string }>("accessToken");
if (token && token.accessToken) {
console.log("📱 Found existing access token");
setAccessToken(token.accessToken);
} else {
console.log("🔍 No existing access token found");
setIsLoading(false);
}
} catch (error) {
console.error("❌ Error loading existing token:", error);
setIsLoading(false);
}
};
loadExistingToken();
}, [isMounted]);
// Monitor access token periodically (giảm frequency xuống)
useEffect(() => {
if (!isMounted) return;
const checkAccessToken = async () => {
try {
const token = await getStoreItem<{ accessToken: string }>("accessToken");
if (!token || !token.accessToken) {
console.log("🔄 Access token not found during periodic check");
clearAllAuthState();
clearAllUserState();
setAccessToken(null);
}
} catch (error) {
console.error("❌ Error during periodic token check:", error);
clearAllAuthState();
clearAllUserState();
setAccessToken(null);
}
};
// Check every 5 seconds instead of 1 second to reduce overhead
const interval = setInterval(checkAccessToken, 5000);
return () => clearInterval(interval);
}, [isMounted, clearAllAuthState, clearAllUserState]);
// Grant permissions
useEffect(() => {
const requestPermissions = async () => {
try {
const inputPermission = await checkInputMonitoringPermission();
const screenPermission = await checkScreenRecordingPermission();
setIsInputPermissionGranted(inputPermission);
setIsScreenPermissionGranted(screenPermission);
if (!inputPermission) {
console.log("🔒 Requesting input monitoring permission");
await requestInputMonitoringPermission();
}
if (!screenPermission) {
console.log("🔒 Requesting screen recording permission");
await requestScreenRecordingPermission();
}
} catch (error) {
console.error("❌ Error requesting permissions:", error);
}
};
const timeout = setTimeout(requestPermissions, 1500);
return () => clearTimeout(timeout);
}, []);
// API queries
const { refetch: refetchUserState, isFetching: isFetchingUserState } = useQuery({
queryKey: ["userState"],
queryFn: async () => {
console.log("🔄 Fetching user state...");
const response = await getUserState();
if ("data" in response && response.data) {
setUserState(response.data);
return response.data;
} else {
setUserState(null);
return null;
}
},
enabled: false,
});
const { refetch: refetchUserInfo, isFetching: isFetchingUserInfo } = useQuery({
queryKey: ["userInfo"],
queryFn: async () => {
console.log("🔄 Fetching user info...");
const response = await getUserInfo();
if ("data" in response && response.data) {
setUserInfo(response.data.user);
setWorkspaces(response.data.workspaces);
return response.data;
} else {
setUserInfo(null);
return null;
}
},
enabled: false,
});
// Fetch user data when access token changes
useEffect(() => {
const fetchUserData = async () => {
if (accessToken) {
console.log("🚀 Access token available, fetching user data...");
try {
await Promise.all([
refetchUserInfo(),
refetchUserState()
]);
console.log("✅ User data fetched successfully");
} catch (error) {
console.error("❌ Error fetching user data:", error);
}
}
};
fetchUserData();
}, [accessToken, refetchUserInfo, refetchUserState]);
const isShowingLoadingState = isFetchingUserState || isFetchingUserInfo || isLoading;
return (
<div className="min-h-screen">
{isShowingLoadingState ? (
<div className="flex justify-center items-center min-h-screen bg-gradient-to-br from-[#E8D9C7] to-[#CBC7F2]">
<span className="loading loading-ring w-20 h-20 text-primary"></span>
</div>
) : (
<div>
{!isLoggedIn ? (
<StartPage />
) : (
<Welcome user={user!} userState={userState!} />
)}
</div>
)}
{/* Permission notifications */}
{!isInputPermissionGranted && (
<div className="fixed bottom-0 left-0 right-0 bg-red-500 text-white p-2 text-center flex gap-4 items-center justify-center">
Please grant input monitoring permission to use this app.
<button
className="border-white border bg-white/10 text-sm font-light text-white px-2 py-1 rounded-lg cursor-pointer"
onClick={() => requestInputMonitoringPermission()}
>
Manage Permissions
</button>
</div>
)}
{!isScreenPermissionGranted && (
<div className="fixed bottom-0 left-0 right-0 bg-orange-700 text-white p-2 text-center flex gap-4 items-center justify-center">
Please grant screen recording permission to create desktop flows
<button
className="border-white border bg-white/10 text-sm font-light text-white px-2 py-1 rounded-lg cursor-pointer"
onClick={() => requestScreenRecordingPermission()}
>
Manage Permissions
</button>
</div>
)}
</div>
);
};
export default App;Editor is loading...
Leave a Comment