Skip to content

Commit

Permalink
feat: add Multiple send message method
Browse files Browse the repository at this point in the history
  • Loading branch information
Peek-A-Booo committed May 25, 2023
1 parent 272f9d4 commit 59a2686
Show file tree
Hide file tree
Showing 13 changed files with 264 additions and 78 deletions.
3 changes: 2 additions & 1 deletion CHANGE_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

## v0.4.2

> 2023-05-24
> 2023-05-25
### Add

- Add a feature to count conversation fees and allow users to view the current amount of USD or tokens consumed during the conversation.
- Improve Token and session introduction
- Access the Help and Feedback Platform at support.qq.com
- Added the ability to switch between using Enter and Command+Enter (Mac)/Ctrl+Enter (Windows) for inputting information.

### Changed

Expand Down
161 changes: 98 additions & 63 deletions src/components/setting/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from "react";
import clsx from "clsx";
import { cn } from "@/lib";
import { useTranslations } from "next-intl";
import { TbTrashXFilled } from "react-icons/tb";
import { BiExport, BiImport } from "react-icons/bi";
Expand All @@ -10,33 +10,38 @@ import Modal from "@/components/ui/Modal";
import Select from "@/components/ui/Select";
import Button from "@/components/ui/Button";
import Confirm from "@/components/ui/Confirm";
import { LLM } from "@/utils/constant";
import { useChannel, useOpenAI, useSetting } from "@/hooks";
import { LLM, sendMessageTypes } from "@/utils/constant";
import { getPlatform } from "@/lib";
import type { Platform } from "@/lib";
import { useChannel, useOpenAI, useSetting, useConfig } from "@/hooks";
import type { IConfigStoreState } from "@/hooks";
import OpenAI from "./openai";
import Azure from "./azure";

const renderLabel = (item: any) => {
return (
<div className="flex items-center gap-2">
{item.ico}
{item.label}
</div>
);
};

