forked from XiaoMo/ChatGPT-Next-Web
Merge pull request #1658 from Yidadaa/bugfix-0520
feat: scrollable mask lists in new-chat page
This commit is contained in:
commit
a57fa2e9ad
@ -30,8 +30,8 @@ export async function requestOpenai(req: NextRequest) {
|
|||||||
controller.abort();
|
controller.abort();
|
||||||
}, 10 * 60 * 1000);
|
}, 10 * 60 * 1000);
|
||||||
|
|
||||||
try {
|
const fetchUrl = `${baseUrl}/${openaiPath}`;
|
||||||
return await fetch(`${baseUrl}/${openaiPath}`, {
|
const fetchOptions: RequestInit = {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: authValue,
|
Authorization: authValue,
|
||||||
@ -43,13 +43,17 @@ export async function requestOpenai(req: NextRequest) {
|
|||||||
method: req.method,
|
method: req.method,
|
||||||
body: req.body,
|
body: req.body,
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
});
|
};
|
||||||
} catch (err: unknown) {
|
|
||||||
if (err instanceof Error && err.name === "AbortError") {
|
try {
|
||||||
console.log("Fetch aborted");
|
const res = await fetch(fetchUrl, fetchOptions);
|
||||||
} else {
|
|
||||||
throw err;
|
if (res.status === 401) {
|
||||||
|
// to prevent browser prompt for credentials
|
||||||
|
res.headers.delete("www-authenticate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
} finally {
|
} finally {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,7 @@ export function getHeaders() {
|
|||||||
const accessStore = useAccessStore.getState();
|
const accessStore = useAccessStore.getState();
|
||||||
let headers: Record<string, string> = {
|
let headers: Record<string, string> = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
"x-requested-with": "XMLHttpRequest",
|
||||||
};
|
};
|
||||||
|
|
||||||
const makeBearer = (token: string) => `Bearer ${token.trim()}`;
|
const makeBearer = (token: string) => `Bearer ${token.trim()}`;
|
||||||
|
@ -54,13 +54,13 @@
|
|||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
margin-top: 5vh;
|
margin-top: 5vh;
|
||||||
margin-bottom: 5vh;
|
margin-bottom: 2vh;
|
||||||
animation: slide-in ease 0.45s;
|
animation: slide-in ease 0.45s;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
.more {
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
||||||
|
.skip {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,16 +68,26 @@
|
|||||||
.masks {
|
.masks {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-top: 20px;
|
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;
|
animation: slide-in ease 0.5s;
|
||||||
|
|
||||||
.mask-row {
|
.mask-row {
|
||||||
margin-bottom: 10px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
// justify-content: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
@for $i from 1 to 10 {
|
@for $i from 1 to 10 {
|
||||||
&:nth-child(#{$i * 2}) {
|
&:nth-child(#{$i * 2}) {
|
||||||
|
@ -27,32 +27,8 @@ function getIntersectionArea(aRect: DOMRect, bRect: DOMRect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function MaskItem(props: { mask: Mask; onClick?: () => void }) {
|
function MaskItem(props: { mask: Mask; onClick?: () => void }) {
|
||||||
const domRef = useRef<HTMLDivElement>(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 (
|
return (
|
||||||
<div className={styles["mask"]} ref={domRef} onClick={props.onClick}>
|
<div className={styles["mask"]} onClick={props.onClick}>
|
||||||
<MaskAvatar mask={props.mask} />
|
<MaskAvatar mask={props.mask} />
|
||||||
<div className={styles["mask-name"] + " one-line"}>{props.mask.name}</div>
|
<div className={styles["mask-name"] + " one-line"}>{props.mask.name}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -63,6 +39,7 @@ function useMaskGroup(masks: Mask[]) {
|
|||||||
const [groups, setGroups] = useState<Mask[][]>([]);
|
const [groups, setGroups] = useState<Mask[][]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const computeGroup = () => {
|
||||||
const appBody = document.getElementById(SlotID.AppBody);
|
const appBody = document.getElementById(SlotID.AppBody);
|
||||||
if (!appBody || masks.length === 0) return;
|
if (!appBody || masks.length === 0) return;
|
||||||
|
|
||||||
@ -88,7 +65,12 @@ function useMaskGroup(masks: Mask[]) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
setGroups(newGroups);
|
setGroups(newGroups);
|
||||||
|
};
|
||||||
|
|
||||||
|
computeGroup();
|
||||||
|
|
||||||
|
window.addEventListener("resize", computeGroup);
|
||||||
|
return () => window.removeEventListener("resize", computeGroup);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -105,6 +87,8 @@ export function NewChat() {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const config = useAppConfig();
|
const config = useAppConfig();
|
||||||
|
|
||||||
|
const maskRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const { state } = useLocation();
|
const { state } = useLocation();
|
||||||
|
|
||||||
const startChat = (mask?: Mask) => {
|
const startChat = (mask?: Mask) => {
|
||||||
@ -123,6 +107,13 @@ export function NewChat() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (maskRef.current) {
|
||||||
|
maskRef.current.scrollLeft =
|
||||||
|
(maskRef.current.scrollWidth - maskRef.current.clientWidth) / 2;
|
||||||
|
}
|
||||||
|
}, [groups]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles["new-chat"]}>
|
<div className={styles["new-chat"]}>
|
||||||
<div className={styles["mask-header"]}>
|
<div className={styles["mask-header"]}>
|
||||||
@ -162,24 +153,24 @@ export function NewChat() {
|
|||||||
|
|
||||||
<div className={styles["actions"]}>
|
<div className={styles["actions"]}>
|
||||||
<IconButton
|
<IconButton
|
||||||
text={Locale.NewChat.Skip}
|
|
||||||
onClick={() => startChat()}
|
|
||||||
icon={<LightningIcon />}
|
|
||||||
type="primary"
|
|
||||||
shadow
|
|
||||||
/>
|
|
||||||
|
|
||||||
<IconButton
|
|
||||||
className={styles["more"]}
|
|
||||||
text={Locale.NewChat.More}
|
text={Locale.NewChat.More}
|
||||||
onClick={() => navigate(Path.Masks)}
|
onClick={() => navigate(Path.Masks)}
|
||||||
icon={<EyeIcon />}
|
icon={<EyeIcon />}
|
||||||
bordered
|
bordered
|
||||||
shadow
|
shadow
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
text={Locale.NewChat.Skip}
|
||||||
|
onClick={() => startChat()}
|
||||||
|
icon={<LightningIcon />}
|
||||||
|
type="primary"
|
||||||
|
shadow
|
||||||
|
className={styles["skip"]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles["masks"]}>
|
<div className={styles["masks"]} ref={maskRef}>
|
||||||
{groups.map((masks, i) => (
|
{groups.map((masks, i) => (
|
||||||
<div key={i} className={styles["mask-row"]}>
|
<div key={i} className={styles["mask-row"]}>
|
||||||
{masks.map((mask, index) => (
|
{masks.map((mask, index) => (
|
||||||
|
@ -221,11 +221,11 @@ const en: RequiredLocaleType = {
|
|||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "Return",
|
Return: "Return",
|
||||||
Skip: "Skip",
|
Skip: "Just Start",
|
||||||
Title: "Pick a Mask",
|
Title: "Pick a Mask",
|
||||||
SubTitle: "Chat with the Soul behind the Mask",
|
SubTitle: "Chat with the Soul behind the Mask",
|
||||||
More: "Find More",
|
More: "Find More",
|
||||||
NotShow: "Dont Show Again",
|
NotShow: "Never Show Again",
|
||||||
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ export const ALL_MODELS = [
|
|||||||
available: ENABLE_GPT4,
|
available: ENABLE_GPT4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ext-davinci-002-render-sha-mobile",
|
name: "text-davinci-002-render-sha-mobile",
|
||||||
available: true,
|
available: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -106,13 +106,13 @@ export const ALL_MODELS = [
|
|||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type ModelType = typeof ALL_MODELS[number]["name"];
|
export type ModelType = (typeof ALL_MODELS)[number]["name"];
|
||||||
|
|
||||||
export function limitNumber(
|
export function limitNumber(
|
||||||
x: number,
|
x: number,
|
||||||
min: number,
|
min: number,
|
||||||
max: number,
|
max: number,
|
||||||
defaultValue: number
|
defaultValue: number,
|
||||||
) {
|
) {
|
||||||
if (typeof x !== "number" || isNaN(x)) {
|
if (typeof x !== "number" || isNaN(x)) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
@ -171,6 +171,6 @@ export const useAppConfig = create<ChatConfigStore>()(
|
|||||||
|
|
||||||
return state;
|
return state;
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user