quick fix
Browse files- app/(public)/projects/page.tsx +2 -5
- app/api/me/route.ts +23 -1
- app/layout.tsx +3 -3
- components/contexts/app-context.tsx +2 -1
- components/editor/ask-ai/index.tsx +4 -1
- components/my-projects/index.tsx +9 -10
- components/pro-modal/index.tsx +5 -1
- hooks/useUser.ts +19 -1
app/(public)/projects/page.tsx
CHANGED
|
@@ -3,10 +3,7 @@ import { MyProjects } from "@/components/my-projects";
|
|
| 3 |
import { NotLogged } from "@/components/not-logged/not-logged";
|
| 4 |
|
| 5 |
export default async function ProjectsPage() {
|
| 6 |
-
const { ok, projects } = await getProjects();
|
| 7 |
-
if (!ok) {
|
| 8 |
-
return <NotLogged />;
|
| 9 |
-
}
|
| 10 |
|
| 11 |
-
return <MyProjects
|
| 12 |
}
|
|
|
|
| 3 |
import { NotLogged } from "@/components/not-logged/not-logged";
|
| 4 |
|
| 5 |
export default async function ProjectsPage() {
|
| 6 |
+
// const { ok, projects } = await getProjects();
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
+
return <MyProjects />;
|
| 9 |
}
|
app/api/me/route.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import { headers } from "next/headers";
|
| 2 |
import { NextResponse } from "next/server";
|
| 3 |
|
|
@@ -21,5 +22,26 @@ export async function GET() {
|
|
| 21 |
);
|
| 22 |
}
|
| 23 |
const user = await userResponse.json();
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
}
|
|
|
|
| 1 |
+
import { listSpaces } from "@huggingface/hub";
|
| 2 |
import { headers } from "next/headers";
|
| 3 |
import { NextResponse } from "next/server";
|
| 4 |
|
|
|
|
| 22 |
);
|
| 23 |
}
|
| 24 |
const user = await userResponse.json();
|
| 25 |
+
const projects = [];
|
| 26 |
+
for await (const space of listSpaces({
|
| 27 |
+
accessToken: token.replace("Bearer ", "") as string,
|
| 28 |
+
additionalFields: ["author", "cardData"],
|
| 29 |
+
search: {
|
| 30 |
+
owner: user.name,
|
| 31 |
+
}
|
| 32 |
+
})) {
|
| 33 |
+
if (
|
| 34 |
+
!space.private &&
|
| 35 |
+
space.sdk === "static" &&
|
| 36 |
+
Array.isArray((space.cardData as { tags?: string[] })?.tags) &&
|
| 37 |
+
(
|
| 38 |
+
((space.cardData as { tags?: string[] })?.tags?.includes("deepsite-v3")) ||
|
| 39 |
+
((space.cardData as { tags?: string[] })?.tags?.includes("deepsite"))
|
| 40 |
+
)
|
| 41 |
+
) {
|
| 42 |
+
projects.push(space);
|
| 43 |
+
}
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
return NextResponse.json({ user, projects, errCode: null }, { status: 200 });
|
| 47 |
}
|
app/layout.tsx
CHANGED
|
@@ -72,16 +72,16 @@ export const viewport: Viewport = {
|
|
| 72 |
async function getMe() {
|
| 73 |
const cookieStore = await cookies();
|
| 74 |
const token = cookieStore.get(MY_TOKEN_KEY())?.value;
|
| 75 |
-
if (!token) return { user: null, errCode: null };
|
| 76 |
try {
|
| 77 |
const res = await apiServer.get("/me", {
|
| 78 |
headers: {
|
| 79 |
Authorization: `Bearer ${token}`,
|
| 80 |
},
|
| 81 |
});
|
| 82 |
-
return { user: res.data.user, errCode: null };
|
| 83 |
} catch (err: any) {
|
| 84 |
-
return { user: null, errCode: err.status };
|
| 85 |
}
|
| 86 |
}
|
| 87 |
|
|
|
|
| 72 |
async function getMe() {
|
| 73 |
const cookieStore = await cookies();
|
| 74 |
const token = cookieStore.get(MY_TOKEN_KEY())?.value;
|
| 75 |
+
if (!token) return { user: null, projects: [], errCode: null };
|
| 76 |
try {
|
| 77 |
const res = await apiServer.get("/me", {
|
| 78 |
headers: {
|
| 79 |
Authorization: `Bearer ${token}`,
|
| 80 |
},
|
| 81 |
});
|
| 82 |
+
return { user: res.data.user, projects: res.data.projects, errCode: null };
|
| 83 |
} catch (err: any) {
|
| 84 |
+
return { user: null, projects: [], errCode: err.status };
|
| 85 |
}
|
| 86 |
}
|
| 87 |
|
components/contexts/app-context.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import { toast } from "sonner";
|
|
| 5 |
import { usePathname, useRouter } from "next/navigation";
|
| 6 |
|
| 7 |
import { useUser } from "@/hooks/useUser";
|
| 8 |
-
import { User } from "@/types";
|
| 9 |
import { useBroadcastChannel } from "@/lib/useBroadcastChannel";
|
| 10 |
|
| 11 |
export default function AppContext({
|
|
@@ -15,6 +15,7 @@ export default function AppContext({
|
|
| 15 |
children: React.ReactNode;
|
| 16 |
me?: {
|
| 17 |
user: User | null;
|
|
|
|
| 18 |
errCode: number | null;
|
| 19 |
};
|
| 20 |
}) {
|
|
|
|
| 5 |
import { usePathname, useRouter } from "next/navigation";
|
| 6 |
|
| 7 |
import { useUser } from "@/hooks/useUser";
|
| 8 |
+
import { ProjectType, User } from "@/types";
|
| 9 |
import { useBroadcastChannel } from "@/lib/useBroadcastChannel";
|
| 10 |
|
| 11 |
export default function AppContext({
|
|
|
|
| 15 |
children: React.ReactNode;
|
| 16 |
me?: {
|
| 17 |
user: User | null;
|
| 18 |
+
projects: ProjectType[];
|
| 19 |
errCode: number | null;
|
| 20 |
};
|
| 21 |
}) {
|
components/editor/ask-ai/index.tsx
CHANGED
|
@@ -28,6 +28,7 @@ import { useLoginModal } from "@/components/contexts/login-context";
|
|
| 28 |
import { Settings } from "./settings";
|
| 29 |
import { useProModal } from "@/components/contexts/pro-context";
|
| 30 |
import { MODELS } from "@/lib/providers";
|
|
|
|
| 31 |
|
| 32 |
export const AskAi = ({
|
| 33 |
project,
|
|
@@ -39,7 +40,7 @@ export const AskAi = ({
|
|
| 39 |
isNew?: boolean;
|
| 40 |
onScrollToBottom?: () => void;
|
| 41 |
}) => {
|
| 42 |
-
const { user } = useUser();
|
| 43 |
const { currentPageData, isUploading, pages, isLoadingProject } = useEditor();
|
| 44 |
const {
|
| 45 |
isAiWorking,
|
|
@@ -86,6 +87,8 @@ export const AskAi = ({
|
|
| 86 |
|
| 87 |
const callAi = async (redesignMarkdown?: string) => {
|
| 88 |
if (!user) return openLoginModal();
|
|
|
|
|
|
|
| 89 |
if (isAiWorking) return;
|
| 90 |
if (!redesignMarkdown && !prompt.trim()) return;
|
| 91 |
|
|
|
|
| 28 |
import { Settings } from "./settings";
|
| 29 |
import { useProModal } from "@/components/contexts/pro-context";
|
| 30 |
import { MODELS } from "@/lib/providers";
|
| 31 |
+
import { MAX_FREE_PROJECTS } from "@/lib/utils";
|
| 32 |
|
| 33 |
export const AskAi = ({
|
| 34 |
project,
|
|
|
|
| 40 |
isNew?: boolean;
|
| 41 |
onScrollToBottom?: () => void;
|
| 42 |
}) => {
|
| 43 |
+
const { user, projects } = useUser();
|
| 44 |
const { currentPageData, isUploading, pages, isLoadingProject } = useEditor();
|
| 45 |
const {
|
| 46 |
isAiWorking,
|
|
|
|
| 87 |
|
| 88 |
const callAi = async (redesignMarkdown?: string) => {
|
| 89 |
if (!user) return openLoginModal();
|
| 90 |
+
if (!user.isPro && projects.length >= MAX_FREE_PROJECTS)
|
| 91 |
+
return openProModal([]);
|
| 92 |
if (isAiWorking) return;
|
| 93 |
if (!redesignMarkdown && !prompt.trim()) return;
|
| 94 |
|
components/my-projects/index.tsx
CHANGED
|
@@ -12,23 +12,22 @@ import { ProTag } from "@/components/pro-modal";
|
|
| 12 |
import { Button } from "@/components/ui/button";
|
| 13 |
import { useProModal } from "@/components/contexts/pro-context";
|
| 14 |
import { api } from "@/lib/api";
|
|
|
|
| 15 |
|
| 16 |
-
export function MyProjects({
|
| 17 |
-
projects
|
| 18 |
-
}: {
|
| 19 |
-
projects: ProjectType[];
|
| 20 |
-
}) {
|
| 21 |
-
const { user } = useUser();
|
| 22 |
const { openProModal } = useProModal();
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
|
|
|
| 26 |
|
| 27 |
const onDelete = async (project: ProjectType) => {
|
| 28 |
const response = await api.delete(`/me/projects/${project.name}`);
|
| 29 |
if (response.data.ok) {
|
| 30 |
toast.success("Project deleted successfully!");
|
| 31 |
-
|
|
|
|
| 32 |
} else {
|
| 33 |
toast.error(response.data.error);
|
| 34 |
}
|
|
|
|
| 12 |
import { Button } from "@/components/ui/button";
|
| 13 |
import { useProModal } from "@/components/contexts/pro-context";
|
| 14 |
import { api } from "@/lib/api";
|
| 15 |
+
import { NotLogged } from "../not-logged/not-logged";
|
| 16 |
|
| 17 |
+
export function MyProjects() {
|
| 18 |
+
const { user, projects, setProjects } = useUser();
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
const { openProModal } = useProModal();
|
| 20 |
+
|
| 21 |
+
if (!user) {
|
| 22 |
+
return <NotLogged />;
|
| 23 |
+
}
|
| 24 |
|
| 25 |
const onDelete = async (project: ProjectType) => {
|
| 26 |
const response = await api.delete(`/me/projects/${project.name}`);
|
| 27 |
if (response.data.ok) {
|
| 28 |
toast.success("Project deleted successfully!");
|
| 29 |
+
const newProjects = projects.filter((p) => p.id !== project.id);
|
| 30 |
+
setProjects(newProjects);
|
| 31 |
} else {
|
| 32 |
toast.error(response.data.error);
|
| 33 |
}
|
components/pro-modal/index.tsx
CHANGED
|
@@ -24,7 +24,10 @@ export const ProModal = ({
|
|
| 24 |
};
|
| 25 |
return (
|
| 26 |
<Dialog open={open} onOpenChange={onClose}>
|
| 27 |
-
<DialogContent
|
|
|
|
|
|
|
|
|
|
| 28 |
<DialogTitle className="hidden" />
|
| 29 |
<main className="flex flex-col items-start text-left relative pt-2">
|
| 30 |
<div className="flex items-center justify-start -space-x-4 mb-5">
|
|
@@ -72,6 +75,7 @@ export const ProModal = ({
|
|
| 72 |
<Button
|
| 73 |
variant="black"
|
| 74 |
size="lg"
|
|
|
|
| 75 |
className="w-full !text-base !h-11 mt-8"
|
| 76 |
onClick={handleProClick}
|
| 77 |
>
|
|
|
|
| 24 |
};
|
| 25 |
return (
|
| 26 |
<Dialog open={open} onOpenChange={onClose}>
|
| 27 |
+
<DialogContent
|
| 28 |
+
showCloseButton={false}
|
| 29 |
+
className="sm:max-w-lg lg:!p-8 !rounded-3xl !bg-white !border-neutral-100"
|
| 30 |
+
>
|
| 31 |
<DialogTitle className="hidden" />
|
| 32 |
<main className="flex flex-col items-start text-left relative pt-2">
|
| 33 |
<div className="flex items-center justify-start -space-x-4 mb-5">
|
|
|
|
| 75 |
<Button
|
| 76 |
variant="black"
|
| 77 |
size="lg"
|
| 78 |
+
tabIndex={-1}
|
| 79 |
className="w-full !text-base !h-11 mt-8"
|
| 80 |
onClick={handleProClick}
|
| 81 |
>
|
hooks/useUser.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
| 4 |
import { useCookie } from "react-use";
|
| 5 |
import { useRouter } from "next/navigation";
|
| 6 |
|
| 7 |
-
import { User } from "@/types";
|
| 8 |
import { api } from "@/lib/api";
|
| 9 |
import { toast } from "sonner";
|
| 10 |
import {
|
|
@@ -19,6 +19,7 @@ import {
|
|
| 19 |
export const useUser = (initialData?: {
|
| 20 |
user: User | null;
|
| 21 |
errCode: number | null;
|
|
|
|
| 22 |
}) => {
|
| 23 |
const client = useQueryClient();
|
| 24 |
const router = useRouter();
|
|
@@ -58,6 +59,18 @@ export const useUser = (initialData?: {
|
|
| 58 |
client.setQueryData(["setLoadingAuth"], value);
|
| 59 |
};
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
const openLoginWindow = async () => {
|
| 62 |
setCurrentRoute(window.location.pathname);
|
| 63 |
|
|
@@ -119,12 +132,15 @@ export const useUser = (initialData?: {
|
|
| 119 |
router.push("/");
|
| 120 |
toast.success("Logout successful");
|
| 121 |
client.invalidateQueries({ queryKey: ["user.me"] });
|
|
|
|
| 122 |
window.location.reload();
|
| 123 |
} catch (error) {
|
| 124 |
console.error("Logout error:", error);
|
| 125 |
clearAuthDataFallback();
|
| 126 |
removeCurrentRoute();
|
| 127 |
client.setQueryData(["user.me"], { user: null, errCode: null });
|
|
|
|
|
|
|
| 128 |
router.push("/");
|
| 129 |
toast.success("Logout successful");
|
| 130 |
window.location.reload();
|
|
@@ -133,6 +149,8 @@ export const useUser = (initialData?: {
|
|
| 133 |
|
| 134 |
return {
|
| 135 |
user,
|
|
|
|
|
|
|
| 136 |
errCode,
|
| 137 |
loading: isLoading || loadingAuth,
|
| 138 |
openLoginWindow,
|
|
|
|
| 4 |
import { useCookie } from "react-use";
|
| 5 |
import { useRouter } from "next/navigation";
|
| 6 |
|
| 7 |
+
import { ProjectType, User } from "@/types";
|
| 8 |
import { api } from "@/lib/api";
|
| 9 |
import { toast } from "sonner";
|
| 10 |
import {
|
|
|
|
| 19 |
export const useUser = (initialData?: {
|
| 20 |
user: User | null;
|
| 21 |
errCode: number | null;
|
| 22 |
+
projects: ProjectType[];
|
| 23 |
}) => {
|
| 24 |
const client = useQueryClient();
|
| 25 |
const router = useRouter();
|
|
|
|
| 59 |
client.setQueryData(["setLoadingAuth"], value);
|
| 60 |
};
|
| 61 |
|
| 62 |
+
const { data: projects } = useQuery({
|
| 63 |
+
queryKey: ["me.projects"],
|
| 64 |
+
queryFn: async () => [],
|
| 65 |
+
refetchOnWindowFocus: false,
|
| 66 |
+
refetchOnReconnect: false,
|
| 67 |
+
refetchOnMount: false,
|
| 68 |
+
initialData: initialData?.projects || [],
|
| 69 |
+
});
|
| 70 |
+
const setProjects = (projects: ProjectType[]) => {
|
| 71 |
+
client.setQueryData(["me.projects"], projects);
|
| 72 |
+
};
|
| 73 |
+
|
| 74 |
const openLoginWindow = async () => {
|
| 75 |
setCurrentRoute(window.location.pathname);
|
| 76 |
|
|
|
|
| 132 |
router.push("/");
|
| 133 |
toast.success("Logout successful");
|
| 134 |
client.invalidateQueries({ queryKey: ["user.me"] });
|
| 135 |
+
client.invalidateQueries({ queryKey: ["me.projects"] });
|
| 136 |
window.location.reload();
|
| 137 |
} catch (error) {
|
| 138 |
console.error("Logout error:", error);
|
| 139 |
clearAuthDataFallback();
|
| 140 |
removeCurrentRoute();
|
| 141 |
client.setQueryData(["user.me"], { user: null, errCode: null });
|
| 142 |
+
client.invalidateQueries({ queryKey: ["user.me"] });
|
| 143 |
+
client.invalidateQueries({ queryKey: ["me.projects"] });
|
| 144 |
router.push("/");
|
| 145 |
toast.success("Logout successful");
|
| 146 |
window.location.reload();
|
|
|
|
| 149 |
|
| 150 |
return {
|
| 151 |
user,
|
| 152 |
+
projects,
|
| 153 |
+
setProjects,
|
| 154 |
errCode,
|
| 155 |
loading: isLoading || loadingAuth,
|
| 156 |
openLoginWindow,
|