Skip to content

Commit

Permalink
revamped logic and UI for api key settings
Browse files Browse the repository at this point in the history
  • Loading branch information
eg9y committed May 29, 2023
1 parent 306e590 commit 356db5c
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 86 deletions.
160 changes: 89 additions & 71 deletions packages/web/src/components/ApiKeySettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@ export default function ApiKeySettings({
selectorSecret,
shallow,
);
const [newApiKey, setNewApiKey] = useState('');
const [newApiKey, setNewApiKey] = useState(openAiKey);
const [isLoading, setIsLoading] = useState(false);
const [helperMessage, setHelperMessage] = useState('');
const [isEditWithMyKey, setIsEditWithMyKey] = useState<boolean | null>(null);

useEffect(() => {
if (session && isEditWithMyKey === null) {
setIsEditWithMyKey(!!session.user.user_metadata.is_edit_with_my_key);
} else if (isEditWithMyKey === null) {
// case where user is not logged in
setIsEditWithMyKey(true);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [session]);
Expand All @@ -37,16 +40,39 @@ export default function ApiKeySettings({
// update user's openai key
if (newApiKey === '') {
console.log('No key entered');
setIsLoading(false);
setOpen(false);
} else {
await supabase.functions.invoke('insert-api-key', {
body: {
api_key: newApiKey,
if (session) {
await supabase.functions.invoke('insert-api-key', {
body: {
api_key: newApiKey,
},
});
}
setOpenAiKey(newApiKey);
}

// update user edit_with_api_key metadata. This will inform the supabase proxy to use the user's key instead of the app's key
if (session) {
const { error } = await supabase.auth.updateUser({
data: {
edit_with_api_key: isEditWithMyKey,
},
});
setOpenAiKey(newApiKey);

if (error) {
setHelperMessage(error.message);
return;
}
const newSession = { ...session };
if (newSession.user) {
newSession.user.user_metadata.edit_with_api_key = isEditWithMyKey;
setSession(newSession);
console.log('coq', isEditWithMyKey);
}
}

// edit here
setIsLoading(false);
setOpen(false);
}
Expand Down Expand Up @@ -85,52 +111,34 @@ export default function ApiKeySettings({
>
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-slate-50 px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6">
<div>
<p className="block text-sm font-medium leading-6 text-gray-900">
Set how you want to call OpenAI API
<p className="block text-center text-xl font-semibold leading-6 text-gray-900">
{`🔑 Set how you use OpenAI's feature`}
</p>
<p className="block pt-2 text-sm leading-6 text-gray-900">
{`
As you test out your chatbot, you can choose to use your
own OpenAI API key or have us handle it for you. Using the app's key will incur
a cost to your message credits.
`}
</p>
<div className="mt-2">
<div className="mt-4">
<Switch.Group as="div" className="flex items-center">
<Switch
checked={!!isEditWithMyKey}
checked={isEditWithMyKey === true}
onChange={async () => {
setIsEditWithMyKey(!isEditWithMyKey);
// update user edit_with_api_key metadata. This will inform the supabase proxy to use the user's key instead of the app's key
const { data, error } =
await supabase.auth.updateUser({
data: {
edit_with_api_key: !isEditWithMyKey,
},
});

if (error) {
setHelperMessage(error.message);
return;
}
if (!data) {
setHelperMessage(
'Your settings have not been updated. ERROR',
);
return;
}

if (session) {
const newSession = { ...session };
if (newSession.user) {
newSession.user.user_metadata.edit_with_api_key =
!isEditWithMyKey;
setSession(newSession);
}
} else {
if (!session) {
setHelperMessage(
'Your settings have not been updated. ERROR',
'You must be logged in to change this setting',
);
return;
}
setIsEditWithMyKey(!isEditWithMyKey);
}}
className={conditionalClassNames(
isEditWithMyKey
session && isEditWithMyKey
? 'bg-green-600'
: 'bg-gray-200',
!session && 'bg-green-200',
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-green-600 focus:ring-offset-2',
)}
>
Expand Down Expand Up @@ -168,7 +176,8 @@ export default function ApiKeySettings({
type="openAiKey"
name="openAiKey"
id="openAiKey"
value={openAiKey}
value={newApiKey}
autoComplete="off"
onChange={(e) => {
const input = e.target.value;
setNewApiKey(input);
Expand All @@ -177,9 +186,6 @@ export default function ApiKeySettings({
className="block w-full rounded-md border-0 px-2 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6"
aria-describedby="username-description"
/>
{isLoading && (
<Loading className="-ml-1 mr-3 h-5 w-5 animate-spin text-black" />
)}
</form>
)}
</div>
Expand All @@ -189,33 +195,45 @@ export default function ApiKeySettings({
>
{helperMessage}
</p>
<div className="flex gap-2">
<button
type="button"
onClick={() => {
if (
session &&
session.user &&
session.user.user_metadata
) {
delete session.user.user_metadata
.is_edit_with_my_key;
}
setOpen(false);
}}
className="rounded-md bg-red-100 px-2.5 py-1.5 text-sm font-semibold text-red-600 shadow-sm hover:bg-red-200"
>
Cancel
</button>
<button
type="button"
onClick={async () => {
await handleChange();
}}
className="rounded-md bg-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600"
>
OK
</button>
<div className="flex items-center justify-between pt-4">
<div className="flex gap-2">
<button
type="button"
onClick={() => {
if (
session &&
session.user &&
session.user.user_metadata
) {
delete session.user.user_metadata
.is_edit_with_my_key;
}
setOpen(false);
setHelperMessage('');
}}
className="rounded-md bg-red-100 px-2.5 py-1.5 text-sm font-semibold text-red-600 shadow-sm hover:bg-red-200"
>
Cancel
</button>
<button
type="button"
disabled={isLoading}
onClick={async () => {
await handleChange();
}}
className={conditionalClassNames(
isLoading
? 'cursor-not-allowed bg-green-300'
: 'bg-green-600 hover:bg-green-500 ',
'rounded-md px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600',
)}
>
OK
</button>
</div>
{isLoading && (
<Loading className="-ml-1 mr-3 h-7 w-7 animate-spin text-black" />
)}
</div>
</div>
</Dialog.Panel>
Expand Down
17 changes: 10 additions & 7 deletions packages/web/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { Dialog } from '@headlessui/react';
import {
ArrowTopRightOnSquareIcon,
Bars3Icon,
DocumentTextIcon,
XMarkIcon,
} from '@heroicons/react/20/solid';
import { Bars3Icon, DocumentTextIcon, XMarkIcon } from '@heroicons/react/20/solid';
import { useState } from 'react';
import { useLocation } from 'wouter';
import { shallow } from 'zustand/shallow';
Expand Down Expand Up @@ -41,7 +36,7 @@ const NavBar = () => {
setNotificationMessage,
setUsername,
} = useStore(selector, shallow);
const { session, setSession } = useStoreSecret(selectorSecret, shallow);
const { session, setSession, userCredits } = useStoreSecret(selectorSecret, shallow);

const [mobileMenuOpen, setMobileMenuOpen] = useState(false);

Expand Down Expand Up @@ -139,6 +134,14 @@ const NavBar = () => {
</div>

<div className="flex flex-1 items-center justify-end gap-4">
{session && userCredits.credits > 0 && (
<a
className="flex cursor-pointer items-center gap-1 hover:font-semibold"
rel="noreferrer"
>
credits: {userCredits.credits}
</a>
)}
<a
className="flex cursor-pointer items-center gap-1 hover:font-semibold"
onClick={() => {
Expand Down
9 changes: 8 additions & 1 deletion packages/web/src/pages/overview/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import UsernamePrompt from '../app/UsernamePrompt';
export default function Overview() {
const { workflows, setNotificationMessage, setUsername, setWorkflows, setCurrentWorkflow } =
useStore(selector, shallow);
const { session, setSession, setOpenAiKey } = useStoreSecret(selectorSecret, shallow);
const { session, setSession, setOpenAiKey, setUserCredits } = useStoreSecret(
selectorSecret,
shallow,
);

const supabase = useSupabase();

Expand Down Expand Up @@ -50,6 +53,10 @@ export default function Overview() {
} else {
setUsername(profile.first_name);
}
setUserCredits({
plan: (profile.plan as 'free' | 'essential' | 'premium') || 'free',
credits: profile.remaining_message_credits || 0,
});
}
};

Expand Down
14 changes: 7 additions & 7 deletions packages/web/src/utils/runFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,18 +126,18 @@ export async function runFlow(
const remainingCredits =
userCredits.credits - creditsUsed > 0 ? userCredits.credits - creditsUsed : 0;

const { data: updatedUser, error } = await supabase
const { error } = await supabase
.from('profiles')
.update({ remaining_message_credits: remainingCredits });
.update({ remaining_message_credits: remainingCredits })
.eq('id', session.user.id);

if (error) {
console.log(error);
} else if (updatedUser) {
setUserCredits({
...userCredits,
credits: remainingCredits,
});
}
setUserCredits({
...userCredits,
credits: remainingCredits,
});
}

node.data.isLoading = false;
Expand Down

0 comments on commit 356db5c

Please sign in to comment.