Skip to content

Commit

Permalink
feat: authentication + pipe store stripe integration
Browse files Browse the repository at this point in the history
  • Loading branch information
louis030195 committed Dec 4, 2024
1 parent 5e28618 commit 7d776b4
Show file tree
Hide file tree
Showing 16 changed files with 299 additions and 76 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ resolver = "2"


[workspace.package]
version = "0.2.7"
version = "0.2.8"
authors = ["louis030195 <[email protected]>"]
description = ""
repository = "https://github.com/mediar-ai/screenpipe"
Expand Down
18 changes: 6 additions & 12 deletions screenpipe-app-tauri/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Inter } from "next/font/google";
import "./globals.css";
import { Providers } from "./providers";
import { Toaster } from "@/components/ui/toaster";
import { ClerkProvider } from "@clerk/clerk-react";

const inter = Inter({ subsets: ["latin"] });

Expand All @@ -14,17 +13,12 @@ export default function RootLayout({
}) {
return (
<html lang="en" suppressHydrationWarning>
<ClerkProvider
publishableKey="pk_test_ZGVjZW50LXRyb3V0LTEuY2xlcmsuYWNjb3VudHMuZGV2JA"
allowedRedirectOrigins={["http://localhost:3000", "tauri://localhost"]}
>
<Providers>
<body className={inter.className}>
{children}
<Toaster />
</body>
</Providers>
</ClerkProvider>
<Providers>
<body className={inter.className}>
{children}
<Toaster />
</body>
</Providers>
</html>
);
}
Binary file modified screenpipe-app-tauri/bun.lockb
Binary file not shown.
47 changes: 26 additions & 21 deletions screenpipe-app-tauri/components/auth.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,43 @@
"use client";
import { SignInButton, SignOutButton, useUser } from "@clerk/clerk-react";

import { invoke } from "@tauri-apps/api/core";
import { Button } from "./ui/button";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useUser } from "@/lib/hooks/use-user";
import { Avatar, AvatarFallback } from "./ui/avatar";
import posthog from "posthog-js";
import { useSettings } from "@/lib/hooks/use-settings";

