feat: i18n refactor and style adjustment

This commit is contained in:
Yidadaa
2023-04-28 00:34:37 +08:00
parent 6c1144f6f4
commit fb32770486
23 changed files with 266 additions and 156 deletions

View File

@@ -18,6 +18,15 @@
cursor: not-allowed;
opacity: 0.5;
}
&.primary {
background-color: var(--primary);
color: white;
path {
fill: white !important;
}
}
}
.shadow {

View File

@@ -5,10 +5,10 @@ import styles from "./button.module.scss";
export function IconButton(props: {
onClick?: () => void;
icon?: JSX.Element;
type?: "primary" | "danger";
text?: string;
bordered?: boolean;
shadow?: boolean;
noDark?: boolean;
className?: string;
title?: string;
disabled?: boolean;
@@ -19,7 +19,7 @@ export function IconButton(props: {
styles["icon-button"] +
` ${props.bordered && styles.border} ${props.shadow && styles.shadow} ${
props.className ?? ""
} clickable`
} clickable ${styles[props.type ?? ""]}`
}
onClick={props.onClick}
title={props.title}
@@ -29,7 +29,8 @@ export function IconButton(props: {
{props.icon && (
<div
className={
styles["icon-button-icon"] + ` ${props.noDark && "no-dark"}`
styles["icon-button-icon"] +
` ${props.type === "primary" && "no-dark"}`
}
>
{props.icon}

View File

@@ -14,6 +14,8 @@ import { useChatStore } from "../store";
import Locale from "../locales";
import { Link, useNavigate } from "react-router-dom";
import { Path } from "../constant";
import { MaskAvatar } from "./mask";
import { Mask } from "../store/mask";
export function ChatItem(props: {
onClick?: () => void;
@@ -25,6 +27,7 @@ export function ChatItem(props: {
id: number;
index: number;
narrow?: boolean;
mask: Mask;
}) {
return (
<Draggable draggableId={`${props.id}`} index={props.index}>
@@ -44,7 +47,7 @@ export function ChatItem(props: {
{props.narrow ? (
<div className={styles["chat-item-narrow"]}>
<div className={styles["chat-item-avatar"] + " no-dark"}>
<BotIcon></BotIcon>
<MaskAvatar mask={props.mask} />
</div>
<div className={styles["chat-item-narrow-count"]}>
{props.count}
@@ -129,6 +132,7 @@ export function ChatList(props: { narrow?: boolean }) {
}
}}
narrow={props.narrow}
mask={item.mask}
/>
))}
{provided.placeholder}

View File

@@ -53,13 +53,16 @@ import { IconButton } from "./button";
import styles from "./home.module.scss";
import chatStyle from "./chat.module.scss";
import { Input, List, ListItem, Modal, Popover, showModal } from "./ui-lib";
import { ListItem, Modal, showModal } from "./ui-lib";
import { useNavigate } from "react-router-dom";
import { Path } from "../constant";
import { ModelConfigList } from "./model-config";
import { Avatar, AvatarPicker } from "./emoji";
import { MaskConfig } from "./mask";
import { DEFAULT_MASK_ID, useMaskStore } from "../store/mask";
import { Avatar } from "./emoji";
import { MaskAvatar, MaskConfig } from "./mask";
import {
DEFAULT_MASK_AVATAR,
DEFAULT_MASK_ID,
useMaskStore,
} from "../store/mask";
const Markdown = dynamic(
async () => memo((await import("./markdown")).Markdown),
@@ -668,10 +671,8 @@ export function Chat() {
<div className={styles["chat-message-avatar"]}>
{message.role === "user" ? (
<Avatar avatar={config.avatar} />
) : session.mask.id === DEFAULT_MASK_ID ? (
<Avatar model={message.model ?? "gpt-3.5-turbo"} />
) : (
<Avatar avatar={session.mask.avatar} />
<MaskAvatar mask={session.mask} />
)}
</div>
{showTyping && (
@@ -778,7 +779,7 @@ export function Chat() {
icon={<SendWhiteIcon />}
text={Locale.Chat.Send}
className={styles["chat-input-send"]}
noDark
type="primary"
onClick={onUserSubmit}
/>
</div>

View File

@@ -2,7 +2,7 @@ import React from "react";
import { IconButton } from "./button";
import GithubIcon from "../icons/github.svg";
import ResetIcon from "../icons/reload.svg";
import { ISSUE_URL, StoreKey } from "../constant";
import { ISSUE_URL } from "../constant";
import Locale from "../locales";
import { downloadAs } from "../utils";
@@ -24,22 +24,15 @@ export class ErrorBoundary extends React.Component<any, IErrorBoundaryState> {
}
clearAndSaveData() {
const snapshot: Record<string, any> = {};
Object.values(StoreKey).forEach((key) => {
snapshot[key] = localStorage.getItem(key);
if (snapshot[key]) {
try {
snapshot[key] = JSON.parse(snapshot[key]);
} catch {}
}
});
try {
downloadAs(JSON.stringify(snapshot), "chatgpt-next-web-snapshot.json");
} catch {}
localStorage.clear();
downloadAs(
JSON.stringify(localStorage),
"chatgpt-next-web-snapshot.json",
);
} finally {
localStorage.clear();
location.reload();
}
}
render() {
@@ -65,7 +58,8 @@ export class ErrorBoundary extends React.Component<any, IErrorBoundaryState> {
icon={<ResetIcon />}
text="Clear All Data"
onClick={() =>
confirm(Locale.Store.ConfirmClearAll) && this.clearAndSaveData()
confirm(Locale.Settings.Actions.ConfirmClearAll) &&
this.clearAndSaveData()
}
bordered
/>

View File

@@ -269,7 +269,7 @@
.chat-item-avatar {
display: flex;
justify-content: center;
opacity: 0.1;
opacity: 0.2;
position: absolute;
transform: scale(4);
}

View File

@@ -52,10 +52,18 @@
animation: slide-in ease 0.4s;
}
.search-bar {
.actions {
margin-top: 5vh;
margin-bottom: 5vh;
animation: slide-in ease 0.45s;
display: flex;
justify-content: center;
.search-bar {
font-size: 12px;
margin-right: 10px;
width: 40vw;
}
}
.masks {

View File

@@ -3,11 +3,14 @@ import { Path, SlotID } from "../constant";
import { IconButton } from "./button";
import { EmojiAvatar } from "./emoji";
import styles from "./new-chat.module.scss";
import LeftIcon from "../icons/left.svg";
import { useNavigate } from "react-router-dom";
import AddIcon from "../icons/lightning.svg";
import { useLocation, useNavigate } from "react-router-dom";
import { createEmptyMask, Mask, useMaskStore } from "../store/mask";
import Locale from "../locales";
import { useChatStore } from "../store";
import { useAppConfig, useChatStore } from "../store";
import { MaskAvatar } from "./mask";
function getIntersectionArea(aRect: DOMRect, bRect: DOMRect) {
@@ -93,10 +96,14 @@ function useMaskGroup(masks: Mask[]) {
export function NewChat() {
const chatStore = useChatStore();
const maskStore = useMaskStore();
const masks = maskStore.getAll();
const groups = useMaskGroup(masks);
const navigate = useNavigate();
const config = useAppConfig();
const { state } = useLocation();
const startChat = (mask?: Mask) => {
chatStore.newSession(mask);
@@ -111,10 +118,19 @@ export function NewChat() {
text={Locale.NewChat.Return}
onClick={() => navigate(Path.Home)}
></IconButton>
<IconButton
text={Locale.NewChat.Skip}
onClick={() => startChat()}
></IconButton>
{!state?.fromHome && (
<IconButton
text={Locale.NewChat.NotShow}
onClick={() => {
if (confirm(Locale.NewChat.ConfirmNoShow)) {
startChat();
config.update(
(config) => (config.dontShowMaskSplashScreen = true),
);
}
}}
></IconButton>
)}
</div>
<div className={styles["mask-cards"]}>
<div className={styles["mask-card"]}>
@@ -131,12 +147,22 @@ export function NewChat() {
<div className={styles["title"]}>{Locale.NewChat.Title}</div>
<div className={styles["sub-title"]}>{Locale.NewChat.SubTitle}</div>
<input
className={styles["search-bar"]}
placeholder={Locale.NewChat.More}
type="text"
onClick={() => navigate(Path.Masks)}
/>
<div className={styles["actions"]}>
<input
className={styles["search-bar"]}
placeholder={Locale.NewChat.More}
type="text"
onClick={() => navigate(Path.Masks)}
/>
<IconButton
text={Locale.NewChat.Skip}
onClick={() => startChat()}
icon={<AddIcon />}
type="primary"
shadow
/>
</div>
<div className={styles["masks"]}>
{groups.map((masks, i) => (

View File

@@ -137,10 +137,7 @@ export function Settings() {
const config = useAppConfig();
const updateConfig = config.update;
const resetConfig = config.reset;
const [clearAllData, clearSessions] = useChatStore((state) => [
state.clearAllData,
state.clearSessions,
]);
const chatStore = useChatStore();
const updateStore = useUpdateStore();
const [checkingUpdate, setCheckingUpdate] = useState(false);
@@ -160,9 +157,9 @@ export function Settings() {
subscription: updateStore.subscription,
};
const [loadingUsage, setLoadingUsage] = useState(false);
function checkUsage() {
function checkUsage(force = false) {
setLoadingUsage(true);
updateStore.updateUsage().finally(() => {
updateStore.updateUsage(force).finally(() => {
setLoadingUsage(false);
});
}
@@ -216,11 +213,8 @@ export function Settings() {
<IconButton
icon={<ClearIcon />}
onClick={() => {
const confirmed = window.confirm(
`${Locale.Settings.Actions.ConfirmClearAll.Confirm}`,
);
if (confirmed) {
clearSessions();
if (confirm(Locale.Settings.Actions.ConfirmClearAll)) {
chatStore.clearAllData();
}
}}
bordered
@@ -231,10 +225,7 @@ export function Settings() {
<IconButton
icon={<ResetIcon />}
onClick={() => {
const confirmed = window.confirm(
`${Locale.Settings.Actions.ConfirmResetAll.Confirm}`,
);
if (confirmed) {
if (confirm(Locale.Settings.Actions.ConfirmResetAll)) {
resetConfig();
}
}}
@@ -370,19 +361,10 @@ export function Settings() {
></InputRange>
</ListItem>
<ListItem title={Locale.Settings.TightBorder}>
<input
type="checkbox"
checked={config.tightBorder}
onChange={(e) =>
updateConfig(
(config) => (config.tightBorder = e.currentTarget.checked),
)
}
></input>
</ListItem>
<ListItem title={Locale.Settings.SendPreviewBubble}>
<ListItem
title={Locale.Settings.SendPreviewBubble.Title}
subTitle={Locale.Settings.SendPreviewBubble.SubTitle}
>
<input
type="checkbox"
checked={config.sendPreviewBubble}
@@ -394,6 +376,23 @@ export function Settings() {
}
></input>
</ListItem>
<ListItem
title={Locale.Settings.Mask.Title}
subTitle={Locale.Settings.Mask.SubTitle}
>
<input
type="checkbox"
checked={!config.dontShowMaskSplashScreen}
onChange={(e) =>
updateConfig(
(config) =>
(config.dontShowMaskSplashScreen =
!e.currentTarget.checked),
)
}
></input>
</ListItem>
</List>
<List>
@@ -448,7 +447,7 @@ export function Settings() {
<IconButton
icon={<ResetIcon></ResetIcon>}
text={Locale.Settings.Usage.Check}
onClick={checkUsage}
onClick={() => checkUsage(true)}
/>
)}
</ListItem>

View File

@@ -87,6 +87,8 @@ export function SideBar(props: { className?: string }) {
const { onDragMouseDown, shouldNarrow } = useDragSideBar();
const navigate = useNavigate();
const config = useAppConfig();
return (
<div
className={`${styles.sidebar} ${props.className} ${
@@ -106,14 +108,14 @@ export function SideBar(props: { className?: string }) {
<div className={styles["sidebar-header-bar"]}>
<IconButton
icon={<MaskIcon />}
text="Mask"
text={shouldNarrow ? undefined : Locale.Mask.Name}
className={styles["sidebar-bar-button"]}
onClick={() => navigate(Path.Masks)}
onClick={() => navigate(Path.NewChat, { state: { fromHome: true } })}
shadow
/>
<IconButton
icon={<PluginIcon />}
text="Plugins"
text={shouldNarrow ? undefined : Locale.Plugin.Name}
className={styles["sidebar-bar-button"]}
onClick={() => showToast(Locale.WIP)}
shadow
@@ -155,7 +157,11 @@ export function SideBar(props: { className?: string }) {
icon={<AddIcon />}
text={shouldNarrow ? undefined : Locale.Home.NewChat}
onClick={() => {
navigate(Path.NewChat);
if (config.dontShowMaskSplashScreen) {
chatStore.newSession();
} else {
navigate(Path.NewChat);
}
}}
shadow
/>