Add ShareBtn component and update SettingsOptionLayout
This commit is contained in:
		
							parent
							
								
									3eabe10bde
								
							
						
					
					
						commit
						7ce79bb134
					
				
							
								
								
									
										2
									
								
								page-share.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								page-share.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | # Page Share  | ||||||
|  | 
 | ||||||
							
								
								
									
										193
									
								
								src/components/Common/ShareBtn.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								src/components/Common/ShareBtn.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,193 @@ | |||||||
|  | import { Form, Image, Input, Modal, Tooltip, message } from "antd" | ||||||
|  | import { Share } from "lucide-react" | ||||||
|  | import { useState } from "react" | ||||||
|  | import type { Message } from "~store/option" | ||||||
|  | import Markdown from "./Markdown" | ||||||
|  | import React from "react" | ||||||
|  | import { useMutation } from "@tanstack/react-query" | ||||||
|  | import { getPageShareUrl } from "~services/ollama" | ||||||
|  | import { cleanUrl } from "~libs/clean-url" | ||||||
|  | import { getUserId, saveWebshare } from "~libs/db" | ||||||
|  | 
 | ||||||
|  | type Props = { | ||||||
|  |   messages: Message[] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const reformatMessages = (messages: Message[], username: string) => { | ||||||
|  |   return messages.map((message, idx) => { | ||||||
|  |     return { | ||||||
|  |       id: idx, | ||||||
|  |       name: message.isBot ? message.name : username, | ||||||
|  |       isBot: message.isBot, | ||||||
|  |       message: message.message, | ||||||
|  |       images: message.images | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const PlaygroundMessage = ( | ||||||
|  |   props: Message & { | ||||||
|  |     username: string | ||||||
|  |   } | ||||||
|  | ) => { | ||||||
|  |   return ( | ||||||
|  |     <div className="group w-full text-gray-800 dark:text-gray-100"> | ||||||
|  |       <div className="text-base gap-4 md:gap-6 md:max-w-2xl lg:max-w-xl xl:max-w-3xl flex lg:px-0 m-auto w-full"> | ||||||
|  |         <div className="flex flex-row gap-4 md:gap-6 md:max-w-2xl lg:max-w-xl xl:max-w-3xl m-auto w-full"> | ||||||
|  |           <div className="w-8 flex flex-col relative items-end"> | ||||||
|  |             <div className="relative h-7 w-7 p-1 rounded-sm text-white flex items-center justify-center  text-opacity-100r"> | ||||||
|  |               {props.isBot ? ( | ||||||
|  |                 <div className="absolute h-8 w-8 rounded-full bg-gradient-to-r from-green-300 to-purple-400"></div> | ||||||
|  |               ) : ( | ||||||
|  |                 <div className="absolute h-8 w-8 rounded-full from-blue-400 to-blue-600 bg-gradient-to-r"></div> | ||||||
|  |               )} | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |           <div className="flex w-[calc(100%-50px)] flex-col gap-3 lg:w-[calc(100%-115px)]"> | ||||||
|  |             <span className="text-xs font-bold text-gray-800 dark:text-white"> | ||||||
|  |               {props.isBot ? props.name : props.username} | ||||||
|  |             </span> | ||||||
|  | 
 | ||||||
|  |             <div className="flex flex-grow flex-col"> | ||||||
|  |               <Markdown message={props.message} /> | ||||||
|  |             </div> | ||||||
|  |             {/* source if aviable */} | ||||||
|  |             {props.images && props.images.length > 0 && ( | ||||||
|  |               <div className="flex md:max-w-2xl lg:max-w-xl xl:max-w-3xl mt-4 m-auto w-full"> | ||||||
|  |                 {props.images | ||||||
|  |                   .filter((image) => image.length > 0) | ||||||
|  |                   .map((image, index) => ( | ||||||
|  |                     <Image | ||||||
|  |                       key={index} | ||||||
|  |                       src={image} | ||||||
|  |                       alt="Uploaded Image" | ||||||
|  |                       width={180} | ||||||
|  |                       className="rounded-md relative" | ||||||
|  |                     /> | ||||||
|  |                   ))} | ||||||
|  |               </div> | ||||||
|  |             )} | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const ShareBtn: React.FC<Props> = ({ messages }) => { | ||||||
|  |   const [open, setOpen] = useState(false) | ||||||
|  |   const [form] = Form.useForm() | ||||||
|  |   const name = Form.useWatch("name", form) | ||||||
|  | 
 | ||||||
|  |   React.useEffect(() => { | ||||||
|  |     if (messages.length > 0) { | ||||||
|  |       form.setFieldsValue({ | ||||||
|  |         title: messages[0].message | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |   }, [messages]) | ||||||
|  | 
 | ||||||
|  |   const onSubmit = async (values: { title: string; name: string }) => { | ||||||
|  |     const owner_id = await getUserId() | ||||||
|  |     const chat = reformatMessages(messages, values.name) | ||||||
|  |     const title = values.title | ||||||
|  |     const url = await getPageShareUrl() | ||||||
|  |     const res = await fetch(`${cleanUrl(url)}/api/v1/share/create`, { | ||||||
|  |       method: "POST", | ||||||
|  |       headers: { | ||||||
|  |         "Content-Type": "application/json" | ||||||
|  |       }, | ||||||
|  |       body: JSON.stringify({ | ||||||
|  |         owner_id, | ||||||
|  |         messages: chat, | ||||||
|  |         title | ||||||
|  |       }) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     if (!res.ok) throw new Error("Failed to create share link") | ||||||
|  | 
 | ||||||
|  |     const data = await res.json() | ||||||
|  | 
 | ||||||
|  |     return { | ||||||
|  |       ...data, | ||||||
|  |       url: `${cleanUrl(url)}/share/${data.chat_id}`, | ||||||
|  |       api_url: cleanUrl(url), | ||||||
|  |       share_id: data.chat_id | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const { mutate: createShareLink, isPending } = useMutation({ | ||||||
|  |     mutationFn: onSubmit, | ||||||
|  |     onSuccess: async (data) => { | ||||||
|  |       const url = data.url | ||||||
|  |       navigator.clipboard.writeText(url) | ||||||
|  |       message.success("Link copied to clipboard") | ||||||
|  |       await saveWebshare({ title: data.title, url, api_url: data.api_url, share_id: data.share_id }) | ||||||
|  |       setOpen(false) | ||||||
|  |     }, | ||||||
|  |     onError: (error) => { | ||||||
|  |       message.error(error?.message || "Failed to create share link") | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <> | ||||||
|  |       <Tooltip title="Share"> | ||||||
|  |         <button | ||||||
|  |           onClick={() => setOpen(true)} | ||||||
|  |           className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"> | ||||||
|  |           <Share className="w-6 h-6" /> | ||||||
|  |         </button> | ||||||
|  |       </Tooltip> | ||||||
|  | 
 | ||||||
|  |       <Modal | ||||||
|  |         title="Share link to Chat" | ||||||
|  |         open={open} | ||||||
|  |         footer={null} | ||||||
|  |         width={600} | ||||||
|  |         onCancel={() => setOpen(false)}> | ||||||
|  |         <Form | ||||||
|  |           form={form} | ||||||
|  |           layout="vertical" | ||||||
|  |           onFinish={createShareLink} | ||||||
|  |           initialValues={{ | ||||||
|  |             title: "Untitled Chat", | ||||||
|  |             name: "Anonymous" | ||||||
|  |           }}> | ||||||
|  |           <Form.Item | ||||||
|  |             name="title" | ||||||
|  |             label="Chat Title" | ||||||
|  |             rules={[{ required: true, message: "Please enter chat title" }]}> | ||||||
|  |             <Input size="large" placeholder="Enter chat title" /> | ||||||
|  |           </Form.Item> | ||||||
|  |           <Form.Item | ||||||
|  |             name="name" | ||||||
|  |             label="Your Name" | ||||||
|  |             rules={[{ required: true, message: "Please enter your name" }]}> | ||||||
|  |             <Input size="large" placeholder="Enter your name" /> | ||||||
|  |           </Form.Item> | ||||||
|  | 
 | ||||||
|  |           <Form.Item> | ||||||
|  |             <div className="max-h-[180px] overflow-x-auto border dark:border-gray-700 rounded-md p-2"> | ||||||
|  |               <div className="flex flex-col p-3"> | ||||||
|  |                 {messages.map((message, index) => ( | ||||||
|  |                   <PlaygroundMessage key={index} {...message} username={name} /> | ||||||
|  |                 ))} | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |           </Form.Item> | ||||||
|  | 
 | ||||||
|  |           <Form.Item> | ||||||
|  |             <div className="flex justify-end"> | ||||||
|  |               <button | ||||||
|  |                 type="submit" | ||||||
|  |                 className="inline-flex items-center rounded-md border border-transparent bg-black px-2 py-2.5 text-md font-medium leading-4 text-white shadow-sm dark:bg-white dark:text-gray-800 disabled:opacity-50 "> | ||||||
|  |                 {isPending ? "Generating link..." : "Generate Link"} | ||||||
|  |               </button> | ||||||
|  |             </div> | ||||||
|  |           </Form.Item> | ||||||
|  |         </Form> | ||||||
|  |       </Modal> | ||||||
|  |     </> | ||||||
|  |   ) | ||||||
|  | } | ||||||
| @ -16,6 +16,7 @@ import { | |||||||
|   ZapIcon |   ZapIcon | ||||||
| } from "lucide-react" | } from "lucide-react" | ||||||
| import { getAllPrompts } from "~libs/db" | import { getAllPrompts } from "~libs/db" | ||||||
|  | import { ShareBtn } from "~components/Common/ShareBtn" | ||||||
| 
 | 
 | ||||||
| export default function OptionLayout({ | export default function OptionLayout({ | ||||||
|   children |   children | ||||||
| @ -29,7 +30,9 @@ export default function OptionLayout({ | |||||||
|     clearChat, |     clearChat, | ||||||
|     selectedSystemPrompt, |     selectedSystemPrompt, | ||||||
|     setSelectedQuickPrompt, |     setSelectedQuickPrompt, | ||||||
|     setSelectedSystemPrompt |     setSelectedSystemPrompt, | ||||||
|  |     messages, | ||||||
|  |     streaming | ||||||
|   } = useMessageOption() |   } = useMessageOption() | ||||||
| 
 | 
 | ||||||
|   const { |   const { | ||||||
| @ -155,6 +158,9 @@ export default function OptionLayout({ | |||||||
|             <div className="flex flex-1 justify-end px-4"> |             <div className="flex flex-1 justify-end px-4"> | ||||||
|               <div className="ml-4 flex items-center md:ml-6"> |               <div className="ml-4 flex items-center md:ml-6"> | ||||||
|                 <div className="flex gap-4 items-center"> |                 <div className="flex gap-4 items-center"> | ||||||
|  |                   {pathname === "/" && messages.length > 0 && !streaming && ( | ||||||
|  |                     <ShareBtn messages={messages} /> | ||||||
|  |                   )} | ||||||
|                   {/* <Tooltip title="Manage Prompts"> |                   {/* <Tooltip title="Manage Prompts"> | ||||||
|                     <NavLink |                     <NavLink | ||||||
|                       to="/prompts" |                       to="/prompts" | ||||||
|  | |||||||
| @ -2,7 +2,8 @@ import { | |||||||
|   Book, |   Book, | ||||||
|   BrainCircuit, |   BrainCircuit, | ||||||
|   CircuitBoardIcon, |   CircuitBoardIcon, | ||||||
|   Orbit |   Orbit, | ||||||
|  |   Share | ||||||
| } from "lucide-react" | } from "lucide-react" | ||||||
| import { Link, useLocation } from "react-router-dom" | import { Link, useLocation } from "react-router-dom" | ||||||
| 
 | 
 | ||||||
| @ -46,7 +47,7 @@ export const SettingsLayout = ({ children }: { children: React.ReactNode }) => { | |||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <div className="mx-auto max-w-7xl lg:flex lg:gap-x-16 lg:px-8"> |       <div className="mx-auto max-w-7xl lg:flex lg:gap-x-16 lg:px-8"> | ||||||
|         <aside className="flex lg:rounded-md bg-white lg:h-56 lg:p-4 lg:mt-20 overflow-x-auto lg:border border-b  py-4 lg:block lg:w-64 lg:flex-none  dark:bg-[#171717] dark:border-gray-600"> |         <aside className="flex lg:rounded-md bg-white lg:p-4 lg:mt-20 overflow-x-auto lg:border-0 border-b  py-4 lg:block lg:w-64 lg:flex-none  dark:bg-[#171717] dark:border-gray-600"> | ||||||
|           <nav className="flex-none  px-4 sm:px-6 lg:px-0"> |           <nav className="flex-none  px-4 sm:px-6 lg:px-0"> | ||||||
|             <ul |             <ul | ||||||
|               role="list" |               role="list" | ||||||
| @ -75,6 +76,12 @@ export const SettingsLayout = ({ children }: { children: React.ReactNode }) => { | |||||||
|                 icon={Book} |                 icon={Book} | ||||||
|                 current={location.pathname} |                 current={location.pathname} | ||||||
|               /> |               /> | ||||||
|  |                <LinkComponent | ||||||
|  |                 href="/settings/share" | ||||||
|  |                 name="Manage Share" | ||||||
|  |                 icon={Share} | ||||||
|  |                 current={location.pathname} | ||||||
|  |               /> | ||||||
|             </ul> |             </ul> | ||||||
|           </nav> |           </nav> | ||||||
|         </aside> |         </aside> | ||||||
|  | |||||||
							
								
								
									
										190
									
								
								src/components/Option/Share/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								src/components/Option/Share/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,190 @@ | |||||||
|  | import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" | ||||||
|  | import { Form, Input, Skeleton, Table, Tooltip, message } from "antd" | ||||||
|  | import { Trash2 } from "lucide-react" | ||||||
|  | import { SaveButton } from "~components/Common/SaveButton" | ||||||
|  | import { deleteWebshare, getAllWebshares, getUserId } from "~libs/db" | ||||||
|  | import { getPageShareUrl, setPageShareUrl } from "~services/ollama" | ||||||
|  | import { verifyPageShareURL } from "~utils/verify-page-share" | ||||||
|  | 
 | ||||||
|  | export const OptionShareBody = () => { | ||||||
|  |   const queryClient = useQueryClient() | ||||||
|  |   const { status, data } = useQuery({ | ||||||
|  |     queryKey: ["fetchShareInfo"], | ||||||
|  |     queryFn: async () => { | ||||||
|  |       const [url, shares] = await Promise.all([ | ||||||
|  |         getPageShareUrl(), | ||||||
|  |         getAllWebshares() | ||||||
|  |       ]) | ||||||
|  |       return { url, shares } | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   const onSubmit = async (values: { url: string }) => { | ||||||
|  |     const isOk = await verifyPageShareURL(values.url) | ||||||
|  |     if (isOk) { | ||||||
|  |       await setPageShareUrl(values.url) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const onDelete = async ({ | ||||||
|  |     api_url, | ||||||
|  |     share_id, | ||||||
|  |     id | ||||||
|  |   }: { | ||||||
|  |     id: string | ||||||
|  |     share_id: string | ||||||
|  |     api_url: string | ||||||
|  |   }) => { | ||||||
|  |     const owner_id = await getUserId() | ||||||
|  |     const res = await fetch(`${api_url}/api/v1/share/delete`, { | ||||||
|  |       method: "POST", | ||||||
|  |       headers: { | ||||||
|  |         "Content-Type": "application/json" | ||||||
|  |       }, | ||||||
|  |       body: JSON.stringify({ | ||||||
|  |         share_id, | ||||||
|  |         owner_id | ||||||
|  |       }) | ||||||
|  |     }) | ||||||
|  |     if (!res.ok) throw new Error("Failed to delete share link") | ||||||
|  |     await deleteWebshare(id) | ||||||
|  |     return "ok" | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const { mutate: updatePageShareUrl, isPending: isUpdatePending } = | ||||||
|  |     useMutation({ | ||||||
|  |       mutationFn: onSubmit, | ||||||
|  |       onSuccess: () => { | ||||||
|  |         queryClient.invalidateQueries({ | ||||||
|  |           queryKey: ["fetchShareInfo"] | ||||||
|  |         }) | ||||||
|  |         message.success("Page Share URL updated successfully") | ||||||
|  |       }, | ||||||
|  |       onError: (error) => { | ||||||
|  |         message.error(error?.message || "Failed to update Page Share URL") | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |   const { mutate: deleteMutation } = useMutation({ | ||||||
|  |     mutationFn: onDelete, | ||||||
|  |     onSuccess: () => { | ||||||
|  |       queryClient.invalidateQueries({ | ||||||
|  |         queryKey: ["fetchShareInfo"] | ||||||
|  |       }) | ||||||
|  |       message.success("Webshare deleted successfully") | ||||||
|  |     }, | ||||||
|  |     onError: (error) => { | ||||||
|  |       message.error(error?.message || "Failed to delete Webshare") | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div className="flex flex-col space-y-3"> | ||||||
|  |       {status === "pending" && <Skeleton paragraph={{ rows: 4 }} active />} | ||||||
|  |       {status === "success" && ( | ||||||
|  |         <div> | ||||||
|  |           <div> | ||||||
|  |             <div> | ||||||
|  |               <h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white"> | ||||||
|  |                 Configure Page Share URL | ||||||
|  |               </h2> | ||||||
|  |               <div className="border border-b border-gray-200 dark:border-gray-600 mt-3 mb-6"></div> | ||||||
|  |             </div> | ||||||
|  |             <Form | ||||||
|  |               layout="vertical" | ||||||
|  |               onFinish={updatePageShareUrl} | ||||||
|  |               initialValues={{ | ||||||
|  |                 url: data.url | ||||||
|  |               }}> | ||||||
|  |               <Form.Item | ||||||
|  |                 name="url" | ||||||
|  |                 help={ | ||||||
|  |                   <span> | ||||||
|  |                     For privacy reasons, you can self-host the page share and | ||||||
|  |                     provide the URL here.{" "} | ||||||
|  |                     <a | ||||||
|  |                       href="https://github.com/n4ze3m/page-assist/blob/main/page-share.md" | ||||||
|  |                       target="__blank" | ||||||
|  |                       className="text-blue-600 dark:text-blue-400"> | ||||||
|  |                       Learn more | ||||||
|  |                     </a> | ||||||
|  |                   </span> | ||||||
|  |                 } | ||||||
|  |                 rules={[ | ||||||
|  |                   { | ||||||
|  |                     required: true, | ||||||
|  |                     message: "Please input your Page Share URL!" | ||||||
|  |                   } | ||||||
|  |                 ]} | ||||||
|  |                 label="Page Share URL"> | ||||||
|  |                 <Input placeholder="Page Share URL" size="large" /> | ||||||
|  |               </Form.Item> | ||||||
|  |               <Form.Item> | ||||||
|  |                 <div className="flex justify-end"> | ||||||
|  |                   <SaveButton disabled={isUpdatePending} btnType="submit" /> | ||||||
|  |                 </div> | ||||||
|  |               </Form.Item> | ||||||
|  |             </Form> | ||||||
|  |           </div> | ||||||
|  |           <div> | ||||||
|  |             <div> | ||||||
|  |               <h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white"> | ||||||
|  |                 Webshares | ||||||
|  |               </h2> | ||||||
|  |               <div className="border border-b border-gray-200 dark:border-gray-600 mt-3 mb-6"></div> | ||||||
|  |             </div> | ||||||
|  |             <div> | ||||||
|  |               <Table | ||||||
|  |                 dataSource={data.shares} | ||||||
|  |                 columns={[ | ||||||
|  |                   { | ||||||
|  |                     title: "Title", | ||||||
|  |                     dataIndex: "title", | ||||||
|  |                     key: "title" | ||||||
|  |                   }, | ||||||
|  |                   { | ||||||
|  |                     title: "URL", | ||||||
|  |                     dataIndex: "url", | ||||||
|  |                     key: "url", | ||||||
|  |                     render: (url: string) => ( | ||||||
|  |                       <a | ||||||
|  |                         href={url} | ||||||
|  |                         target="__blank" | ||||||
|  |                         className="text-blue-600 dark:text-blue-400"> | ||||||
|  |                         {url} | ||||||
|  |                       </a> | ||||||
|  |                     ) | ||||||
|  |                   }, | ||||||
|  |                   { | ||||||
|  |                     title: "Actions", | ||||||
|  |                     render: (_, render) => ( | ||||||
|  |                       <Tooltip title="Delete Share"> | ||||||
|  |                         <button | ||||||
|  |                           onClick={() => { | ||||||
|  |                             if ( | ||||||
|  |                               window.confirm( | ||||||
|  |                                 "Are you sure you want to delete this webshare?" | ||||||
|  |                               ) | ||||||
|  |                             ) { | ||||||
|  |                               deleteMutation({ | ||||||
|  |                                 id: render.id, | ||||||
|  |                                 share_id: render.share_id, | ||||||
|  |                                 api_url: render.api_url | ||||||
|  |                               }) | ||||||
|  |                             } | ||||||
|  |                           }} | ||||||
|  |                           className="text-red-500 dark:text-red-400"> | ||||||
|  |                           <Trash2 className="w-5 h-5" /> | ||||||
|  |                         </button> | ||||||
|  |                       </Tooltip> | ||||||
|  |                     ) | ||||||
|  |                   } | ||||||
|  |                 ]} | ||||||
|  |               /> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       )} | ||||||
|  |     </div> | ||||||
|  |   ) | ||||||
|  | } | ||||||
| @ -32,6 +32,16 @@ type Message = { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | type Webshare = { | ||||||
|  |   id: string | ||||||
|  |   title: string | ||||||
|  |   url: string | ||||||
|  |   api_url: string | ||||||
|  |   share_id: string | ||||||
|  |   createdAt: number | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| type Prompt = { | type Prompt = { | ||||||
|   id: string |   id: string | ||||||
|   title: string |   title: string | ||||||
| @ -154,6 +164,47 @@ export class PageAssitDatabase { | |||||||
|     return prompts.find((prompt) => prompt.id === id) |     return prompts.find((prompt) => prompt.id === id) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |   async getWebshare(id: string) { | ||||||
|  |     return new Promise((resolve, reject) => { | ||||||
|  |       this.db.get(id, (result) => { | ||||||
|  |         resolve(result[id] || []) | ||||||
|  |       }) | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   async getAllWebshares(): Promise<Webshare[]> { | ||||||
|  |     return new Promise((resolve, reject) => { | ||||||
|  |       this.db.get("webshares", (result) => { | ||||||
|  |         resolve(result.webshares || []) | ||||||
|  |       }) | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async addWebshare(webshare: Webshare) { | ||||||
|  |     const webshares = await this.getAllWebshares() | ||||||
|  |     const newWebshares = [webshare, ...webshares] | ||||||
|  |     this.db.set({ webshares: newWebshares }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async deleteWebshare(id: string) { | ||||||
|  |     const webshares = await this.getAllWebshares() | ||||||
|  |     const newWebshares = webshares.filter((webshare) => webshare.id !== id) | ||||||
|  |     this.db.set({ webshares: newWebshares }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async getUserID() { | ||||||
|  |     return new Promise((resolve, reject) => { | ||||||
|  |       this.db.get("user_id", (result) => { | ||||||
|  |         resolve(result.user_id || "") | ||||||
|  |       }) | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async setUserID(id: string) { | ||||||
|  |     this.db.set({ user_id: id }) | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -278,3 +329,38 @@ export const getPromptById = async (id: string) => { | |||||||
|   const db = new PageAssitDatabase() |   const db = new PageAssitDatabase() | ||||||
|   return await db.getPromptById(id) |   return await db.getPromptById(id) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export const getAllWebshares = async () => { | ||||||
|  |   const db = new PageAssitDatabase() | ||||||
|  |   return await db.getAllWebshares() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const deleteWebshare = async (id: string) => { | ||||||
|  |   const db = new PageAssitDatabase() | ||||||
|  |   await db.deleteWebshare(id) | ||||||
|  |   return id | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const saveWebshare = async ({ title, url, api_url, share_id }: { title: string, url: string, api_url: string, share_id: string }) => { | ||||||
|  |   const db = new PageAssitDatabase() | ||||||
|  |   const id = generateID() | ||||||
|  |   const createdAt = Date.now() | ||||||
|  |   const webshare = { id, title, url, share_id, createdAt, api_url } | ||||||
|  |   await db.addWebshare(webshare) | ||||||
|  |   return webshare | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const getUserId = async () => { | ||||||
|  |   const db = new PageAssitDatabase() | ||||||
|  |   const id = await db.getUserID() as string | ||||||
|  |   if (!id || id?.trim() === "") { | ||||||
|  |     const user_id = "user_xxxx-xxxx-xxx-xxxx-xxxx".replace(/[x]/g, () => { | ||||||
|  |       const r = Math.floor(Math.random() * 16) | ||||||
|  |       return r.toString(16) | ||||||
|  |     }) | ||||||
|  |     db.setUserID(user_id) | ||||||
|  |     return user_id | ||||||
|  |   } | ||||||
|  |   return id | ||||||
|  | } | ||||||
| @ -7,6 +7,7 @@ import { OptionModal } from "./option-settings-model" | |||||||
| import { OptionPrompt } from "./option-settings-prompt" | import { OptionPrompt } from "./option-settings-prompt" | ||||||
| import { OptionOllamaSettings } from "./options-settings-ollama" | import { OptionOllamaSettings } from "./options-settings-ollama" | ||||||
| import { OptionSettings } from "./option-settings" | import { OptionSettings } from "./option-settings" | ||||||
|  | import { OptionShare } from "./option-settings-share" | ||||||
| 
 | 
 | ||||||
| export const OptionRouting = () => { | export const OptionRouting = () => { | ||||||
|   const { mode } = useDarkMode() |   const { mode } = useDarkMode() | ||||||
| @ -19,6 +20,7 @@ export const OptionRouting = () => { | |||||||
|         <Route path="/settings/model" element={<OptionModal />} /> |         <Route path="/settings/model" element={<OptionModal />} /> | ||||||
|         <Route path="/settings/prompt" element={<OptionPrompt />} /> |         <Route path="/settings/prompt" element={<OptionPrompt />} /> | ||||||
|         <Route path="/settings/ollama" element={<OptionOllamaSettings />} /> |         <Route path="/settings/ollama" element={<OptionOllamaSettings />} /> | ||||||
|  |         <Route path="/settings/share" element={<OptionShare />} /> | ||||||
|       </Routes> |       </Routes> | ||||||
|     </div> |     </div> | ||||||
|   ) |   ) | ||||||
|  | |||||||
							
								
								
									
										13
									
								
								src/routes/option-settings-share.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/routes/option-settings-share.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout" | ||||||
|  | import OptionLayout from "~components/Layouts/Layout" | ||||||
|  | import { OptionShareBody } from "~components/Option/Share" | ||||||
|  | 
 | ||||||
|  | export const OptionShare = () => { | ||||||
|  |   return ( | ||||||
|  |     <OptionLayout> | ||||||
|  |       <SettingsLayout> | ||||||
|  |         <OptionShareBody /> | ||||||
|  |       </SettingsLayout> | ||||||
|  |     </OptionLayout> | ||||||
|  |   ) | ||||||
|  | } | ||||||
| @ -6,6 +6,7 @@ const storage = new Storage() | |||||||
| 
 | 
 | ||||||
| const DEFAULT_OLLAMA_URL = "http://127.0.0.1:11434" | const DEFAULT_OLLAMA_URL = "http://127.0.0.1:11434" | ||||||
| const DEFAULT_ASK_FOR_MODEL_SELECTION_EVERY_TIME = true | const DEFAULT_ASK_FOR_MODEL_SELECTION_EVERY_TIME = true | ||||||
|  | const DEFAULT_PAGE_SHARE_URL = "https://pageassist.xyz" | ||||||
| 
 | 
 | ||||||
| const DEFAULT_RAG_QUESTION_PROMPT = | const DEFAULT_RAG_QUESTION_PROMPT = | ||||||
|   "Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question.   Chat History: {chat_history} Follow Up Input: {question} Standalone question:" |   "Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question.   Chat History: {chat_history} Follow Up Input: {question} Standalone question:" | ||||||
| @ -303,3 +304,16 @@ export const getIsSimpleInternetSearch = async () => { | |||||||
| export const setIsSimpleInternetSearch = async (isSimpleInternetSearch: boolean) => { | export const setIsSimpleInternetSearch = async (isSimpleInternetSearch: boolean) => { | ||||||
|   await storage.set("isSimpleInternetSearch", isSimpleInternetSearch.toString()) |   await storage.set("isSimpleInternetSearch", isSimpleInternetSearch.toString()) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export const getPageShareUrl = async () => { | ||||||
|  |   const pageShareUrl = await storage.get("pageShareUrl") | ||||||
|  |   if (!pageShareUrl || pageShareUrl.length === 0) { | ||||||
|  |     return DEFAULT_PAGE_SHARE_URL | ||||||
|  |   } | ||||||
|  |   return pageShareUrl | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | export const setPageShareUrl = async (pageShareUrl: string) => { | ||||||
|  |   await storage.set("pageShareUrl", pageShareUrl) | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								src/utils/verify-page-share.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/utils/verify-page-share.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | import { cleanUrl } from "~libs/clean-url" | ||||||
|  | 
 | ||||||
|  | export const verifyPageShareURL = async (url: string) => { | ||||||
|  |     const res = await fetch(`${cleanUrl(url)}/api/v1/ping`) | ||||||
|  |     if (!res.ok) { | ||||||
|  |         throw new Error("Unable to verify page share") | ||||||
|  |     } | ||||||
|  |     const data = await res.text() | ||||||
|  |     return data === "pong" | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user