mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-26 09:16:22 +02:00
feat: no login experience and prem tokens
Some checks are pending
Build and Push Docker Images / tag_release (push) Waiting to run
Build and Push Docker Images / build (./surfsense_backend, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_web, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_web, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (backend, surfsense-backend) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (web, surfsense-web) (push) Blocked by required conditions
Some checks are pending
Build and Push Docker Images / tag_release (push) Waiting to run
Build and Push Docker Images / build (./surfsense_backend, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_web, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_web, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (backend, surfsense-backend) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (web, surfsense-web) (push) Blocked by required conditions
This commit is contained in:
parent
87452bb315
commit
ff4e0f9b62
68 changed files with 5914 additions and 121 deletions
74
surfsense_web/contexts/anonymous-mode.tsx
Normal file
74
surfsense_web/contexts/anonymous-mode.tsx
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
"use client";
|
||||
|
||||
import { createContext, type ReactNode, useContext, useEffect, useMemo, useState } from "react";
|
||||
import { anonymousChatApiService } from "@/lib/apis/anonymous-chat-api.service";
|
||||
|
||||
export interface AnonymousModeContextValue {
|
||||
isAnonymous: true;
|
||||
modelSlug: string;
|
||||
setModelSlug: (slug: string) => void;
|
||||
uploadedDoc: { filename: string; sizeBytes: number } | null;
|
||||
setUploadedDoc: (doc: { filename: string; sizeBytes: number } | null) => void;
|
||||
resetKey: number;
|
||||
resetChat: () => void;
|
||||
}
|
||||
|
||||
interface AuthenticatedContextValue {
|
||||
isAnonymous: false;
|
||||
}
|
||||
|
||||
type ContextValue = AnonymousModeContextValue | AuthenticatedContextValue;
|
||||
|
||||
const DEFAULT_VALUE: AuthenticatedContextValue = { isAnonymous: false };
|
||||
|
||||
const AnonymousModeContext = createContext<ContextValue>(DEFAULT_VALUE);
|
||||
|
||||
export function AnonymousModeProvider({
|
||||
initialModelSlug,
|
||||
children,
|
||||
}: {
|
||||
initialModelSlug: string;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const [modelSlug, setModelSlug] = useState(initialModelSlug);
|
||||
const [uploadedDoc, setUploadedDoc] = useState<{ filename: string; sizeBytes: number } | null>(
|
||||
null
|
||||
);
|
||||
const [resetKey, setResetKey] = useState(0);
|
||||
|
||||
const resetChat = () => setResetKey((k) => k + 1);
|
||||
|
||||
useEffect(() => {
|
||||
anonymousChatApiService
|
||||
.getDocument()
|
||||
.then((doc) => {
|
||||
if (doc) {
|
||||
setUploadedDoc({ filename: doc.filename, sizeBytes: doc.size_bytes });
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
const value = useMemo<AnonymousModeContextValue>(
|
||||
() => ({
|
||||
isAnonymous: true,
|
||||
modelSlug,
|
||||
setModelSlug,
|
||||
uploadedDoc,
|
||||
setUploadedDoc,
|
||||
resetKey,
|
||||
resetChat,
|
||||
}),
|
||||
[modelSlug, uploadedDoc, resetKey]
|
||||
);
|
||||
|
||||
return <AnonymousModeContext.Provider value={value}>{children}</AnonymousModeContext.Provider>;
|
||||
}
|
||||
|
||||
export function useAnonymousMode(): ContextValue {
|
||||
return useContext(AnonymousModeContext);
|
||||
}
|
||||
|
||||
export function useIsAnonymous(): boolean {
|
||||
return useContext(AnonymousModeContext).isAnonymous;
|
||||
}
|
||||
84
surfsense_web/contexts/login-gate.tsx
Normal file
84
surfsense_web/contexts/login-gate.tsx
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { createContext, type ReactNode, useCallback, useContext, useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { useIsAnonymous } from "./anonymous-mode";
|
||||
|
||||
interface LoginGateContextValue {
|
||||
gate: (feature: string) => void;
|
||||
}
|
||||
|
||||
const LoginGateContext = createContext<LoginGateContextValue>({
|
||||
gate: () => {},
|
||||
});
|
||||
|
||||
export function LoginGateProvider({ children }: { children: ReactNode }) {
|
||||
const isAnonymous = useIsAnonymous();
|
||||
const [feature, setFeature] = useState<string | null>(null);
|
||||
|
||||
const gate = useCallback(
|
||||
(feat: string) => {
|
||||
if (isAnonymous) {
|
||||
setFeature(feat);
|
||||
}
|
||||
},
|
||||
[isAnonymous]
|
||||
);
|
||||
|
||||
const close = () => setFeature(null);
|
||||
|
||||
return (
|
||||
<LoginGateContext.Provider value={{ gate }}>
|
||||
{children}
|
||||
<Dialog open={feature !== null} onOpenChange={(open) => !open && close()}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create a free account to {feature}</DialogTitle>
|
||||
<DialogDescription>
|
||||
Get 5 million tokens, save chat history, upload documents, use all AI tools, and
|
||||
connect 30+ integrations.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter className="flex flex-col gap-2 sm:flex-row">
|
||||
<Button asChild>
|
||||
<Link href="/register">Create Free Account</Link>
|
||||
</Button>
|
||||
<Button variant="outline" asChild>
|
||||
<Link href="/login">Log In</Link>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</LoginGateContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useLoginGate(): LoginGateContextValue {
|
||||
return useContext(LoginGateContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a click handler that triggers the login gate when anonymous,
|
||||
* or calls the original handler when authenticated.
|
||||
*/
|
||||
export function useGatedHandler(handler: (() => void) | undefined, feature: string): () => void {
|
||||
const { gate } = useLoginGate();
|
||||
const isAnonymous = useIsAnonymous();
|
||||
|
||||
return useCallback(() => {
|
||||
if (isAnonymous) {
|
||||
gate(feature);
|
||||
} else {
|
||||
handler?.();
|
||||
}
|
||||
}, [isAnonymous, gate, feature, handler]);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue