From f14b413b7c94a477ce3644953a3df2b4ace666bf Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sat, 20 May 2023 23:49:10 +0800 Subject: [PATCH 1/4] feat: scrollable mask lists in new-chat page --- app/components/new-chat.module.scss | 22 +++++-- app/components/new-chat.tsx | 99 +++++++++++++---------------- app/locales/en.ts | 4 +- app/store/config.ts | 10 +-- 4 files changed, 67 insertions(+), 68 deletions(-) diff --git a/app/components/new-chat.module.scss b/app/components/new-chat.module.scss index b0e472ea..b291a236 100644 --- a/app/components/new-chat.module.scss +++ b/app/components/new-chat.module.scss @@ -54,13 +54,13 @@ .actions { margin-top: 5vh; - margin-bottom: 5vh; + margin-bottom: 2vh; animation: slide-in ease 0.45s; display: flex; justify-content: center; + font-size: 12px; - .more { - font-size: 12px; + .skip { margin-left: 10px; } } @@ -68,16 +68,26 @@ .masks { flex-grow: 1; width: 100%; - overflow: hidden; + overflow: auto; align-items: center; padding-top: 20px; + $linear: linear-gradient( + to bottom, + rgba(0, 0, 0, 0), + rgba(0, 0, 0, 1), + rgba(0, 0, 0, 0) + ); + + -webkit-mask-image: $linear; + mask-image: $linear; + animation: slide-in ease 0.5s; .mask-row { - margin-bottom: 10px; display: flex; - justify-content: center; + // justify-content: center; + margin-bottom: 10px; @for $i from 1 to 10 { &:nth-child(#{$i * 2}) { diff --git a/app/components/new-chat.tsx b/app/components/new-chat.tsx index 81858fb0..5fc42bb7 100644 --- a/app/components/new-chat.tsx +++ b/app/components/new-chat.tsx @@ -27,32 +27,8 @@ function getIntersectionArea(aRect: DOMRect, bRect: DOMRect) { } function MaskItem(props: { mask: Mask; onClick?: () => void }) { - const domRef = useRef(null); - - useEffect(() => { - const changeOpacity = () => { - const dom = domRef.current; - const parent = document.getElementById(SlotID.AppBody); - if (!parent || !dom) return; - - const domRect = dom.getBoundingClientRect(); - const parentRect = parent.getBoundingClientRect(); - const intersectionArea = getIntersectionArea(domRect, parentRect); - const domArea = domRect.width * domRect.height; - const ratio = intersectionArea / domArea; - const opacity = ratio > 0.9 ? 1 : 0.4; - dom.style.opacity = opacity.toString(); - }; - - setTimeout(changeOpacity, 30); - - window.addEventListener("resize", changeOpacity); - - return () => window.removeEventListener("resize", changeOpacity); - }, [domRef]); - return ( -
+
{props.mask.name}
@@ -63,32 +39,36 @@ function useMaskGroup(masks: Mask[]) { const [groups, setGroups] = useState([]); useEffect(() => { - const appBody = document.getElementById(SlotID.AppBody); - if (!appBody || masks.length === 0) return; + const computeGroup = () => { + const appBody = document.getElementById(SlotID.AppBody); + if (!appBody || masks.length === 0) return; - const rect = appBody.getBoundingClientRect(); - const maxWidth = rect.width; - const maxHeight = rect.height * 0.6; - const maskItemWidth = 120; - const maskItemHeight = 50; + const rect = appBody.getBoundingClientRect(); + const maxWidth = rect.width; + const maxHeight = rect.height * 0.6; + const maskItemWidth = 120; + const maskItemHeight = 50; - const randomMask = () => masks[Math.floor(Math.random() * masks.length)]; - let maskIndex = 0; - const nextMask = () => masks[maskIndex++ % masks.length]; + const randomMask = () => masks[Math.floor(Math.random() * masks.length)]; + let maskIndex = 0; + const nextMask = () => masks[maskIndex++ % masks.length]; - const rows = Math.ceil(maxHeight / maskItemHeight); - const cols = Math.ceil(maxWidth / maskItemWidth); + const rows = Math.ceil(maxHeight / maskItemHeight); + const cols = Math.ceil(maxWidth / maskItemWidth); - const newGroups = new Array(rows) - .fill(0) - .map((_, _i) => - new Array(cols) - .fill(0) - .map((_, j) => (j < 1 || j > cols - 2 ? randomMask() : nextMask())), - ); + const newGroups = new Array(rows) + .fill(0) + .map((_, _i) => + new Array(cols) + .fill(0) + .map((_, j) => (j < 1 || j > cols - 2 ? randomMask() : nextMask())), + ); - setGroups(newGroups); + setGroups(newGroups); + }; + window.addEventListener("resize", computeGroup); + return () => window.removeEventListener("resize", computeGroup); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -105,6 +85,8 @@ export function NewChat() { const navigate = useNavigate(); const config = useAppConfig(); + const maskRef = useRef(null); + const { state } = useLocation(); const startChat = (mask?: Mask) => { @@ -123,6 +105,13 @@ export function NewChat() { }, }); + useEffect(() => { + if (maskRef.current) { + maskRef.current.scrollLeft = + (maskRef.current.scrollWidth - maskRef.current.clientWidth) / 2; + } + }, [groups]); + return (
@@ -162,24 +151,24 @@ export function NewChat() {
startChat()} - icon={} - type="primary" - shadow - /> - - navigate(Path.Masks)} icon={} bordered shadow /> + + startChat()} + icon={} + type="primary" + shadow + className={styles["skip"]} + />
-
+
{groups.map((masks, i) => (
{masks.map((mask, index) => ( diff --git a/app/locales/en.ts b/app/locales/en.ts index cd7e7c71..1f136f00 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -221,11 +221,11 @@ const en: RequiredLocaleType = { }, NewChat: { Return: "Return", - Skip: "Skip", + Skip: "Just Start", Title: "Pick a Mask", SubTitle: "Chat with the Soul behind the Mask", More: "Find More", - NotShow: "Dont Show Again", + NotShow: "Never Show Again", ConfirmNoShow: "Confirm to disable?You can enable it in settings later.", }, diff --git a/app/store/config.ts b/app/store/config.ts index 5fc9cd54..6802a402 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -73,7 +73,7 @@ export const ALL_MODELS = [ available: ENABLE_GPT4, }, { - name: "ext-davinci-002-render-sha-mobile", + name: "text-davinci-002-render-sha-mobile", available: true, }, { @@ -106,13 +106,13 @@ export const ALL_MODELS = [ }, ] as const; -export type ModelType = typeof ALL_MODELS[number]["name"]; +export type ModelType = (typeof ALL_MODELS)[number]["name"]; export function limitNumber( x: number, min: number, max: number, - defaultValue: number + defaultValue: number, ) { if (typeof x !== "number" || isNaN(x)) { return defaultValue; @@ -171,6 +171,6 @@ export const useAppConfig = create()( return state; }, - } - ) + }, + ), ); From 76e6957a8acc8528217a016b53260cc1e62b9752 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sat, 20 May 2023 23:53:39 +0800 Subject: [PATCH 2/4] fixup --- app/components/new-chat.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/components/new-chat.tsx b/app/components/new-chat.tsx index 5fc42bb7..30041c5c 100644 --- a/app/components/new-chat.tsx +++ b/app/components/new-chat.tsx @@ -67,6 +67,8 @@ function useMaskGroup(masks: Mask[]) { setGroups(newGroups); }; + computeGroup(); + window.addEventListener("resize", computeGroup); return () => window.removeEventListener("resize", computeGroup); // eslint-disable-next-line react-hooks/exhaustive-deps From 600b1814a1b982e6faca151afff0518b15884c79 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sat, 20 May 2023 23:58:36 +0800 Subject: [PATCH 3/4] fix: wont show auth popup when receiving a 401 http code --- app/client/api.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/client/api.ts b/app/client/api.ts index c76fab57..a966d733 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -62,6 +62,7 @@ export function getHeaders() { const accessStore = useAccessStore.getState(); let headers: Record = { "Content-Type": "application/json", + "x-requested-with": "XMLHttpRequest", }; const makeBearer = (token: string) => `Bearer ${token.trim()}`; From c2b36cdffaa0b418bc22588c637f5fcde6fc9ef5 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 21 May 2023 00:06:28 +0800 Subject: [PATCH 4/4] feat: prevent browser to invoke basic auth popup --- app/api/common.ts | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/app/api/common.ts b/app/api/common.ts index eb073922..b606d8ca 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -30,26 +30,30 @@ export async function requestOpenai(req: NextRequest) { controller.abort(); }, 10 * 60 * 1000); + const fetchUrl = `${baseUrl}/${openaiPath}`; + const fetchOptions: RequestInit = { + headers: { + "Content-Type": "application/json", + Authorization: authValue, + ...(process.env.OPENAI_ORG_ID && { + "OpenAI-Organization": process.env.OPENAI_ORG_ID, + }), + }, + cache: "no-store", + method: req.method, + body: req.body, + signal: controller.signal, + }; + try { - return await fetch(`${baseUrl}/${openaiPath}`, { - headers: { - "Content-Type": "application/json", - Authorization: authValue, - ...(process.env.OPENAI_ORG_ID && { - "OpenAI-Organization": process.env.OPENAI_ORG_ID, - }), - }, - cache: "no-store", - method: req.method, - body: req.body, - signal: controller.signal, - }); - } catch (err: unknown) { - if (err instanceof Error && err.name === "AbortError") { - console.log("Fetch aborted"); - } else { - throw err; + const res = await fetch(fetchUrl, fetchOptions); + + if (res.status === 401) { + // to prevent browser prompt for credentials + res.headers.delete("www-authenticate"); } + + return res; } finally { clearTimeout(timeoutId); }