const Setting: React.FC = () => {
const [channel, setChannel] = useChannel();
const [openai, setOpenAI] = useOpenAI();
const [visible, setVisible] = useSetting();
const [config, setConfig] = useConfig();
const { format } = useDateFormat();

const fileRef = React.useRef<any>(null);
const [model, setModel] = React.useState<string>("");
const [plat, setPlat] = React.useState<Platform>("windows");

const t = useTranslations("setting");

const onClose = () => setVisible(false);

const renderLabel = (item: any) => {
return (
<div className="flex items-center gap-2">
{item.ico}
{item.label}
</div>
);
};

const handleResetData = () => {
localStorage.clear();
window.location.reload();
Expand Down Expand Up @@ -86,64 +91,94 @@ const Setting: React.FC = () => {
fileRef.current.value = "";
};

const onChangeSendMessageType = (
value: IConfigStoreState["sendMessageType"]
) => {
setConfig((config) => {
config.sendMessageType = value;
return config;
});
};

React.useEffect(() => {
if (visible && !model) setModel(LLM[0].value);
}, [visible]);

React.useEffect(() => {
setPlat(getPlatform());
}, []);

return (
<Modal
footer={null}
maskClosable={false}
title={t("title")}
open={visible}
onClose={onClose}
>
<div className="px-1">
<Select
className="w-full"
size="large"
options={LLM}
value={model}
renderLabel={renderLabel}
onChange={setModel}
/>
</div>
{model === LLM[0].value && <OpenAI />}
{model === LLM[1].value && <Azure />}
<div
className={clsx(
"flex items-center justify-between py-2 px-1 border-b",
"border-slate-100 dark:border-neutral-500/60"
)}
<>
<Modal
footer={null}
maskClosable={false}
title={t("title")}
open={visible}
onClose={onClose}
>
<div className="text-sm">{t("reset-data")}</div>
<Confirm
title={t("reset-data")}
content={t("reset-data-tip")}
trigger={
<Button type="danger">
<TbTrashXFilled size={18} />
<div className="px-1">
<Select
className="w-full"
size="large"
options={LLM}
value={model}
renderLabel={renderLabel}
onChange={setModel}
/>
</div>
{model === LLM[0].value && <OpenAI />}
{model === LLM[1].value && <Azure />}
<div
className={cn(
"flex items-center justify-between py-2 px-1 border-b",
"border-slate-100 dark:border-neutral-500/60"
)}
>
<div className="text-sm">{t("reset-data")}</div>
<Confirm
title={t("reset-data")}
content={t("reset-data-tip")}
trigger={
<Button type="danger">
<TbTrashXFilled size={18} />
</Button>
}
onOk={handleResetData}
/>
</div>
<div
className={cn(
"flex items-center justify-between py-2 px-1 border-b",
"border-slate-100 dark:border-neutral-500/60"
)}
>
<div className="text-sm">{t("export-import")}</div>
<div className="flex gap-2">
<Button onClick={handleExport}>
<BiExport size={18} />
</Button>
}
onOk={handleResetData}
/>
</div>
<div
className={clsx(
"flex items-center justify-between py-2 px-1 border-b",
"border-slate-100 dark:border-neutral-500/60"
)}
>
<div className="text-sm">{t("export-import")}</div>
<div className="flex gap-2">
<Button onClick={handleExport}>
<BiExport size={18} />
</Button>
<Button onClick={() => fileRef.current?.click()}>
<BiImport size={18} />
</Button>
<Button onClick={() => fileRef.current?.click()}>
<BiImport size={18} />
</Button>
</div>
</div>

<div
className={cn(
"flex items-center justify-between py-2 px-1 border-b",
"border-slate-100 dark:border-neutral-500/60"
)}
>
<div className="text-sm">{t("send-message")}</div>
<Select
className="w-44"
options={sendMessageTypes(plat)}
value={config.sendMessageType}
onChange={onChangeSendMessageType}
/>
</div>
</div>
</Modal>
<input
ref={fileRef}
className="sr-only"
Expand All @@ -152,7 +187,7 @@ const Setting: React.FC = () => {
accept=".json"
onChange={(e) => handleImport(e.target.files)}
/>
</Modal>
</>
);
};

Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/Select/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type OptionsChildren = {
};

type Options = {
label: string;
label: React.ReactNode;
value: any;
children?: OptionsChildren[];
};
Expand Down
63 changes: 57 additions & 6 deletions src/components/ui/Textarea/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import * as React from "react";
import clsx from "clsx";
import { useTranslations } from "next-intl";
import { AiOutlineSend, AiOutlineLoading } from "react-icons/ai";
import { useChat } from "@/hooks";
import { useChat, useConfig } from "@/hooks";
import { isMobile, getPlatform } from "@/lib";

export interface TextAreaProps {
className?: string | undefined;
Expand All @@ -23,15 +24,15 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(

const t = useTranslations("chat");
const { loadingResponseFinish } = useChat();
const [config] = useConfig();

// data
const [isFocus, setIsFocus] = React.useState<boolean>(false);
const [placeholder, setPlaceholder] = React.useState<string>("");

// ref
const inputRef = React.useRef<any>(null);

const placeholder = t("type-message");

const onInput = () => {
inputRef.current.style.height = "auto";
inputRef.current.style.height = inputRef.current.scrollHeight + "px";
Expand All @@ -43,9 +44,44 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
};

const onKeyDown = (e: any) => {
if (e.keyCode === 13 && !e.shiftKey) {
e.preventDefault();
onSubmit();
const isMobileDevice = isMobile();
const { sendMessageType } = config;
const platform = getPlatform();

if (isMobileDevice) {
if (sendMessageType === "Enter") {
if (e.keyCode === 13) {
e.preventDefault();
onSubmit();
}
}
} else {
// We need to differentiate between Windows and Mac.
if (sendMessageType === "Enter") {
if (platform === "mac") {
if (e.keyCode === 13 && !e.shiftKey) {
e.preventDefault();
onSubmit();
}
} else if (platform === "windows") {
if ((e.keyCode === 13 || e.keyCode === 10) && !e.shiftKey) {
e.preventDefault();
onSubmit();
}
}
} else if (sendMessageType === "CommandEnter") {
if (platform === "mac") {
if (e.keyCode === 13 && e.metaKey) {
e.preventDefault();
onSubmit();
}
} else if (platform === "windows") {
if ((e.keyCode === 13 || e.keyCode === 10) && e.ctrlKey) {
e.preventDefault();
onSubmit();
}
}
}
}
};

Expand All @@ -62,6 +98,21 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
},
}));

React.useEffect(() => {
const isMobileDevice = isMobile();
const platform = getPlatform();
if (isMobileDevice) return setPlaceholder(t("type-message"));
if (config.sendMessageType === "CommandEnter") {
if (platform === "windows") {
setPlaceholder(t("type-message-ctrlEnter"));
} else if (platform === "mac") {
setPlaceholder(t("type-message-commandEnter"));
}
} else if (config.sendMessageType === "Enter") {
setPlaceholder(t("type-message-enter"));
}
}, [config.sendMessageType]);

return (
<div
className={clsx(
Expand Down
1 change: 1 addition & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from "./useChannel";
export * from "./useChat";
export * from "./useConfig";
export * from "./useMobileMenu";
export * from "./useOpenAI";
export * from "./useRevoke";
Expand Down
53 changes: 53 additions & 0 deletions src/hooks/useConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import * as React from "react";
import { create } from "zustand";

export interface IConfigStoreState {
sendMessageType: "Enter" | "CommandEnter";
}

interface IConfigStoreAction {
update: (args: SaveConfig) => void;
}

type SaveConfig =
| IConfigStoreState
| ((prev: IConfigStoreState) => IConfigStoreState);

type UseConfigReturn = [IConfigStoreState, (args: SaveConfig) => void];

const useStore = create<IConfigStoreState & IConfigStoreAction>((set) => ({
sendMessageType: "Enter",

update: (args: SaveConfig) => {
if (typeof args === "function") {
set((state) => {
const newState = JSON.parse(JSON.stringify(state));
const { sendMessageType } = args(newState);
localStorage.setItem("sendMessageType", sendMessageType);
return newState;
});
} else {
const { sendMessageType } = args;
localStorage.setItem("sendMessageType", sendMessageType);
set(() => ({ sendMessageType }));
}
},
}));

export const useConfig = (): UseConfigReturn => {
const { sendMessageType } = useStore();

const update = useStore((state) => state.update);

React.useEffect(() => {
const localSendMessageType =
localStorage.getItem("sendMessageType") || "Enter";

update({
sendMessageType:
localSendMessageType as IConfigStoreState["sendMessageType"],
});
}, []);

return [{ sendMessageType }, update];
};
Loading

1 comment on commit 59a2686

@vercel
Copy link

@vercel vercel bot commented on 59a2686 May 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.