Преглед изворни кода

feat: scrollable mask lists in new-chat page

Yidadaa пре 1 година
родитељ
комит
f14b413b7c
4 измењених фајлова са 72 додато и 73 уклоњено
  1. 16 6
      app/components/new-chat.module.scss
  2. 49 60
      app/components/new-chat.tsx
  3. 2 2
      app/locales/en.ts
  4. 5 5
      app/store/config.ts

+ 16 - 6
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}) {

+ 49 - 60
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<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 (
-    <div className={styles["mask"]} ref={domRef} onClick={props.onClick}>
+    <div className={styles["mask"]} onClick={props.onClick}>
       <MaskAvatar mask={props.mask} />
       <div className={styles["mask-name"] + " one-line"}>{props.mask.name}</div>
     </div>
@@ -63,32 +39,36 @@ function useMaskGroup(masks: Mask[]) {
   const [groups, setGroups] = useState<Mask[][]>([]);
 
   useEffect(() => {
-    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 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 newGroups = new Array(rows)
-      .fill(0)
-      .map((_, _i) =>
-        new Array(cols)
-          .fill(0)
-          .map((_, j) => (j < 1 || j > cols - 2 ? randomMask() : nextMask())),
-      );
-
-    setGroups(newGroups);
+    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 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 newGroups = new Array(rows)
+        .fill(0)
+        .map((_, _i) =>
+          new Array(cols)
+            .fill(0)
+            .map((_, j) => (j < 1 || j > cols - 2 ? randomMask() : nextMask())),
+        );
+
+      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<HTMLDivElement>(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 (
     <div className={styles["new-chat"]}>
       <div className={styles["mask-header"]}>
@@ -162,24 +151,24 @@ export function NewChat() {
 
       <div className={styles["actions"]}>
         <IconButton
-          text={Locale.NewChat.Skip}
-          onClick={() => startChat()}
-          icon={<LightningIcon />}
-          type="primary"
-          shadow
-        />
-
-        <IconButton
-          className={styles["more"]}
           text={Locale.NewChat.More}
           onClick={() => navigate(Path.Masks)}
           icon={<EyeIcon />}
           bordered
           shadow
         />
+
+        <IconButton
+          text={Locale.NewChat.Skip}
+          onClick={() => startChat()}
+          icon={<LightningIcon />}
+          type="primary"
+          shadow
+          className={styles["skip"]}
+        />
       </div>
 
-      <div className={styles["masks"]}>
+      <div className={styles["masks"]} ref={maskRef}>
         {groups.map((masks, i) => (
           <div key={i} className={styles["mask-row"]}>
             {masks.map((mask, index) => (

+ 2 - 2
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.",
   },
 

+ 5 - 5
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<ChatConfigStore>()(
 
         return state;
       },
-    }
-  )
+    },
+  ),
 );