|
|
@ -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,32 +39,36 @@ function useMaskGroup(masks: Mask[]) {
|
|
|
|
const [groups, setGroups] = useState<Mask[][]>([]);
|
|
|
|
const [groups, setGroups] = useState<Mask[][]>([]);
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
const appBody = document.getElementById(SlotID.AppBody);
|
|
|
|
const computeGroup = () => {
|
|
|
|
if (!appBody || masks.length === 0) return;
|
|
|
|
const appBody = document.getElementById(SlotID.AppBody);
|
|
|
|
|
|
|
|
if (!appBody || masks.length === 0) return;
|
|
|
|
const rect = appBody.getBoundingClientRect();
|
|
|
|
|
|
|
|
const maxWidth = rect.width;
|
|
|
|
const rect = appBody.getBoundingClientRect();
|
|
|
|
const maxHeight = rect.height * 0.6;
|
|
|
|
const maxWidth = rect.width;
|
|
|
|
const maskItemWidth = 120;
|
|
|
|
const maxHeight = rect.height * 0.6;
|
|
|
|
const maskItemHeight = 50;
|
|
|
|
const maskItemWidth = 120;
|
|
|
|
|
|
|
|
const maskItemHeight = 50;
|
|
|
|
const randomMask = () => masks[Math.floor(Math.random() * masks.length)];
|
|
|
|
|
|
|
|
let maskIndex = 0;
|
|
|
|
const randomMask = () => masks[Math.floor(Math.random() * masks.length)];
|
|
|
|
const nextMask = () => masks[maskIndex++ % 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)
|
|
|
|
const newGroups = new Array(rows)
|
|
|
|
.map((_, _i) =>
|
|
|
|
.fill(0)
|
|
|
|
new Array(cols)
|
|
|
|
.map((_, _i) =>
|
|
|
|
.fill(0)
|
|
|
|
new Array(cols)
|
|
|
|
.map((_, j) => (j < 1 || j > cols - 2 ? randomMask() : nextMask())),
|
|
|
|
.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
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
}, []);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
@ -105,6 +85,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 +105,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 +151,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) => (
|
|
|
|