-
-
-
Coco Cloud
+
+
+
+
+ {isConnect ?
+
+
+
+
+ Coco Cloud
+
+
+ Available
+
-
- Available
-
+
+
+
-
-
-
-
-
-
-
Service provision: INFINI Labs
-
|
-
Version Number: v2.3.0
-
|
-
Update time: 2023-05-12
+
+
+
+ Service provision: INFINI Labs
+ |
+ Version Number: v2.3.0
+ |
+ Update time: 2023-05-12
+
+
+ Coco Cloud provides users with a cloud storage and data
+ integration platform that supports account registration and data
+ source management. Users can integrate multiple data sources (such
+ as Google Drive, yuque, GitHub, etc.), easily access and search
+ for files, documents and codes across platforms, and achieve
+ efficient data collaboration and management.
+
+
+
+
+
+ Account Information
+
+ {isLogin ? (
+
+ ) : (
+
+ Login
+
+ )}
-
- Coco Cloud provides users with a cloud storage and data integration
- platform that supports account registration and data source
- management. Users can integrate multiple data sources (such as
- Google Drive, yuque, GitHub, etc.), easily access and search for
- files, documents and codes across platforms, and achieve efficient
- data collaboration and management.
-
-
-
-
Account Information
-
- Login
-
-
-
-
-
+ {isLogin ? : null }
+ :
}
+
);
}
diff --git a/src/components/Auth/ConnectService.tsx b/src/components/Auth/ConnectService.tsx
new file mode 100644
index 0000000..423f227
--- /dev/null
+++ b/src/components/Auth/ConnectService.tsx
@@ -0,0 +1,53 @@
+import React, { useState } from 'react';
+import { ArrowLeft } from 'lucide-react';
+
+export function ConnectService() {
+ const [sourceName, setSourceName] = useState('');
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ console.log('Connecting Google Drive with name:', sourceName);
+ };
+
+ return (
+
+
+
+
+ Connect Google Drive
+
+
+
+
+
+ Coco needs to obtain authorization from your Google Drive account
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/Auth/DataSourceItem.tsx b/src/components/Auth/DataSourceItem.tsx
index f0d1f2d..e8c72b7 100644
--- a/src/components/Auth/DataSourceItem.tsx
+++ b/src/components/Auth/DataSourceItem.tsx
@@ -1,4 +1,4 @@
-import { Link2, Trash2 } from 'lucide-react';
+import { Link2, Trash2 } from "lucide-react";
interface Account {
email: string;
@@ -18,21 +18,19 @@ export function DataSourceItem({ name, type, accounts }: DataSourceItemProps) {
-
+
{name}
- {isConnected ? '管理' : '连接账户'}
+
+ {isConnected ? "Manage" : "Connect Accounts"}
+
{accounts.map((account, index) => (
-
@@ -44,14 +42,14 @@ export function DataSourceItem({ name, type, accounts }: DataSourceItemProps) {
- {index === 0 ? '我的网盘' : `网盘${index + 1}`}
+ {index === 0 ? "My network disk" : `Network disk ${index + 1}`}
{account.email}
- 最近同步: {account.lastSync}
+ Recently Synced: {account.lastSync}
@@ -61,4 +59,4 @@ export function DataSourceItem({ name, type, accounts }: DataSourceItemProps) {
))}
);
-}
\ No newline at end of file
+}
diff --git a/src/components/Auth/DataSourcesList.tsx b/src/components/Auth/DataSourcesList.tsx
index 8c2ce35..213b5f2 100644
--- a/src/components/Auth/DataSourcesList.tsx
+++ b/src/components/Auth/DataSourcesList.tsx
@@ -7,8 +7,8 @@ export function DataSourcesList() {
name: 'Google Drive',
type: 'google',
accounts: [
- { email: 'an121245@gmail.com', lastSync: '2025年1月2日 09:50 AM' },
- { email: '9paiii@gmail.com', lastSync: '2025年1月2日 09:50 AM' }
+ { email: 'an121245@gmail.com', lastSync: '2025-01-02 09:50 AM' },
+ { email: '9paiii@gmail.com', lastSync: '2025-01-02 09:50 AM' }
]
},
{
@@ -27,7 +27,7 @@ export function DataSourcesList() {
return (
-
数据源
+
Data Source
{dataSources.map(source => (
diff --git a/src/components/Auth/Sidebar.tsx b/src/components/Auth/Sidebar.tsx
new file mode 100644
index 0000000..fe94b8a
--- /dev/null
+++ b/src/components/Auth/Sidebar.tsx
@@ -0,0 +1,28 @@
+import { Cloud, Plus } from "lucide-react";
+
+export function Sidebar() {
+ return (
+
+
+
+
+
+
+ Third-party services
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/Auth/UserProfile.tsx b/src/components/Auth/UserProfile.tsx
index 27c8821..63f4f8d 100644
--- a/src/components/Auth/UserProfile.tsx
+++ b/src/components/Auth/UserProfile.tsx
@@ -8,7 +8,6 @@ interface UserProfileProps {
export function UserProfile({ name, email }: UserProfileProps) {
return (
-
账户信息
diff --git a/src/components/Auth/callback.template.ts b/src/components/Auth/callback.template.ts
new file mode 100644
index 0000000..9eed79b
--- /dev/null
+++ b/src/components/Auth/callback.template.ts
@@ -0,0 +1,59 @@
+export default `
+
+
+
+
+
+
+
+
+
Coco Auth
+
+
+
+
+
Coco
+ You are now signed in. Please re-open the Coco desktop app to continue.
+
+
+
+
+`;
diff --git a/src/components/Auth/login2.tsx b/src/components/Auth/login2.tsx
new file mode 100644
index 0000000..05d70ec
--- /dev/null
+++ b/src/components/Auth/login2.tsx
@@ -0,0 +1,55 @@
+import { useEffect } from 'react';
+import { Github, Mail, Apple } from 'lucide-react';
+import { useSearchParams } from "react-router-dom";
+
+import { LoginForm } from '@/components/Auth/LoginForm';
+import { SocialButton } from '@/components/Auth/SocialButton';
+import { Divider } from '@/components/Auth/Divider';
+import { authWitheGithub } from '@/utils/index';
+
+export default function LoginPage() {
+ const [searchParams] = useSearchParams();
+ const uid = searchParams.get("uid");
+ const code = searchParams.get("code");
+
+ useEffect(()=>{
+
+ }, [code])
+
+ function GithubClick() {
+ uid && authWitheGithub(uid)
+ }
+
+ return (
+
+
+
+
Welcome Back
+
Sign in to continue to Coco
+
+
+
+ }
+ provider="GitHub"
+ onClick={() => GithubClick()}
+ />
+ }
+ provider="Google"
+ onClick={() => console.log('Google login')}
+ />
+ }
+ provider="Apple"
+ onClick={() => console.log('Apple login')}
+ />
+
+
+
+
+
console.log(email, password)} />
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/Settings/Account.tsx b/src/components/Settings/Account.tsx
index 11d1c3a..dc80691 100644
--- a/src/components/Settings/Account.tsx
+++ b/src/components/Settings/Account.tsx
@@ -1,13 +1,133 @@
+import { useState, useEffect } from "react";
+import { useNavigate } from "react-router-dom";
import { v4 as uuidv4 } from "uuid";
+import { invoke } from "@tauri-apps/api/core";
+import { listen } from "@tauri-apps/api/event";
+import { getCurrentWindow } from "@tauri-apps/api/window";
+import * as shell from "@tauri-apps/plugin-shell";
import { useAppStore } from "@/stores/appStore";
+import { useAuthStore } from "@/stores/authStore";
import { OpenBrowserURL } from "@/utils/index";
import logoImg from "@/assets/32x32.png";
+import callbackTemplate from "@/components/Auth/callback.template";
+import { clientEnv } from "@/utils/env";
+
export default function Account() {
const app_uid = useAppStore((state) => state.app_uid);
const setAppUid = useAppStore((state) => state.setAppUid);
+ const { auth, setAuth } = useAuthStore();
+
+ const navigate = useNavigate();
+
+ const [loading, setLoading] = useState(false);
+
+ useEffect(() => {
+ let unsubscribe: (() => void) | undefined;
+
+ const setupAuthListener = async () => {
+ try {
+ if (!auth) {
+ // Replace the current route with signin
+ // navigate("/signin", { replace: true });
+ }
+ } catch (error) {
+ console.error("Failed to set up auth listener:", error);
+ }
+ };
+
+ setupAuthListener();
+
+ // Clean up logic on unmount
+ return () => {
+ const cleanup = async () => {
+ try {
+ await invoke("plugin:oauth|stop");
+ } catch (e) {
+ // Ignore errors if no server is running
+ }
+ if (unsubscribe) {
+ unsubscribe();
+ }
+ };
+
+ cleanup();
+ };
+ }, [auth]);
+
+ async function signIn() {
+ let res: (url: URL) => void;
+
+ try {
+ const stopListening = await listen(
+ "oauth://url",
+ (data: { payload: string }) => {
+ if (!data.payload.includes("token")) {
+ return;
+ }
+
+ const urlObject = new URL(data.payload);
+ res(urlObject);
+ }
+ );
+
+ // Stop any existing OAuth server first
+ try {
+ await invoke("plugin:oauth|stop");
+ } catch (e) {
+ // Ignore errors if no server is running
+ }
+
+ const port: string = await invoke("plugin:oauth|start", {
+ config: {
+ response: callbackTemplate,
+ headers: {
+ "Content-Type": "text/html; charset=utf-8",
+ "Cache-Control": "no-store, no-cache, must-revalidate",
+ Pragma: "no-cache",
+ },
+ // Add a cleanup function to stop the server after handling the request
+ cleanup: true,
+ },
+ });
+
+ await shell.open(
+ `${clientEnv.COCO_SERVER_URL}/api/desktop/session/request?port=${port}`
+ );
+
+ const url = await new Promise
((r) => {
+ res = r;
+ });
+ stopListening();
+
+ const token = url.searchParams.get("token");
+ const user_id = url.searchParams.get("user_id");
+ const expires = Number(url.searchParams.get("expires"));
+ if (!token || !expires || !user_id) {
+ throw new Error("Invalid token or expires");
+ }
+
+ await setAuth({
+ token,
+ user_id,
+ expires,
+ plan: { upgraded: false, last_checked: 0 },
+ });
+
+ getCurrentWindow()
+ .setFocus()
+ .catch(() => {});
+
+ return navigate("/");
+ } catch (error) {
+ console.error("Sign in failed:", error);
+ await setAuth(undefined);
+ throw error;
+ }
+ }
+
async function initializeUser() {
let uid = app_uid;
if (!uid) {
@@ -22,6 +142,15 @@ export default function Account() {
// const { token } = await response.json();
// localStorage.setItem("auth_token", token);
OpenBrowserURL(`http://localhost:1420/login?uid=${uid}`);
+
+ setLoading(true);
+ try {
+ await signIn();
+ } catch (error) {
+ console.error(error);
+ } finally {
+ setLoading(false);
+ }
}
function LoginClick(event: React.MouseEvent) {
@@ -59,7 +188,7 @@ export default function Account() {
className="text-sm/6 font-semibold text-gray-900 dark:text-gray-100"
onClick={LoginClick}
>
- Log In →
+ {loading ? "Signing In..." : "Sign In"}
diff --git a/src/components/Settings/index2.tsx b/src/components/Settings/index2.tsx
index 50dc8e1..54d1b85 100644
--- a/src/components/Settings/index2.tsx
+++ b/src/components/Settings/index2.tsx
@@ -7,7 +7,7 @@ import SettingsPanel from "./SettingsPanel";
import GeneralSettings from "./GeneralSettings";
import AboutView from "./AboutView";
import Account from "./Account";
-// import CocoCloud from "@/components/Auth/CocoCloud"
+import CocoCloud from "@/components/Auth/CocoCloud"
import Footer from "../Footer";
import { useTheme } from "../../contexts/ThemeContext";
import { AppTheme } from "../../utils/tauri";
@@ -25,7 +25,7 @@ function SettingsPage() {
const tabs = [
{ name: "General", icon: Settings },
{ name: "Extensions", icon: Puzzle },
- { name: "Account", icon: User },
+ { name: "Connect", icon: User },
{ name: "Advanced", icon: Settings2 },
{ name: "About", icon: Info },
];
@@ -79,7 +79,7 @@ function SettingsPage() {
- {/* */}
+
diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx
index f8a9983..5d09503 100644
--- a/src/pages/login/index.tsx
+++ b/src/pages/login/index.tsx
@@ -1,55 +1,124 @@
-import { Github, Mail, Apple } from 'lucide-react';
+import { useEffect } from "react";
+import { GoogleOAuthProvider } from "@react-oauth/google";
import { useSearchParams } from "react-router-dom";
-import { LoginForm } from '@/components/Auth/LoginForm';
-import { SocialButton } from '@/components/Auth/SocialButton';
-import { Divider } from '@/components/Auth/Divider';
-import { authWitheGithub } from '@/utils/index';
-import { useEffect } from 'react';
+import { authWitheGithub } from "@/utils/index";
+import loginImg from "@/assets/images/bg-login.png";
+import logoImg from "@/assets/images/coco-logo.png";
+import AppleImg from "@/assets/images/apple.png";
+import GithubImg from "@/assets/images/github.png";
+import GoogleImg from "@/assets/images/google.png";
export default function LoginPage() {
+ const handleGoogleSignIn = (response: any) => {
+ console.log("Google Login Success:", response);
+ // response.credential
+ };
+
const [searchParams] = useSearchParams();
const uid = searchParams.get("uid");
const code = searchParams.get("code");
- useEffect(()=>{
-
- }, [code])
+ useEffect(() => {}, [code]);
- function GithubClick() {
- uid && authWitheGithub(uid)
+ function handleGithubSignIn() {
+ uid && authWitheGithub(uid);
}
- return (
-
-
-
-
Welcome Back
-
Sign in to continue to Coco
-
+ const clientId = "YOUR_APPLE_CLIENT_ID";
+ const redirectUri = "http://localhost:3000";
+ const scope = "name email";
-
-
}
- provider="GitHub"
- onClick={() => GithubClick()}
- />
-
}
- provider="Google"
- onClick={() => console.log('Google login')}
- />
-
}
- provider="Apple"
- onClick={() => console.log('Apple login')}
+ const handleAppleSignIn = () => {
+ const authUrl = `https://appleid.apple.com/auth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code`;
+ window.location.href = authUrl;
+ };
+
+ return (
+
+
+ {/* Background Image */}
+
+
+
-
+ {/* Content */}
+
+ {/* Logo */}
+
+
+
-
console.log(email, password)} />
+ {/* Main Text */}
+
+
+ INSERT
+
+ THE
+
+ STRAW
+
+
+ LET'S BEGIN!
+
+
+ With Coco AI, accessing your data is as easy as sipping fresh
+ coconut juice.
+
+
+
+ {/* Social Login Buttons */}
+
+ Sign in With
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Footer */}
+
+ © {new Date().getFullYear()} Coco Labs. All rights reserved.
+
);
-}
\ No newline at end of file
+}
diff --git a/src/stores/authStore.ts b/src/stores/authStore.ts
new file mode 100644
index 0000000..314a816
--- /dev/null
+++ b/src/stores/authStore.ts
@@ -0,0 +1,34 @@
+import { create } from "zustand";
+import { persist } from "zustand/middleware";
+
+export type Plan = {
+ upgraded: boolean;
+ last_checked: number;
+};
+
+export type AuthStore = {
+ token: string;
+ user_id: string | null;
+ expires: number;
+ plan: Plan | null;
+};
+
+export type IAuthStore = {
+ auth: AuthStore | undefined;
+ setAuth: (auth: AuthStore | undefined) => void;
+ resetAuth: () => void;
+};
+
+export const useAuthStore = create
()(
+ persist(
+ (set) => ({
+ auth: undefined,
+ setAuth: (auth) => set({ auth }),
+ resetAuth: () => set({ auth: undefined }),
+ }),
+ {
+ name: "auth-store",
+ partialize: (state) => ({ auth: state.auth }),
+ }
+ )
+);