export function AuthButton() {
const { isSignedIn, user } = useUser();
const { settings } = useSettings();

useEffect(() => {
posthog.identify(settings.userId, {
email: user?.primaryEmailAddress?.emailAddress,
clerkId: user?.id,
posthog.identify(user?.user_id, {
email: user?.email,
});
posthog.setPersonProperties({
email: user?.primaryEmailAddress?.emailAddress,
clerkId: user?.id,
email: user?.email,
});
}, [isSignedIn, user, settings.userId]);
}, [isSignedIn, user]);

const handleSignIn = async () => {
// This would typically open your auth window
await invoke("open_auth_window");
};

return (
<div >
<div>
{isSignedIn ? (
<SignOutButton>
<Button variant="outline">
sign out
</Button>
</SignOutButton>
<Button variant="ghost" onClick={handleSignIn}>
<Avatar>
<AvatarFallback>
{user?.email.charAt(0).toUpperCase()}
</AvatarFallback>
</Avatar>
</Button>
) : (
<SignInButton mode="redirect">
<Button variant="outline">
sign in
</Button>
</SignInButton>
<Button variant="outline" onClick={handleSignIn}>
sign in
</Button>
)}
</div>
);
Expand Down
22 changes: 14 additions & 8 deletions screenpipe-app-tauri/components/pipe-store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ import {
} from "@/components/ui/collapsible";
import { LogFileButton } from "./log-file-button";
import { useSettings } from "@/lib/hooks/use-settings";
import { useUser } from "@clerk/clerk-react";
import { StripeSubscriptionButton } from "./stripe-subscription-button";
import { useUser } from "@/lib/hooks/use-user";

export interface Pipe {
enabled: boolean;
Expand Down Expand Up @@ -105,7 +105,14 @@ const PipeDialog: React.FC = () => {
const [pipes, setPipes] = useState<Pipe[]>([]);
const { health } = useHealthCheck();
const { getDataDir } = useSettings();
const { isSignedIn } = useUser();
const { user, checkLoomSubscription } = useUser();
const [hasLoomSubscription, setHasLoomSubscription] = useState(false);

useEffect(() => {
if (user) {
checkLoomSubscription().then(setHasLoomSubscription);
}
}, []);

useEffect(() => {
fetchInstalledPipes();
Expand Down Expand Up @@ -217,12 +224,11 @@ const PipeDialog: React.FC = () => {

const handleToggleEnabled = async (pipe: Pipe) => {
if (pipe.id === "pipe-for-loom" && !pipe.enabled) {
const isSubscribed =
localStorage.getItem("loom_pipe_subscribed") === "true";
if (!isSubscribed) {
const hasLoomSubscription = await checkLoomSubscription();
if (!hasLoomSubscription) {
toast({
title: "Subscription required",
description: "Please subscribe to use the Loom pipe",
title: "subscription required",
description: "please subscribe to use the loom pipe",
});
return;
}
Expand Down Expand Up @@ -438,7 +444,7 @@ const PipeDialog: React.FC = () => {
<div className="flex space-x-2 mb-4">
{selectedPipe.id === "pipe-for-loom" &&
!selectedPipe.enabled &&
!localStorage.getItem("loom_pipe_subscribed") ? (
!hasLoomSubscription ? (
<StripeSubscriptionButton
onSubscriptionComplete={() => handleToggleEnabled(selectedPipe)}
/>
Expand Down
20 changes: 12 additions & 8 deletions screenpipe-app-tauri/components/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
} from "@/components/ui/select";
import { useInterval } from "@/lib/hooks/use-interval";
import { useHealthCheck } from "@/lib/hooks/use-health-check";
import { AuthButton } from "./auth";

export function Settings({ className }: { className?: string }) {
const { settings, updateSettings, resetSetting } = useSettings();
Expand Down Expand Up @@ -484,7 +485,6 @@ export function Settings({ className }: { className?: string }) {
// Use the useInterval hook to periodically check the status
useInterval(checkEmbeddedAIStatus, 10000); // Check every 10 seconds


const handleShortcutToggle = (checked: boolean) => {
console.log("handleShortcutToggle", checked);
let newDisabledShortcuts = [...localSettings.disabledShortcuts];
Expand Down Expand Up @@ -515,22 +515,21 @@ export function Settings({ className }: { className?: string }) {
debouncedFetchHealth();
};

window.addEventListener('settings-updated', handleSettingsUpdate);
window.addEventListener("settings-updated", handleSettingsUpdate);

return () => {
window.removeEventListener('settings-updated', handleSettingsUpdate);
window.removeEventListener("settings-updated", handleSettingsUpdate);
};
}, [debouncedFetchHealth]);


return (
<Dialog
onOpenChange={(open) => {
if (!open) {
// Use a more reliable state update mechanism
const event = new CustomEvent('settings-updated');
const event = new CustomEvent("settings-updated");
window.dispatchEvent(event);

// Add a small delay before refetching health
setTimeout(() => {
debouncedFetchHealth();
Expand All @@ -546,7 +545,12 @@ export function Settings({ className }: { className?: string }) {
</DialogTrigger>
<DialogContent className="max-w-[80vw] w-full max-h-[80vh] h-full overflow-y-auto">
<DialogHeader>
<DialogTitle>settings</DialogTitle>
<DialogTitle>
<div className="flex items-center gap-4">
settings
<AuthButton />
</div>
</DialogTitle>
<DialogDescription>
choose your AI provider, enter necessary credentials, and more.
</DialogDescription>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"use client";

import { Button } from "@/components/ui/button";
import { useUser } from "@clerk/clerk-react";
import { open } from "@tauri-apps/plugin-shell";
import { toast } from "./ui/use-toast";
import posthog from "posthog-js";
import { useEffect, useState } from "react";
import { useUser } from "@/lib/hooks/use-user";

interface StripeSubscriptionButtonProps {
onSubscriptionComplete?: () => void;
Expand All @@ -14,7 +14,7 @@ interface StripeSubscriptionButtonProps {
export function StripeSubscriptionButton({
onSubscriptionComplete,
}: StripeSubscriptionButtonProps) {
const { user, isSignedIn } = useUser();
const { isSignedIn, user } = useUser();
const [isSubscribed, setIsSubscribed] = useState(false);

useEffect(() => {
Expand All @@ -24,7 +24,7 @@ export function StripeSubscriptionButton({

const handleSubscribe = async () => {
posthog.capture("subscribe_button_clicked", {
email: user?.primaryEmailAddress?.emailAddress,
email: user?.email,
});
if (!isSignedIn) {
toast({
Expand All @@ -37,7 +37,7 @@ export function StripeSubscriptionButton({

try {
// Direct Stripe Checkout URL with price_id
const checkoutUrl = `https://buy.stripe.com/28o00JcCq2JsgAE9AX?prefilled_email=${user.primaryEmailAddress?.emailAddress}&client_reference_id=${user.id}`;
const checkoutUrl = `https://buy.stripe.com/28o00JcCq2JsgAE9AX?prefilled_email=${user?.email}&client_reference_id=${user?.user_id}`;

// Open Stripe checkout in default browser
await open(checkoutUrl);
Expand Down
2 changes: 1 addition & 1 deletion screenpipe-app-tauri/lib/hooks/use-health-check.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export function useHealthCheck() {
console.error("Health check error:", error);
if (!isServerDown) {
setIsServerDown(true);
await invoke("set_tray_unhealth_icon");
// await invoke("set_tray_unhealth_icon");
const errorHealth: HealthCheckResponse = {
last_frame_timestamp: null,
last_audio_timestamp: null,
Expand Down
1 change: 1 addition & 0 deletions screenpipe-app-tauri/lib/hooks/use-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export function useSettings() {
? "windows-native"
: "tesseract";


// no need to call load() as it's done automatically
const savedKey = (await store!.get<string>("openaiApiKey")) || "";
const savedDeepgramKey =
Expand Down
92 changes: 92 additions & 0 deletions screenpipe-app-tauri/lib/hooks/use-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { useState, useEffect, useCallback } from "react";
import { createStore } from "@tauri-apps/plugin-store";
import { localDataDir, join } from "@tauri-apps/api/path";

interface UserData {
token: string;
email: string;
user_id: string;
}

interface UserState {
isSignedIn: boolean;
isLoading: boolean;
error: string | null;
user: UserData | null;
checkLoomSubscription: () => Promise<boolean>;
}

let store: Awaited<ReturnType<typeof createStore>> | null = null;

async function initStore() {
const dataDir = await localDataDir();
const storePath = await join(dataDir, "screenpipe", "store.bin");
store = await createStore(storePath);
const entries = await store.entries();
console.log("store", entries);
}

async function checkSubscription(email: string): Promise<boolean> {
try {
const isDev = window.location.href.includes("localhost");
const baseUrl = isDev ? "http://localhost:3001" : "https://screenpi.pe";

const response = await fetch(`${baseUrl}/api/stripe-loom`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
...(isDev ? {} : { credentials: "include" }),
body: JSON.stringify({ email }),
});

if (!response.ok) {
console.error("subscription check failed with status:", response.status);
return false;
}

const data = await response.json();
console.log("subscription data:", data);
return data.hasActiveSubscription;
} catch (error) {
console.error("failed to check subscription:", error);
return false;
}
}

export function useUser() {
const [user, setUser] = useState<UserData | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
const loadUser = async () => {
if (!store) await initStore();

try {
const savedUser = await store!.get<UserData>("auth_data");
setUser(savedUser || null);
} catch (err) {
console.error("failed to load user:", err);
setError(err instanceof Error ? err.message : "failed to load user");
} finally {
setIsLoading(false);
}
};

loadUser();
}, []);

const checkLoomSubscription = useCallback(async () => {
if (!user?.email) return false;
return checkSubscription(user.email);
}, [user?.email]);

return {
user,
isSignedIn: !!user,
isLoading,
error,
checkLoomSubscription,
};
}
1 change: 0 additions & 1 deletion screenpipe-app-tauri/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
},
"dependencies": {
"@ai-sdk/openai": "^0.0.39",
"@clerk/clerk-react": "^5.17.1",
"@dnd-kit/core": "^6.1.0",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/context-zone": "^1.22.0",
Expand Down
3 changes: 2 additions & 1 deletion screenpipe-app-tauri/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 7d776b4

Please sign in to comment.