Untitled

 avatar
unknown
plain_text
a year 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