feat: maskpage&newchatpage adapt new ui framework done

This commit is contained in:
butterfly
2024-04-19 11:55:51 +08:00
parent b3559f99a2
commit 3d0a98d5d2
40 changed files with 2273 additions and 774 deletions

View File

@@ -1,35 +0,0 @@
import { Path } from "@/app/constant";
import { ComponentType } from "react";
import { useNavigate } from "react-router-dom";
export interface MenuWrapperProps {
show: boolean;
wrapperClassName?: string;
}
export default function MenuWrapper<ComponentProps>(
Component: ComponentType<ComponentProps>,
) {
return function MenuHood(props: MenuWrapperProps & ComponentProps) {
const { show, wrapperClassName } = props;
const navigate = useNavigate();
if (!show) {
return null;
}
return (
<div
className={`flex flex-col px-6 pb-6 ${wrapperClassName}`}
onClick={(e) => {
if (e.target === e.currentTarget) {
navigate(Path.Home);
}
}}
>
<Component {...props} />
</div>
);
};
}

View File

@@ -1,220 +0,0 @@
import {
DragDropContext,
Droppable,
Draggable,
OnDragEndResponder,
} from "@hello-pangea/dnd";
import { useAppConfig, useChatStore } from "@/app/store";
import Locale from "@/app/locales";
import { useLocation, useNavigate } from "react-router-dom";
import { Path } from "@/app/constant";
import { Mask } from "@/app/store/mask";
import { useRef, useEffect, useMemo } from "react";
import { showConfirm } from "@/app/components/ui-lib";
import AddIcon from "@/app/icons/addIcon.svg";
import NextChatTitle from "@/app/icons/nextchatTitle.svg";
import { ListHoodProps } from "./types";
import useMobileScreen from "@/app/hooks/useMobileScreen";
import { getTime } from "@/app/utils";
import DeleteIcon from "@/app/icons/deleteIcon.svg";
import LogIcon from "@/app/icons/logIcon.svg";
export function SessionItem(props: {
onClick?: () => void;
onDelete?: () => void;
title: string;
count: number;
time: string;
selected: boolean;
id: string;
index: number;
narrow?: boolean;
mask: Mask;
}) {
const draggableRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
if (props.selected && draggableRef.current) {
draggableRef.current?.scrollIntoView({
block: "center",
});
}
}, [props.selected]);
const { pathname: currentPath } = useLocation();
return (
<Draggable draggableId={`${props.id}`} index={props.index}>
{(provided) => (
<div
className={`group relative flex p-3 items-center gap-2 self-stretch rounded-md hover:bg-gray-200 mb-2 ${
props.selected &&
(currentPath === Path.Chat || currentPath === Path.Home)
? `bg-blue-100 border-blue-200 border`
: `bg-gray-100`
}`}
onClick={props.onClick}
ref={(ele) => {
draggableRef.current = ele;
provided.innerRef(ele);
}}
{...provided.draggableProps}
{...provided.dragHandleProps}
title={`${props.title}\n${Locale.ChatItem.ChatItemCount(
props.count,
)}`}
>
<div className=" flex-shrink-0">
<LogIcon />
</div>
<div className="flex flex-col flex-1">
<div className={`flex justify-between items-center`}>
<div
className={` text-gray-900 text-sm-title line-clamp-1 flex-1`}
>
{props.title}
</div>
<div
className={`text-gray-500 text-sm group-hover:opacity-0 pl-3`}
>
{getTime(props.time)}
</div>
</div>
<div className={`text-gray-500 text-sm`}>
{Locale.ChatItem.ChatItemCount(props.count)}
</div>
</div>
<div
className={`absolute top-[50%] translate-y-[-50%] right-3 pointer-events-none opacity-0 group-hover:pointer-events-auto group-hover:opacity-100`}
onClickCapture={(e) => {
props.onDelete?.();
e.preventDefault();
e.stopPropagation();
}}
>
<DeleteIcon />
</div>
</div>
)}
</Draggable>
);
}
export default function SessionList(props: ListHoodProps) {
const [sessions, selectedIndex, selectSession, moveSession] = useChatStore(
(state) => [
state.sessions,
state.currentSessionIndex,
state.selectSession,
state.moveSession,
],
);
const chatStore = useChatStore();
const navigate = useNavigate();
const isMobileScreen = useMobileScreen();
const config = useAppConfig();
const onDragEnd: OnDragEndResponder = (result) => {
const { destination, source } = result;
if (!destination) {
return;
}
if (
destination.droppableId === source.droppableId &&
destination.index === source.index
) {
return;
}
moveSession(source.index, destination.index);
};
let layoutClassName = "py-7 px-0";
if (isMobileScreen) {
layoutClassName = "h-menu-title-mobile py-6";
}
return (
<>
<div data-tauri-drag-region>
<div
className={`flex items-center justify-between ${layoutClassName}`}
data-tauri-drag-region
>
<div className="">
<NextChatTitle />
</div>
<div
className=""
onClick={() => {
if (config.dontShowMaskSplashScreen) {
chatStore.newSession();
navigate(Path.Chat);
} else {
navigate(Path.NewChat);
}
}}
>
<AddIcon />
</div>
</div>
<div className={`pb-3 text-sm sm:text-sm-mobile text-blue-500`}>
Build your own AI assistant.
</div>
</div>
<div
className={`flex overflow-y-auto overflow-x-hidden`}
onClick={(e) => {
if (e.target === e.currentTarget) {
navigate(Path.Home);
}
}}
>
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="chat-list">
{(provided) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
className="w-[100%]"
>
{sessions.map((item, i) => (
<SessionItem
title={item.topic}
time={new Date(item.lastUpdate).toLocaleString()}
count={item.messages.length}
key={item.id}
id={item.id}
index={i}
selected={i === selectedIndex}
onClick={() => {
navigate(Path.Chat);
selectSession(i);
}}
onDelete={async () => {
if (
!isMobileScreen ||
(await showConfirm(Locale.Home.DeleteChat))
) {
chatStore.deleteSession(i);
}
}}
mask={item.mask}
/>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</div>
</>
);
}

View File

@@ -1,5 +0,0 @@
import { ListHoodProps } from "./types";
export default function SettingList(props: ListHoodProps) {
return <></>;
}

View File

@@ -1,42 +1,25 @@
import DragIcon from "@/app/icons/drag.svg";
import DiscoverIcon from "@/app/icons/discoverActive.svg";
import AssistantActiveIcon from "@/app/icons/assistantActive.svg";
import GitHubIcon from "@/app/icons/githubIcon.svg";
import SettingIcon from "@/app/icons/settingActive.svg";
import DiscoverIcon from "@/app/icons/discoverActive.svg";
import DiscoverInactiveIcon from "@/app/icons/discoverInactive.svg";
import AssistantInactiveIcon from "@/app/icons/assistantInactive.svg";
import DiscoverMobileActive from "@/app/icons/discoverMobileActive.svg";
import DiscoverMobileInactive from "@/app/icons/discoverMobileInactive.svg";
import SettingIcon from "@/app/icons/settingActive.svg";
import SettingInactiveIcon from "@/app/icons/settingInactive.svg";
import SettingMobileActive from "@/app/icons/settingMobileActive.svg";
import DiscoverMobileActive from "@/app/icons/discoverMobileActive.svg";
import SettingMobileInactive from "@/app/icons/settingMobileInactive.svg";
import AssistantActiveIcon from "@/app/icons/assistantActive.svg";
import AssistantInactiveIcon from "@/app/icons/assistantInactive.svg";
import AssistantMobileActive from "@/app/icons/assistantMobileActive.svg";
import AssistantMobileInactive from "@/app/icons/assistantMobileInactive.svg";
import { useAppConfig } from "@/app/store";
import { Path, REPO_URL } from "@/app/constant";
import { useNavigate, useLocation } from "react-router-dom";
import dynamic from "next/dynamic";
import useHotKey from "@/app/hooks/useHotKey";
import useDragSideBar from "@/app/hooks/useDragSideBar";
import useMobileScreen from "@/app/hooks/useMobileScreen";
import MenuWrapper from "./MenuWrapper";
import ActionsBar from "@/app/components/ActionsBar";
const SessionList = MenuWrapper(
dynamic(async () => await import("./SessionList"), {
loading: () => null,
}),
);
const SettingList = MenuWrapper(
dynamic(async () => await import("./SettingList"), {
loading: () => null,
}),
);
export function SideBar(props: { className?: string }) {
// drag side bar
const { onDragStart } = useDragSideBar();
const navigate = useNavigate();
const loc = useLocation();
@@ -56,18 +39,15 @@ export function SideBar(props: { className?: string }) {
selectedTab = Path.Settings;
break;
default:
selectedTab = Path.Chat;
selectedTab = Path.Home;
}
let containerClassName = "relative flex h-[100%] w-[100%]";
let containerClassName = "relative flex h-[100%]";
let tabActionsClassName = "2xl:px-5 xl:px-4 px-2 py-6";
let menuClassName =
"max-md:px-4 max-md:pb-4 rounded-md my-2.5 bg-gray-50 flex-1";
if (isMobileScreen) {
containerClassName = "flex flex-col-reverse w-[100%] h-[100%]";
tabActionsClassName = "bg-gray-100 rounded-tl-md rounded-tr-md h-mobile";
menuClassName = `flex-1 px-4`;
}
return (
@@ -81,12 +61,12 @@ export function SideBar(props: { className?: string }) {
active: <DiscoverIcon />,
inactive: <DiscoverInactiveIcon />,
mobileActive: <DiscoverMobileActive />,
mobileInactive: <DiscoverInactiveIcon />,
mobileInactive: <DiscoverMobileInactive />,
},
title: "Discover",
},
{
id: Path.Chat,
id: Path.Home,
icons: {
active: <AssistantActiveIcon />,
inactive: <AssistantInactiveIcon />,
@@ -106,7 +86,7 @@ export function SideBar(props: { className?: string }) {
active: <SettingIcon />,
inactive: <SettingInactiveIcon />,
mobileActive: <SettingMobileActive />,
mobileInactive: <SettingInactiveIcon />,
mobileInactive: <SettingMobileInactive />,
},
className: "p-2",
title: "Settrings",
@@ -127,36 +107,16 @@ export function SideBar(props: { className?: string }) {
}}
groups={{
normal: [
[Path.Chat, Path.Masks],
[Path.Home, Path.Masks],
["github", Path.Settings],
],
mobile: [[Path.Chat, Path.Masks, Path.Settings]],
mobile: [[Path.Home, Path.Masks, Path.Settings]],
}}
selected={selectedTab}
className={`${
isMobileScreen ? "justify-around" : "flex-col"
} ${tabActionsClassName}`}
/>
<SessionList
show={selectedTab === Path.Chat}
wrapperClassName={menuClassName}
/>
<SettingList
show={selectedTab === Path.Settings}
wrapperClassName={menuClassName}
/>
{!isMobileScreen && (
<div
className={`group absolute right-0 h-[100%] flex items-center`}
onPointerDown={(e) => onDragStart(e as any)}
>
<div className="opacity-0 group-hover:bg-[rgba($color: #000000, $alpha: 0.01) group-hover:opacity-20">
<DragIcon />
</div>
</div>
)}
</div>
);
}

View File

@@ -1,4 +0,0 @@
export interface ListHoodProps {
// narrow?: boolean;
className?: string;
}