Revert "Revert "feat: remove private prompts on related brain delete (#842)" (#870)" (#876)

This reverts commit 6184dfadfe.
This commit is contained in:
Stan Girard 2023-08-07 10:15:38 +02:00 committed by GitHub
parent 8eb564f5bc
commit 6d74aea729
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 189 additions and 58 deletions

View File

@ -5,9 +5,11 @@ from auth.auth_bearer import AuthBearer, get_current_user
from fastapi import APIRouter, Depends, HTTPException
from models.brains import Brain
from models.brains_subscription_invitations import BrainSubscription
from models.prompt import PromptStatusEnum
from models.users import User
from pydantic import BaseModel
from repository.brain.create_brain_user import create_brain_user
from repository.brain.get_brain_by_id import get_brain_by_id
from repository.brain.get_brain_details import get_brain_details
from repository.brain.get_brain_for_user import get_brain_for_user
from repository.brain.update_user_rights import update_brain_user_rights
@ -17,6 +19,8 @@ from repository.brain_subscription.resend_invitation_email import (
from repository.brain_subscription.subscription_invitation_service import (
SubscriptionInvitationService,
)
from repository.prompt.delete_prompt_py_id import delete_prompt_by_id
from repository.prompt.get_prompt_by_id import get_prompt_by_id
from repository.user.get_user_email_by_user_id import get_user_email_by_user_id
from repository.user.get_user_id_by_user_email import get_user_id_by_user_email
@ -143,6 +147,14 @@ async def remove_user_subscription(
]
if len(brain_other_owners) == 0:
# Delete its prompt if it's private
deleting_brain = get_brain_by_id(brain_id)
if deleting_brain and deleting_brain.prompt_id:
deleting_brain_prompt = get_prompt_by_id(deleting_brain.prompt_id)
if deleting_brain_prompt is not None and (
deleting_brain_prompt.status == PromptStatusEnum.private
):
delete_prompt_by_id(deleting_brain.prompt_id)
brain.delete_brain(current_user.id)
else:
brain.delete_user_from_brain(current_user.id)

View File

@ -10,7 +10,7 @@ import { TextArea } from "@/lib/components/ui/TextArea";
import { models, paidModels } from "@/lib/context/BrainConfigProvider/types";
import { defineMaxTokens } from "@/lib/helpers/defineMexTokens";
import { PublicPrompts } from "./components/PublicPrompts";
import { PublicPrompts } from "./components/PublicPrompts/PublicPrompts";
import { useSettingsTab } from "./hooks/useSettingsTab";
type SettingsTabProps = {

View File

@ -1,57 +0,0 @@
import * as Accordion from "@radix-ui/react-accordion";
import { ChangeEvent, useEffect, useState } from "react";
import { usePromptApi } from "@/lib/api/prompt/usePromptApi";
import { Prompt } from "@/lib/types/Prompt";
type PublicPromptsProps = {
onSelect: ({ title, content }: { title: string; content: string }) => void;
};
export const PublicPrompts = ({
onSelect,
}: PublicPromptsProps): JSX.Element => {
const [publicPrompts, setPublicPrompts] = useState<Prompt[]>([]);
const { getPublicPrompts } = usePromptApi();
const fetchPublicPrompts = async () => {
setPublicPrompts(await getPublicPrompts());
};
const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {
const selectedPrompt = publicPrompts.find(
(prompt) => prompt.id === event.target.value
);
if (selectedPrompt) {
onSelect({
title: selectedPrompt.title,
content: selectedPrompt.content,
});
}
};
useEffect(() => {
void fetchPublicPrompts();
}, []);
return (
<Accordion.Root className="AccordionRoot" type="single" collapsible>
<Accordion.Item className="AccordionItem" value="item-1">
<Accordion.Trigger>Pick in public prompts</Accordion.Trigger>
<Accordion.Content>
<select
onChange={handleChange}
className="px-5 w-full py-2 dark:bg-gray-700 bg-gray-200 rounded-md"
>
{publicPrompts.map((prompt) => (
<option value={prompt.id} key={prompt.id}>
{prompt.title}
</option>
))}
</select>
</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
);
};

View File

@ -0,0 +1,22 @@
import { PublicPromptsList } from "./components/PublicPromptsList/PublicPromptsList";
import { usePublicPrompts } from "./hooks/usePublicPrompts";
type PublicPromptsProps = {
onSelect: ({ title, content }: { title: string; content: string }) => void;
};
export const PublicPrompts = ({
onSelect,
}: PublicPromptsProps): JSX.Element => {
const { handleChange, publicPrompts } = usePublicPrompts({
onSelect,
});
return (
<PublicPromptsList
options={publicPrompts}
onChange={handleChange}
onSelect={onSelect}
/>
);
};

View File

@ -0,0 +1,53 @@
import { ChangeEvent } from "react";
import { Prompt } from "@/lib/types/Prompt";
import { usePublicPromptsList } from "./hooks/usePublicPromptsList";
type PublicPromptsListProps = {
options: Prompt[];
onChange: (event: ChangeEvent<HTMLSelectElement>) => void;
onSelect: ({ title, content }: { title: string; content: string }) => void;
};
export const PublicPromptsList = ({
options,
onChange,
onSelect,
}: PublicPromptsListProps): JSX.Element => {
const {
handleOptionClick,
isOpen,
selectRef,
selectedOption,
toggleDropdown,
} = usePublicPromptsList({
onChange,
onSelect,
});
return (
<div ref={selectRef} className="relative min-w-[200px] inline-block">
<button
onClick={toggleDropdown}
type="button"
className="px-4 py-2 w-full text-gray-700 bg-white border rounded-md focus:outline-none focus:border-blue-500"
>
{selectedOption ? selectedOption.title : "Select a Quivr Personality"}
</button>
{isOpen && (
<div className="absolute top-10 w-full bg-white border rounded-md shadow-lg">
{options.map((option) => (
<div
key={option.id}
className="px-4 py-2 cursor-pointer hover:bg-gray-100"
onClick={() => handleOptionClick(option)}
>
{option.title}
</div>
))}
</div>
)}
</div>
);
};

View File

@ -0,0 +1,59 @@
import { ChangeEvent, useEffect, useRef, useState } from "react";
import { Prompt } from "@/lib/types/Prompt";
type UsePublicPromptsListProps = {
onChange: (event: ChangeEvent<HTMLSelectElement>) => void;
onSelect: ({ title, content }: { title: string; content: string }) => void;
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const usePublicPromptsList = ({
onChange,
onSelect,
}: UsePublicPromptsListProps) => {
const [isOpen, setIsOpen] = useState(false);
const [selectedOption, setSelectedOption] = useState<Prompt | null>(null);
const selectRef = useRef<HTMLDivElement>(null);
const toggleDropdown = () => {
setIsOpen((prevIsOpen) => !prevIsOpen);
};
const handleOptionClick = (option: Prompt) => {
setSelectedOption(option);
setIsOpen(false);
onChange({
target: { value: option.id },
} as ChangeEvent<HTMLSelectElement>);
onSelect({
title: option.title,
content: option.content,
});
};
const handleClickOutside = (event: MouseEvent) => {
if (
selectRef.current &&
!selectRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
};
useEffect(() => {
document.addEventListener("click", handleClickOutside, true);
return () => {
document.removeEventListener("click", handleClickOutside, true);
};
}, []);
return {
isOpen,
selectedOption,
selectRef,
toggleDropdown,
handleOptionClick,
};
};

View File

@ -0,0 +1,38 @@
import { ChangeEvent, useEffect, useState } from "react";
import { usePromptApi } from "@/lib/api/prompt/usePromptApi";
import { Prompt } from "@/lib/types/Prompt";
type UsePublicPromptsProps = {
onSelect: ({ title, content }: { title: string; content: string }) => void;
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const usePublicPrompts = ({ onSelect }: UsePublicPromptsProps) => {
const [publicPrompts, setPublicPrompts] = useState<Prompt[]>([]);
const { getPublicPrompts } = usePromptApi();
useEffect(() => {
const fetchPublicPrompts = async () => {
setPublicPrompts(await getPublicPrompts());
};
void fetchPublicPrompts();
}, []);
const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {
const selectedPrompt = publicPrompts.find(
(prompt) => prompt.id === event.target.value
);
if (selectedPrompt) {
onSelect({
title: selectedPrompt.title,
content: selectedPrompt.content,
});
}
};
return {
publicPrompts,
handleChange,
};
};