mirror of
https://github.com/usememos/memos.git
synced 2025-03-31 11:00:21 +02:00
feat: change main language to english
This commit is contained in:
parent
321183db52
commit
226e9c156a
@ -13,7 +13,7 @@ const AboutSiteDialog: React.FC<Props> = ({ destroy }: Props) => {
|
|||||||
<>
|
<>
|
||||||
<div className="dialog-header-container">
|
<div className="dialog-header-container">
|
||||||
<p className="title-text">
|
<p className="title-text">
|
||||||
<span className="icon-text">🤠</span>关于 <b>Memos</b>
|
<span className="icon-text">🤠</span>About <b>Memos</b>
|
||||||
</p>
|
</p>
|
||||||
<button className="btn close-btn" onClick={handleCloseBtnClick}>
|
<button className="btn close-btn" onClick={handleCloseBtnClick}>
|
||||||
<img className="icon-img" src="/icons/close.svg" />
|
<img className="icon-img" src="/icons/close.svg" />
|
||||||
@ -21,9 +21,9 @@ const AboutSiteDialog: React.FC<Props> = ({ destroy }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="dialog-content-container">
|
<div className="dialog-content-container">
|
||||||
<p>
|
<p>
|
||||||
把玩 <a href="https://flomoapp.com">flomo</a> 后有感而作的开源项目
|
Memos is an open source, self-hosted alternative to <a href="https://flomoapp.com">flomo</a>.
|
||||||
</p>
|
</p>
|
||||||
<p>特点:精美且细节的视觉样式、体验优良的交互逻辑</p>
|
<p>Built with `Golang` and `React`.</p>
|
||||||
<br />
|
<br />
|
||||||
<p>
|
<p>
|
||||||
🏗 This project is working in progress, <br /> and very pleasure to welcome your{" "}
|
🏗 This project is working in progress, <br /> and very pleasure to welcome your{" "}
|
||||||
|
@ -44,19 +44,19 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
|||||||
|
|
||||||
const handleSaveBtnClick = async () => {
|
const handleSaveBtnClick = async () => {
|
||||||
if (oldPassword === "" || newPassword === "" || newPasswordAgain === "") {
|
if (oldPassword === "" || newPassword === "" || newPasswordAgain === "") {
|
||||||
toastHelper.error("密码不能为空");
|
toastHelper.error("Please fill in all fields.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newPassword !== newPasswordAgain) {
|
if (newPassword !== newPasswordAgain) {
|
||||||
toastHelper.error("新密码两次输入不一致");
|
toastHelper.error("New passwords do not match.");
|
||||||
setNewPasswordAgain("");
|
setNewPasswordAgain("");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const passwordValidResult = validate(newPassword, validateConfig);
|
const passwordValidResult = validate(newPassword, validateConfig);
|
||||||
if (!passwordValidResult.result) {
|
if (!passwordValidResult.result) {
|
||||||
toastHelper.error("密码 " + passwordValidResult.reason);
|
toastHelper.error("Password " + passwordValidResult.reason);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,13 +64,13 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
|||||||
const isValid = await userService.checkPasswordValid(oldPassword);
|
const isValid = await userService.checkPasswordValid(oldPassword);
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
toastHelper.error("旧密码不匹配");
|
toastHelper.error("Old password is invalid.");
|
||||||
setOldPassword("");
|
setOldPassword("");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await userService.updatePassword(newPassword);
|
await userService.updatePassword(newPassword);
|
||||||
toastHelper.info("密码修改成功!");
|
toastHelper.info("Password changed.");
|
||||||
handleCloseBtnClick();
|
handleCloseBtnClick();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toastHelper.error(error);
|
toastHelper.error(error);
|
||||||
@ -80,30 +80,30 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="dialog-header-container">
|
<div className="dialog-header-container">
|
||||||
<p className="title-text">修改密码</p>
|
<p className="title-text">Change Password</p>
|
||||||
<button className="btn close-btn" onClick={handleCloseBtnClick}>
|
<button className="btn close-btn" onClick={handleCloseBtnClick}>
|
||||||
<img className="icon-img" src="/icons/close.svg" />
|
<img className="icon-img" src="/icons/close.svg" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="dialog-content-container">
|
<div className="dialog-content-container">
|
||||||
<label className="form-label input-form-label">
|
<label className="form-label input-form-label">
|
||||||
<span className={"normal-text " + (oldPassword === "" ? "" : "not-null")}>旧密码</span>
|
<span className={"normal-text " + (oldPassword === "" ? "" : "not-null")}>Old password</span>
|
||||||
<input type="password" value={oldPassword} onChange={handleOldPasswordChanged} />
|
<input type="password" value={oldPassword} onChange={handleOldPasswordChanged} />
|
||||||
</label>
|
</label>
|
||||||
<label className="form-label input-form-label">
|
<label className="form-label input-form-label">
|
||||||
<span className={"normal-text " + (newPassword === "" ? "" : "not-null")}>新密码</span>
|
<span className={"normal-text " + (newPassword === "" ? "" : "not-null")}>New passworld</span>
|
||||||
<input type="password" value={newPassword} onChange={handleNewPasswordChanged} />
|
<input type="password" value={newPassword} onChange={handleNewPasswordChanged} />
|
||||||
</label>
|
</label>
|
||||||
<label className="form-label input-form-label">
|
<label className="form-label input-form-label">
|
||||||
<span className={"normal-text " + (newPasswordAgain === "" ? "" : "not-null")}>再次输入新密码</span>
|
<span className={"normal-text " + (newPasswordAgain === "" ? "" : "not-null")}>New password again</span>
|
||||||
<input type="password" value={newPasswordAgain} onChange={handleNewPasswordAgainChanged} />
|
<input type="password" value={newPasswordAgain} onChange={handleNewPasswordAgainChanged} />
|
||||||
</label>
|
</label>
|
||||||
<div className="btns-container">
|
<div className="btns-container">
|
||||||
<span className="btn cancel-btn" onClick={handleCloseBtnClick}>
|
<span className="btn cancel-btn" onClick={handleCloseBtnClick}>
|
||||||
取消
|
Cancel
|
||||||
</span>
|
</span>
|
||||||
<span className="btn confirm-btn" onClick={handleSaveBtnClick}>
|
<span className="btn confirm-btn" onClick={handleSaveBtnClick}>
|
||||||
保存
|
Save
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,29 +27,31 @@ const ConfirmResetOpenIdDialog: React.FC<Props> = ({ destroy }: Props) => {
|
|||||||
try {
|
try {
|
||||||
await userService.resetOpenId();
|
await userService.resetOpenId();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toastHelper.error("请求重置 Open API 失败");
|
toastHelper.error("Request reset open API failed.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
toastHelper.success("重置成功!");
|
toastHelper.success("Reset open API succeeded.");
|
||||||
handleCloseBtnClick();
|
handleCloseBtnClick();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="dialog-header-container">
|
<div className="dialog-header-container">
|
||||||
<p className="title-text">重置 Open API</p>
|
<p className="title-text">Reset Open API</p>
|
||||||
<button className="btn close-btn" onClick={handleCloseBtnClick}>
|
<button className="btn close-btn" onClick={handleCloseBtnClick}>
|
||||||
<img className="icon-img" src="/icons/close.svg" />
|
<img className="icon-img" src="/icons/close.svg" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="dialog-content-container">
|
<div className="dialog-content-container">
|
||||||
<p className="warn-text">⚠️ 现有 API 将失效,并生成新的 API,确定要重置吗?</p>
|
<p className="warn-text">
|
||||||
|
⚠️ The existing API will be invalidated and a new one will be generated, are you sure you want to reset?
|
||||||
|
</p>
|
||||||
<div className="btns-container">
|
<div className="btns-container">
|
||||||
<span className="btn cancel-btn" onClick={handleCloseBtnClick}>
|
<span className="btn cancel-btn" onClick={handleCloseBtnClick}>
|
||||||
取消
|
Cancel
|
||||||
</span>
|
</span>
|
||||||
<span className={`btn confirm-btn ${resetBtnClickLoadingState.isLoading ? "loading" : ""}`} onClick={handleConfirmBtnClick}>
|
<span className={`btn confirm-btn ${resetBtnClickLoadingState.isLoading ? "loading" : ""}`} onClick={handleConfirmBtnClick}>
|
||||||
确定重置!
|
Reset!
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,7 +40,7 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
const handleSaveBtnClick = async () => {
|
const handleSaveBtnClick = async () => {
|
||||||
if (!title) {
|
if (!title) {
|
||||||
toastHelper.error("标题不能为空!");
|
toastHelper.error("Title is required");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
|||||||
if (filters.length > 0) {
|
if (filters.length > 0) {
|
||||||
const lastFilter = filters[filters.length - 1];
|
const lastFilter = filters[filters.length - 1];
|
||||||
if (lastFilter.value.value === "") {
|
if (lastFilter.value.value === "") {
|
||||||
toastHelper.info("先完善上一个过滤器吧");
|
toastHelper.info("Please fill in previous filter value");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
|||||||
<div className="dialog-header-container">
|
<div className="dialog-header-container">
|
||||||
<p className="title-text">
|
<p className="title-text">
|
||||||
<span className="icon-text">🔖</span>
|
<span className="icon-text">🔖</span>
|
||||||
{shortcutId ? "编辑检索" : "创建检索"}
|
{shortcutId ? "Edit Shortcut" : "Create Shortcut"}
|
||||||
</p>
|
</p>
|
||||||
<button className="btn close-btn" onClick={destroy}>
|
<button className="btn close-btn" onClick={destroy}>
|
||||||
<img className="icon-img" src="/icons/close.svg" />
|
<img className="icon-img" src="/icons/close.svg" />
|
||||||
@ -98,11 +98,11 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="dialog-content-container">
|
<div className="dialog-content-container">
|
||||||
<div className="form-item-container input-form-container">
|
<div className="form-item-container input-form-container">
|
||||||
<span className="normal-text">标题</span>
|
<span className="normal-text">Title</span>
|
||||||
<input className="title-input" type="text" value={title} onChange={handleTitleInputChange} />
|
<input className="title-input" type="text" value={title} onChange={handleTitleInputChange} />
|
||||||
</div>
|
</div>
|
||||||
<div className="form-item-container filter-form-container">
|
<div className="form-item-container filter-form-container">
|
||||||
<span className="normal-text">过滤器</span>
|
<span className="normal-text">Filter</span>
|
||||||
<div className="filters-wrapper">
|
<div className="filters-wrapper">
|
||||||
{filters.map((f, index) => {
|
{filters.map((f, index) => {
|
||||||
return (
|
return (
|
||||||
@ -116,7 +116,7 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<div className="create-filter-btn" onClick={handleAddFilterBenClick}>
|
<div className="create-filter-btn" onClick={handleAddFilterBenClick}>
|
||||||
添加筛选条件
|
New Filter
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -125,10 +125,10 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
|||||||
<div></div>
|
<div></div>
|
||||||
<div className="btns-container">
|
<div className="btns-container">
|
||||||
<span className={`tip-text ${filters.length === 0 && "hidden"}`}>
|
<span className={`tip-text ${filters.length === 0 && "hidden"}`}>
|
||||||
符合条件的 Memo 有 <strong>{shownMemoLength}</strong> 条
|
<strong>{shownMemoLength}</strong> eligible memo
|
||||||
</span>
|
</span>
|
||||||
<button className={`btn save-btn ${requestState.isLoading ? "requesting" : ""}`} onClick={handleSaveBtnClick}>
|
<button className={`btn save-btn ${requestState.isLoading ? "requesting" : ""}`} onClick={handleSaveBtnClick}>
|
||||||
保存
|
Save
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -106,11 +106,11 @@ const DailyMemoDiaryDialog: React.FC<Props> = (props: Props) => {
|
|||||||
/>
|
/>
|
||||||
{loadingState.isLoading ? (
|
{loadingState.isLoading ? (
|
||||||
<div className="tip-container">
|
<div className="tip-container">
|
||||||
<p className="tip-text">努力加载中...</p>
|
<p className="tip-text">Loading...</p>
|
||||||
</div>
|
</div>
|
||||||
) : memos.length === 0 ? (
|
) : memos.length === 0 ? (
|
||||||
<div className="tip-container">
|
<div className="tip-container">
|
||||||
<p className="tip-text">空空如也</p>
|
<p className="tip-text">Oops, there is nothing.</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="dailymemos-wrapper">
|
<div className="dailymemos-wrapper">
|
||||||
|
@ -40,7 +40,7 @@ const DeletedMemo: React.FC<Props> = (props: Props) => {
|
|||||||
try {
|
try {
|
||||||
await memoService.restoreMemoById(memo.id);
|
await memoService.restoreMemoById(memo.id);
|
||||||
handleDeletedMemoAction(memo.id);
|
handleDeletedMemoAction(memo.id);
|
||||||
toastHelper.info("恢复成功");
|
toastHelper.info("Restored successfully");
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toastHelper.error(error.message);
|
toastHelper.error(error.message);
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ const DeletedMemo: React.FC<Props> = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div className={`memo-wrapper ${"memos-" + memo.id}`} onMouseLeave={handleMouseLeaveMemoWrapper}>
|
<div className={`memo-wrapper ${"memos-" + memo.id}`} onMouseLeave={handleMouseLeaveMemoWrapper}>
|
||||||
<div className="memo-top-wrapper">
|
<div className="memo-top-wrapper">
|
||||||
<span className="time-text">删除于 {memo.deletedAtStr}</span>
|
<span className="time-text">Deleted at {memo.deletedAtStr}</span>
|
||||||
<div className="btns-container">
|
<div className="btns-container">
|
||||||
<span className="btn more-action-btn">
|
<span className="btn more-action-btn">
|
||||||
<img className="icon-img" src="/icons/more.svg" />
|
<img className="icon-img" src="/icons/more.svg" />
|
||||||
@ -63,10 +63,10 @@ const DeletedMemo: React.FC<Props> = (props: Props) => {
|
|||||||
<div className="more-action-btns-wrapper">
|
<div className="more-action-btns-wrapper">
|
||||||
<div className="more-action-btns-container">
|
<div className="more-action-btns-container">
|
||||||
<span className="btn restore-btn" onClick={handleRestoreMemoClick}>
|
<span className="btn restore-btn" onClick={handleRestoreMemoClick}>
|
||||||
恢复
|
Restore
|
||||||
</span>
|
</span>
|
||||||
<span className={`btn delete-btn ${showConfirmDeleteBtn ? "final-confirm" : ""}`} onClick={handleDeleteMemoClick}>
|
<span className={`btn delete-btn ${showConfirmDeleteBtn ? "final-confirm" : ""}`} onClick={handleDeleteMemoClick}>
|
||||||
{showConfirmDeleteBtn ? "确定删除!" : "完全删除"}
|
{showConfirmDeleteBtn ? "Delete!" : "Delete"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -176,12 +176,12 @@ const Editor = forwardRef((props: EditorProps, ref: React.ForwardedRef<EditorRef
|
|||||||
<div className="btns-container">
|
<div className="btns-container">
|
||||||
<Only when={showCancelBtn}>
|
<Only when={showCancelBtn}>
|
||||||
<button className="action-btn cancel-btn" onClick={handleCommonCancelBtnClick}>
|
<button className="action-btn cancel-btn" onClick={handleCommonCancelBtnClick}>
|
||||||
撤销修改
|
Cancel editting
|
||||||
</button>
|
</button>
|
||||||
</Only>
|
</Only>
|
||||||
<Only when={showConfirmBtn}>
|
<Only when={showConfirmBtn}>
|
||||||
<button className="action-btn confirm-btn" disabled={!editorRef.current?.value} onClick={handleCommonConfirmBtnClick}>
|
<button className="action-btn confirm-btn" disabled={!editorRef.current?.value} onClick={handleCommonConfirmBtnClick}>
|
||||||
记下<span className="icon-text">✍️</span>
|
Save <span className="icon-text">✍️</span>
|
||||||
</button>
|
</button>
|
||||||
</Only>
|
</Only>
|
||||||
</div>
|
</div>
|
||||||
|
@ -93,19 +93,19 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
<div className="more-action-btns-wrapper">
|
<div className="more-action-btns-wrapper">
|
||||||
<div className="more-action-btns-container">
|
<div className="more-action-btns-container">
|
||||||
<span className="btn" onClick={handleShowMemoStoryDialog}>
|
<span className="btn" onClick={handleShowMemoStoryDialog}>
|
||||||
查看详情
|
View Story
|
||||||
</span>
|
</span>
|
||||||
<span className="btn" onClick={handleMarkMemoClick}>
|
<span className="btn" onClick={handleMarkMemoClick}>
|
||||||
Mark
|
Mark
|
||||||
</span>
|
</span>
|
||||||
<span className="btn" onClick={handleGenMemoImageBtnClick}>
|
<span className="btn" onClick={handleGenMemoImageBtnClick}>
|
||||||
分享
|
Share
|
||||||
</span>
|
</span>
|
||||||
<span className="btn" onClick={handleEditMemoClick}>
|
<span className="btn" onClick={handleEditMemoClick}>
|
||||||
编辑
|
Edit
|
||||||
</span>
|
</span>
|
||||||
<span className={`btn delete-btn ${showConfirmDeleteBtn ? "final-confirm" : ""}`} onClick={handleDeleteMemoClick}>
|
<span className={`btn delete-btn ${showConfirmDeleteBtn ? "final-confirm" : ""}`} onClick={handleDeleteMemoClick}>
|
||||||
{showConfirmDeleteBtn ? "确定删除!" : "删除"}
|
{showConfirmDeleteBtn ? "Delete!" : "Delete"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -151,7 +151,7 @@ export function formatMemoContent(content: string) {
|
|||||||
.replace(LINK_REG, "<a class='link' target='_blank' rel='noreferrer' href='$1'>$1</a>")
|
.replace(LINK_REG, "<a class='link' target='_blank' rel='noreferrer' href='$1'>$1</a>")
|
||||||
.replace(MEMO_LINK_REG, "<span class='memo-link-text' data-value='$2'>$1</span>");
|
.replace(MEMO_LINK_REG, "<span class='memo-link-text' data-value='$2'>$1</span>");
|
||||||
|
|
||||||
// 中英文之间加空格
|
// Add space in english and chinese
|
||||||
if (shouldSplitMemoWord) {
|
if (shouldSplitMemoWord) {
|
||||||
content = content
|
content = content
|
||||||
.replace(/([\u4e00-\u9fa5])([A-Za-z0-9?.,;[\]]+)/g, "$1 $2")
|
.replace(/([\u4e00-\u9fa5])([A-Za-z0-9?.,;[\]]+)/g, "$1 $2")
|
||||||
|
@ -148,7 +148,7 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
{linkMemos.length > 0 ? (
|
{linkMemos.length > 0 ? (
|
||||||
<div className="linked-memos-wrapper">
|
<div className="linked-memos-wrapper">
|
||||||
<p className="normal-text">关联了 {linkMemos.length} 个 MEMO</p>
|
<p className="normal-text">{linkMemos.length} related MEMO</p>
|
||||||
{linkMemos.map((m) => {
|
{linkMemos.map((m) => {
|
||||||
const rawtext = parseHtmlToRawText(formatMemoContent(m.content)).replaceAll("\n", " ");
|
const rawtext = parseHtmlToRawText(formatMemoContent(m.content)).replaceAll("\n", " ");
|
||||||
return (
|
return (
|
||||||
@ -162,7 +162,7 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
|||||||
) : null}
|
) : null}
|
||||||
{linkedMemos.length > 0 ? (
|
{linkedMemos.length > 0 ? (
|
||||||
<div className="linked-memos-wrapper">
|
<div className="linked-memos-wrapper">
|
||||||
<p className="normal-text">{linkedMemos.length} 个链接至此的 MEMO</p>
|
<p className="normal-text">{linkedMemos.length} linked MEMO</p>
|
||||||
{linkedMemos.map((m) => {
|
{linkedMemos.map((m) => {
|
||||||
const rawtext = parseHtmlToRawText(formatMemoContent(m.content)).replaceAll("\n", " ");
|
const rawtext = parseHtmlToRawText(formatMemoContent(m.content)).replaceAll("\n", " ");
|
||||||
return (
|
return (
|
||||||
|
@ -140,7 +140,7 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
|
|
||||||
const handleSaveBtnClick = useCallback(async (content: string) => {
|
const handleSaveBtnClick = useCallback(async (content: string) => {
|
||||||
if (content === "") {
|
if (content === "") {
|
||||||
toastHelper.error("内容不能为空呀");
|
toastHelper.error("Content can't be empty");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,7 +270,7 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
() => ({
|
() => ({
|
||||||
className: "memo-editor",
|
className: "memo-editor",
|
||||||
initialContent: getEditorContentCache(),
|
initialContent: getEditorContentCache(),
|
||||||
placeholder: "现在的想法是...",
|
placeholder: "Any thoughts...",
|
||||||
showConfirmBtn: true,
|
showConfirmBtn: true,
|
||||||
showCancelBtn: showEditStatus,
|
showCancelBtn: showEditStatus,
|
||||||
onConfirmBtnClick: handleSaveBtnClick,
|
onConfirmBtnClick: handleSaveBtnClick,
|
||||||
@ -282,7 +282,7 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"memo-editor-wrapper " + (showEditStatus ? "edit-ing" : "")}>
|
<div className={"memo-editor-wrapper " + (showEditStatus ? "edit-ing" : "")}>
|
||||||
<p className={"tip-text " + (showEditStatus ? "" : "hidden")}>正在修改中...</p>
|
<p className={"tip-text " + (showEditStatus ? "" : "hidden")}>Editting...</p>
|
||||||
<Editor
|
<Editor
|
||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
{...editorConfig}
|
{...editorConfig}
|
||||||
|
@ -18,7 +18,7 @@ const MemoFilter: React.FC<FilterProps> = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`filter-query-container ${showFilter ? "" : "hidden"}`}>
|
<div className={`filter-query-container ${showFilter ? "" : "hidden"}`}>
|
||||||
<span className="tip-text">筛选:</span>
|
<span className="tip-text">Filter:</span>
|
||||||
<div
|
<div
|
||||||
className={"filter-item-container " + (queryFilter ? "" : "hidden")}
|
className={"filter-item-container " + (queryFilter ? "" : "hidden")}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -83,7 +83,7 @@ const MemoList: React.FC<Props> = () => {
|
|||||||
setFetchStatus(false);
|
setFetchStatus(false);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toastHelper.error("😭 请求数据失败了");
|
toastHelper.error("😭 Refresh failed, please try again later.");
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -111,7 +111,13 @@ const MemoList: React.FC<Props> = () => {
|
|||||||
))}
|
))}
|
||||||
<div className="status-text-container">
|
<div className="status-text-container">
|
||||||
<p className="status-text">
|
<p className="status-text">
|
||||||
{isFetching ? "努力请求数据中..." : shownMemos.length === 0 ? "空空如也" : showMemoFilter ? "" : "所有数据加载完啦 🎉"}
|
{isFetching
|
||||||
|
? "Fetching data..."
|
||||||
|
: shownMemos.length === 0
|
||||||
|
? "Oops, there is nothing"
|
||||||
|
: showMemoFilter
|
||||||
|
? ""
|
||||||
|
: "Fetching completed 🎉"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,16 +49,16 @@ const MenuBtnsPopup: React.FC<Props> = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div className={`menu-btns-popup ${shownStatus ? "" : "hidden"}`} ref={popupElRef}>
|
<div className={`menu-btns-popup ${shownStatus ? "" : "hidden"}`} ref={popupElRef}>
|
||||||
<button className="btn action-btn" onClick={handleMyAccountBtnClick}>
|
<button className="btn action-btn" onClick={handleMyAccountBtnClick}>
|
||||||
<span className="icon">👤</span> 账号与设置
|
<span className="icon">👤</span> Settings
|
||||||
</button>
|
</button>
|
||||||
<button className="btn action-btn" onClick={handleMemosTrashBtnClick}>
|
<button className="btn action-btn" onClick={handleMemosTrashBtnClick}>
|
||||||
<span className="icon">🗑️</span> 回收站
|
<span className="icon">🗑️</span> Recycle Bin
|
||||||
</button>
|
</button>
|
||||||
<button className="btn action-btn" onClick={handleAboutBtnClick}>
|
<button className="btn action-btn" onClick={handleAboutBtnClick}>
|
||||||
<span className="icon">🤠</span> 关于
|
<span className="icon">🤠</span> About
|
||||||
</button>
|
</button>
|
||||||
<button className="btn action-btn" onClick={handleSignOutBtnClick}>
|
<button className="btn action-btn" onClick={handleSignOutBtnClick}>
|
||||||
<span className="icon">👋</span> 退出
|
<span className="icon">👋</span> Sign out
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -30,7 +30,7 @@ const MyAccountSection: React.FC<Props> = () => {
|
|||||||
|
|
||||||
const handleConfirmEditUsernameBtnClick = async () => {
|
const handleConfirmEditUsernameBtnClick = async () => {
|
||||||
if (user.name === "guest") {
|
if (user.name === "guest") {
|
||||||
toastHelper.info("🈲 不要修改我的用户名");
|
toastHelper.info("Do not change my username");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ const MyAccountSection: React.FC<Props> = () => {
|
|||||||
|
|
||||||
const usernameValidResult = validate(username, validateConfig);
|
const usernameValidResult = validate(username, validateConfig);
|
||||||
if (!usernameValidResult.result) {
|
if (!usernameValidResult.result) {
|
||||||
toastHelper.error("用户名 " + usernameValidResult.reason);
|
toastHelper.error("Username " + usernameValidResult.reason);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,13 +48,13 @@ const MyAccountSection: React.FC<Props> = () => {
|
|||||||
const isUsable = await userService.checkUsernameUsable(username);
|
const isUsable = await userService.checkUsernameUsable(username);
|
||||||
|
|
||||||
if (!isUsable) {
|
if (!isUsable) {
|
||||||
toastHelper.error("用户名无法使用");
|
toastHelper.error("Username is not available");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await userService.updateUsername(username);
|
await userService.updateUsername(username);
|
||||||
await userService.doSignIn();
|
await userService.doSignIn();
|
||||||
toastHelper.info("修改成功~");
|
toastHelper.info("Username changed");
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toastHelper.error(error.message);
|
toastHelper.error(error.message);
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ const MyAccountSection: React.FC<Props> = () => {
|
|||||||
|
|
||||||
const handleChangePasswordBtnClick = () => {
|
const handleChangePasswordBtnClick = () => {
|
||||||
if (user.name === "guest") {
|
if (user.name === "guest") {
|
||||||
toastHelper.info("🈲 不要修改我的密码");
|
toastHelper.info("Do not change my password");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,21 +81,21 @@ const MyAccountSection: React.FC<Props> = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="section-container account-section-container">
|
<div className="section-container account-section-container">
|
||||||
<p className="title-text">基本信息</p>
|
<p className="title-text">Account Information</p>
|
||||||
<label className="form-label input-form-label">
|
<label className="form-label input-form-label">
|
||||||
<span className="normal-text">ID:</span>
|
<span className="normal-text">ID:</span>
|
||||||
<span className="normal-text">{user.id}</span>
|
<span className="normal-text">{user.id}</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="form-label input-form-label">
|
<label className="form-label input-form-label">
|
||||||
<span className="normal-text">创建时间:</span>
|
<span className="normal-text">Created at:</span>
|
||||||
<span className="normal-text">{utils.getDateString(user.createdAt)}</span>
|
<span className="normal-text">{utils.getDateString(user.createdAt)}</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="form-label input-form-label username-label">
|
<label className="form-label input-form-label username-label">
|
||||||
<span className="normal-text">账号:</span>
|
<span className="normal-text">Username:</span>
|
||||||
<input type="text" value={username} onChange={handleUsernameChanged} />
|
<input type="text" value={username} onChange={handleUsernameChanged} />
|
||||||
<div className={`btns-container ${username === user.name ? "hidden" : ""}`} onClick={handlePreventDefault}>
|
<div className={`btns-container ${username === user.name ? "hidden" : ""}`} onClick={handlePreventDefault}>
|
||||||
<span className="btn confirm-btn" onClick={handleConfirmEditUsernameBtnClick}>
|
<span className="btn confirm-btn" onClick={handleConfirmEditUsernameBtnClick}>
|
||||||
保存
|
Save
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className="btn cancel-btn"
|
className="btn cancel-btn"
|
||||||
@ -103,25 +103,25 @@ const MyAccountSection: React.FC<Props> = () => {
|
|||||||
setUsername(user.name);
|
setUsername(user.name);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
撤销
|
Cancel
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label className="form-label password-label">
|
<label className="form-label password-label">
|
||||||
<span className="normal-text">密码:</span>
|
<span className="normal-text">Password:</span>
|
||||||
<span className="btn" onClick={handleChangePasswordBtnClick}>
|
<span className="btn" onClick={handleChangePasswordBtnClick}>
|
||||||
修改密码
|
Change It
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="section-container openapi-section-container">
|
<div className="section-container openapi-section-container">
|
||||||
<p className="title-text">Open API(实验性功能)</p>
|
<p className="title-text">Open API (Experimental feature)</p>
|
||||||
<p className="value-text">{openAPIRoute}</p>
|
<p className="value-text">{openAPIRoute}</p>
|
||||||
<span className="reset-btn" onClick={handleResetOpenIdBtnClick}>
|
<span className="reset-btn" onClick={handleResetOpenIdBtnClick}>
|
||||||
重置 API
|
Reset API
|
||||||
</span>
|
</span>
|
||||||
<div className="usage-guide-container">
|
<div className="usage-guide-container">
|
||||||
<p className="title-text">使用方法:</p>
|
<p className="title-text">Usage guide:</p>
|
||||||
<pre>{`POST ${openAPIRoute}\nContent-type: application/json\n{\n "content": "Hello, #memos ${window.location.origin}"\n}`}</pre>
|
<pre>{`POST ${openAPIRoute}\nContent-type: application/json\n{\n "content": "Hello, #memos ${window.location.origin}"\n}`}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,7 +11,7 @@ const PreferencesSection: React.FC<Props> = () => {
|
|||||||
const { globalState } = useContext(appContext);
|
const { globalState } = useContext(appContext);
|
||||||
const { useTinyUndoHistoryCache, shouldHideImageUrl, shouldSplitMemoWord, shouldUseMarkdownParser } = globalState;
|
const { useTinyUndoHistoryCache, shouldHideImageUrl, shouldSplitMemoWord, shouldUseMarkdownParser } = globalState;
|
||||||
|
|
||||||
const demoMemoContent = "👋 你好呀~欢迎使用memos!\n* ✨ **开源项目**;\n* 😋 精美且细节的视觉样式;\n* 📑 体验优良的交互逻辑;";
|
const demoMemoContent = "👋 Hiya, welcome to memos!\n* ✨ **Open source project**;\n* 😋 What do you think;\n* 📑 Tell me something plz;";
|
||||||
|
|
||||||
const handleOpenTinyUndoChanged = () => {
|
const handleOpenTinyUndoChanged = () => {
|
||||||
globalStateService.setAppSetting({
|
globalStateService.setAppSetting({
|
||||||
@ -65,29 +65,29 @@ const PreferencesSection: React.FC<Props> = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="section-container preferences-section-container">
|
<div className="section-container preferences-section-container">
|
||||||
<p className="title-text">Memo 显示相关</p>
|
<p className="title-text">Memo Display</p>
|
||||||
<div
|
<div
|
||||||
className="demo-content-container memo-content-text"
|
className="demo-content-container memo-content-text"
|
||||||
dangerouslySetInnerHTML={{ __html: formatMemoContent(demoMemoContent) }}
|
dangerouslySetInnerHTML={{ __html: formatMemoContent(demoMemoContent) }}
|
||||||
></div>
|
></div>
|
||||||
<label className="form-label checkbox-form-label" onClick={handleSplitWordsValueChanged}>
|
<label className="form-label checkbox-form-label hidden" onClick={handleSplitWordsValueChanged}>
|
||||||
<span className="normal-text">中英文内容自动间隔</span>
|
<span className="normal-text">Auto-space in English and Chinese</span>
|
||||||
<img className="icon-img" src={shouldSplitMemoWord ? "/icons/checkbox-active.svg" : "/icons/checkbox.svg"} />
|
<img className="icon-img" src={shouldSplitMemoWord ? "/icons/checkbox-active.svg" : "/icons/checkbox.svg"} />
|
||||||
</label>
|
</label>
|
||||||
<label className="form-label checkbox-form-label" onClick={handleUseMarkdownParserChanged}>
|
<label className="form-label checkbox-form-label" onClick={handleUseMarkdownParserChanged}>
|
||||||
<span className="normal-text">部分 markdown 格式解析</span>
|
<span className="normal-text">Partial markdown format parsing</span>
|
||||||
<img className="icon-img" src={shouldUseMarkdownParser ? "/icons/checkbox-active.svg" : "/icons/checkbox.svg"} />
|
<img className="icon-img" src={shouldUseMarkdownParser ? "/icons/checkbox-active.svg" : "/icons/checkbox.svg"} />
|
||||||
</label>
|
</label>
|
||||||
<label className="form-label checkbox-form-label" onClick={handleHideImageUrlValueChanged}>
|
<label className="form-label checkbox-form-label" onClick={handleHideImageUrlValueChanged}>
|
||||||
<span className="normal-text">隐藏图片链接地址</span>
|
<span className="normal-text">Hide image url</span>
|
||||||
<img className="icon-img" src={shouldHideImageUrl ? "/icons/checkbox-active.svg" : "/icons/checkbox.svg"} />
|
<img className="icon-img" src={shouldHideImageUrl ? "/icons/checkbox-active.svg" : "/icons/checkbox.svg"} />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="section-container preferences-section-container">
|
<div className="section-container preferences-section-container">
|
||||||
<p className="title-text">编辑器</p>
|
<p className="title-text">Editor Extensions</p>
|
||||||
<label className="form-label checkbox-form-label" onClick={handleOpenTinyUndoChanged}>
|
<label className="form-label checkbox-form-label" onClick={handleOpenTinyUndoChanged}>
|
||||||
<span className="normal-text">
|
<span className="normal-text">
|
||||||
启用{" "}
|
Use{" "}
|
||||||
<a target="_blank" href="https://github.com/boojack/tiny-undo" onClick={(e) => e.stopPropagation()} rel="noreferrer">
|
<a target="_blank" href="https://github.com/boojack/tiny-undo" onClick={(e) => e.stopPropagation()} rel="noreferrer">
|
||||||
tiny-undo
|
tiny-undo
|
||||||
</a>
|
</a>
|
||||||
@ -96,13 +96,13 @@ const PreferencesSection: React.FC<Props> = () => {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="section-container">
|
<div className="section-container">
|
||||||
<p className="title-text">其他</p>
|
<p className="title-text">Others</p>
|
||||||
<div className="w-full flex flex-row justify-start items-center">
|
<div className="w-full flex flex-row justify-start items-center">
|
||||||
<button className="px-2 py-1 border rounded text-base hover:opacity-80" onClick={handleExportBtnClick}>
|
<button className="px-2 py-1 border rounded text-base hover:opacity-80" onClick={handleExportBtnClick}>
|
||||||
导出数据(JSON)
|
Export data as JSON
|
||||||
</button>
|
</button>
|
||||||
<button className="btn format-btn hidden" onClick={handleFormatMemosBtnClick}>
|
<button className="btn format-btn hidden" onClick={handleFormatMemosBtnClick}>
|
||||||
格式化数据
|
Format Data
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,8 +43,8 @@ const PreviewImageDialog: React.FC<Props> = ({ destroy, imgUrl }: Props) => {
|
|||||||
|
|
||||||
<div className="img-container">
|
<div className="img-container">
|
||||||
<img className={imgWidth <= 0 ? "hidden" : ""} ref={imgRef} width={imgWidth + "%"} src={imgUrl} />
|
<img className={imgWidth <= 0 ? "hidden" : ""} ref={imgRef} width={imgWidth + "%"} src={imgUrl} />
|
||||||
<span className={"loading-text " + (imgWidth === -1 ? "" : "hidden")}>图片加载中...</span>
|
<span className={"loading-text " + (imgWidth === -1 ? "" : "hidden")}>Loading image...</span>
|
||||||
<span className={"loading-text " + (imgWidth === 0 ? "" : "hidden")}>😟 图片加载失败,可能是无效的链接</span>
|
<span className={"loading-text " + (imgWidth === 0 ? "" : "hidden")}>😟 Failed to load image</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="action-btns-container">
|
<div className="action-btns-container">
|
||||||
|
@ -36,7 +36,7 @@ const SearchBar: React.FC<Props> = () => {
|
|||||||
<div className="quickly-action-container">
|
<div className="quickly-action-container">
|
||||||
<p className="title-text">QUICKLY FILTER</p>
|
<p className="title-text">QUICKLY FILTER</p>
|
||||||
<div className="section-container types-container">
|
<div className="section-container types-container">
|
||||||
<span className="section-text">类型:</span>
|
<span className="section-text">Type:</span>
|
||||||
<div className="values-container">
|
<div className="values-container">
|
||||||
{memoSpecialTypes.map((t, idx) => {
|
{memoSpecialTypes.map((t, idx) => {
|
||||||
return (
|
return (
|
||||||
|
@ -65,7 +65,7 @@ const ShareMemoImageDialog: React.FC<Props> = (props: Props) => {
|
|||||||
<>
|
<>
|
||||||
<div className="dialog-header-container">
|
<div className="dialog-header-container">
|
||||||
<p className="title-text">
|
<p className="title-text">
|
||||||
<span className="icon-text">🥰</span>分享 Memo 图片
|
<span className="icon-text">🥰</span>Share Memo
|
||||||
</p>
|
</p>
|
||||||
<button className="btn close-btn" onClick={handleCloseBtnClick}>
|
<button className="btn close-btn" onClick={handleCloseBtnClick}>
|
||||||
<img className="icon-img" src="/icons/close.svg" />
|
<img className="icon-img" src="/icons/close.svg" />
|
||||||
@ -73,7 +73,7 @@ const ShareMemoImageDialog: React.FC<Props> = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="dialog-content-container">
|
<div className="dialog-content-container">
|
||||||
<div className={`tip-words-container ${shortcutImgUrl ? "finish" : "loading"}`}>
|
<div className={`tip-words-container ${shortcutImgUrl ? "finish" : "loading"}`}>
|
||||||
<p className="tip-text">{shortcutImgUrl ? "右键或长按即可保存图片 👇" : "图片生成中..."}</p>
|
<p className="tip-text">{shortcutImgUrl ? "Right click or long press to save image 👇" : "Generating the screenshot..."}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="memo-container" ref={memoElRef}>
|
<div className="memo-container" ref={memoElRef}>
|
||||||
<Only when={shortcutImgUrl !== ""}>
|
<Only when={shortcutImgUrl !== ""}>
|
||||||
|
@ -37,7 +37,7 @@ const ShortcutList: React.FC<Props> = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="shortcuts-wrapper">
|
<div className="shortcuts-wrapper">
|
||||||
<p className="title-text">
|
<p className="title-text">
|
||||||
<span className="normal-text">快速检索</span>
|
<span className="normal-text">Shortcuts</span>
|
||||||
<span className="btn" onClick={() => showCreateQueryDialog()}>
|
<span className="btn" onClick={() => showCreateQueryDialog()}>
|
||||||
+
|
+
|
||||||
</span>
|
</span>
|
||||||
@ -45,7 +45,7 @@ const ShortcutList: React.FC<Props> = () => {
|
|||||||
<Only when={loadingState.isSucceed && sortedShortcuts.length === 0}>
|
<Only when={loadingState.isSucceed && sortedShortcuts.length === 0}>
|
||||||
<div className="create-shortcut-btn-container">
|
<div className="create-shortcut-btn-container">
|
||||||
<span className="btn" onClick={() => showCreateQueryDialog()}>
|
<span className="btn" onClick={() => showCreateQueryDialog()}>
|
||||||
创建检索
|
New shortcut
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Only>
|
</Only>
|
||||||
@ -147,17 +147,17 @@ const ShortcutContainer: React.FC<ShortcutContainerProps> = (props: ShortcutCont
|
|||||||
<div className={`action-btns-wrapper ${showActionBtns ? "" : "hidden"}`} onMouseLeave={handleActionBtnContainerMouseLeave}>
|
<div className={`action-btns-wrapper ${showActionBtns ? "" : "hidden"}`} onMouseLeave={handleActionBtnContainerMouseLeave}>
|
||||||
<div className="action-btns-container">
|
<div className="action-btns-container">
|
||||||
<span className="btn" onClick={handlePinQueryBtnClick}>
|
<span className="btn" onClick={handlePinQueryBtnClick}>
|
||||||
{shortcut.rowStatus === "ARCHIVED" ? "取消置顶" : "置顶"}
|
{shortcut.rowStatus === "ARCHIVED" ? "Unpin" : "Pin"}
|
||||||
</span>
|
</span>
|
||||||
<span className="btn" onClick={handleEditQueryBtnClick}>
|
<span className="btn" onClick={handleEditQueryBtnClick}>
|
||||||
编辑
|
Edit
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`btn delete-btn ${showConfirmDeleteBtn ? "final-confirm" : ""}`}
|
className={`btn delete-btn ${showConfirmDeleteBtn ? "final-confirm" : ""}`}
|
||||||
onClick={handleDeleteMemoClick}
|
onClick={handleDeleteMemoClick}
|
||||||
onMouseLeave={handleDeleteBtnMouseLeave}
|
onMouseLeave={handleDeleteBtnMouseLeave}
|
||||||
>
|
>
|
||||||
{showConfirmDeleteBtn ? "确定删除!" : "删除"}
|
{showConfirmDeleteBtn ? "Delete!" : "Delete"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -70,14 +70,14 @@ const TagList: React.FC<Props> = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tags-wrapper">
|
<div className="tags-wrapper">
|
||||||
<p className="title-text">常用标签</p>
|
<p className="title-text">Tags</p>
|
||||||
<div className="tags-container">
|
<div className="tags-container">
|
||||||
{tags.map((t, idx) => (
|
{tags.map((t, idx) => (
|
||||||
<TagItemContainer key={t.text + "-" + idx} tag={t} tagQuery={tagQuery} />
|
<TagItemContainer key={t.text + "-" + idx} tag={t} tagQuery={tagQuery} />
|
||||||
))}
|
))}
|
||||||
<Only when={tags.length < 5 && memoService.initialized}>
|
<Only when={tags.length < 5 && memoService.initialized}>
|
||||||
<p className="tag-tip-container">
|
<p className="tag-tip-container">
|
||||||
输入<span className="code-text"># Tag </span>来创建标签吧~
|
Enter <span className="code-text"># Tag </span> to create a tag
|
||||||
</p>
|
</p>
|
||||||
</Only>
|
</Only>
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,7 +58,7 @@ const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) => {
|
|||||||
<img className="icon-img" src="/icons/arrow-left.svg" />
|
<img className="icon-img" src="/icons/arrow-left.svg" />
|
||||||
</span>
|
</span>
|
||||||
<span className="normal-text">
|
<span className="normal-text">
|
||||||
{firstDate.getFullYear()} 年 {firstDate.getMonth() + 1} 月
|
{firstDate.getFullYear()}/{firstDate.getMonth() + 1}
|
||||||
</span>
|
</span>
|
||||||
<span className="btn-text" onClick={() => handleChangeMonthBtnClick(1)}>
|
<span className="btn-text" onClick={() => handleChangeMonthBtnClick(1)}>
|
||||||
<img className="icon-img" src="/icons/arrow-right.svg" />
|
<img className="icon-img" src="/icons/arrow-right.svg" />
|
||||||
@ -66,13 +66,13 @@ const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="date-picker-day-container">
|
<div className="date-picker-day-container">
|
||||||
<div className="date-picker-day-header">
|
<div className="date-picker-day-header">
|
||||||
<span className="day-item">周一</span>
|
<span className="day-item">Mon</span>
|
||||||
<span className="day-item">周二</span>
|
<span className="day-item">Tue</span>
|
||||||
<span className="day-item">周三</span>
|
<span className="day-item">Web</span>
|
||||||
<span className="day-item">周四</span>
|
<span className="day-item">Thu</span>
|
||||||
<span className="day-item">周五</span>
|
<span className="day-item">Fri</span>
|
||||||
<span className="day-item">周六</span>
|
<span className="day-item">Sat</span>
|
||||||
<span className="day-item">周日</span>
|
<span className="day-item">Sun</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{dayList.map((d) => {
|
{dayList.map((d) => {
|
||||||
|
@ -15,7 +15,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const nullItem = {
|
const nullItem = {
|
||||||
text: "请选择",
|
text: "Select",
|
||||||
value: "",
|
value: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
// 移动端样式适配额外类名
|
// mobile style addtion classname
|
||||||
export const SHOW_SIDERBAR_MOBILE_CLASSNAME = "mobile-show-sidebar";
|
export const SHOW_SIDERBAR_MOBILE_CLASSNAME = "mobile-show-sidebar";
|
||||||
|
|
||||||
// 默认动画持续时长
|
// default animation duration
|
||||||
export const ANIMATION_DURATION = 200;
|
export const ANIMATION_DURATION = 200;
|
||||||
|
|
||||||
// toast 动画持续时长
|
// toast animation duration
|
||||||
export const TOAST_ANIMATION_DURATION = 400;
|
export const TOAST_ANIMATION_DURATION = 400;
|
||||||
|
|
||||||
// 一天的毫秒数
|
// millisecond in a day
|
||||||
export const DAILY_TIMESTAMP = 3600 * 24 * 1000;
|
export const DAILY_TIMESTAMP = 3600 * 24 * 1000;
|
||||||
|
|
||||||
// 标签 正则
|
// tag regex
|
||||||
export const TAG_REG = /#\s?(.+?)\s/g;
|
export const TAG_REG = /#\s?(.+?)\s/g;
|
||||||
|
|
||||||
// URL 正则
|
// URL regex
|
||||||
export const LINK_REG = /(https?:\/\/[^\s<\\*>']+)/g;
|
export const LINK_REG = /(https?:\/\/[^\s<\\*>']+)/g;
|
||||||
|
|
||||||
// 图片 正则
|
// image regex
|
||||||
export const IMAGE_URL_REG = /([^\s<\\*>']+\.(jpeg|jpg|gif|png|svg))/g;
|
export const IMAGE_URL_REG = /([^\s<\\*>']+\.(jpeg|jpg|gif|png|svg))/g;
|
||||||
|
|
||||||
// memo 关联正则
|
// linked memo regex
|
||||||
export const MEMO_LINK_REG = /\[@(.+?)\]\((.+?)\)/g;
|
export const MEMO_LINK_REG = /\[@(.+?)\]\((.+?)\)/g;
|
||||||
|
@ -1,68 +1,68 @@
|
|||||||
import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG } from "./consts";
|
import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG } from "./consts";
|
||||||
|
|
||||||
export const relationConsts = [
|
export const relationConsts = [
|
||||||
{ text: "且", value: "AND" },
|
{ text: "And", value: "AND" },
|
||||||
{ text: "或", value: "OR" },
|
{ text: "Or", value: "OR" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const filterConsts = {
|
export const filterConsts = {
|
||||||
TAG: {
|
TAG: {
|
||||||
|
text: "Tag",
|
||||||
value: "TAG",
|
value: "TAG",
|
||||||
text: "标签",
|
|
||||||
operators: [
|
operators: [
|
||||||
{
|
{
|
||||||
text: "包括",
|
text: "Contains",
|
||||||
value: "CONTAIN",
|
value: "CONTAIN",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "排除",
|
text: "Does not contain",
|
||||||
value: "NOT_CONTAIN",
|
value: "NOT_CONTAIN",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
TYPE: {
|
TYPE: {
|
||||||
|
text: "Type",
|
||||||
value: "TYPE",
|
value: "TYPE",
|
||||||
text: "类型",
|
|
||||||
operators: [
|
operators: [
|
||||||
{
|
{
|
||||||
|
text: "Is",
|
||||||
value: "IS",
|
value: "IS",
|
||||||
text: "是",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
text: "Is not",
|
||||||
value: "IS_NOT",
|
value: "IS_NOT",
|
||||||
text: "不是",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
values: [
|
values: [
|
||||||
{
|
{
|
||||||
|
text: "Connected",
|
||||||
value: "CONNECTED",
|
value: "CONNECTED",
|
||||||
text: "有关联",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
text: "No tags",
|
||||||
value: "NOT_TAGGED",
|
value: "NOT_TAGGED",
|
||||||
text: "无标签",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
text: "Has links",
|
||||||
value: "LINKED",
|
value: "LINKED",
|
||||||
text: "有超链接",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
text: "Has images",
|
||||||
value: "IMAGED",
|
value: "IMAGED",
|
||||||
text: "有图片",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
TEXT: {
|
TEXT: {
|
||||||
|
text: "Text",
|
||||||
value: "TEXT",
|
value: "TEXT",
|
||||||
text: "文本",
|
|
||||||
operators: [
|
operators: [
|
||||||
{
|
{
|
||||||
|
text: "Contain",
|
||||||
value: "CONTAIN",
|
value: "CONTAIN",
|
||||||
text: "包括",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
text: "Does not contain",
|
||||||
value: "NOT_CONTAIN",
|
value: "NOT_CONTAIN",
|
||||||
text: "排除",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -4,13 +4,10 @@ import { InputAction } from "tiny-undo";
|
|||||||
* Define storage data type
|
* Define storage data type
|
||||||
*/
|
*/
|
||||||
interface StorageData {
|
interface StorageData {
|
||||||
// 编辑器输入缓存内容
|
// Editor content cache
|
||||||
editorContentCache: string;
|
editorContentCache: string;
|
||||||
// 分词开关
|
|
||||||
shouldSplitMemoWord: boolean;
|
shouldSplitMemoWord: boolean;
|
||||||
// 是否隐藏图片链接地址
|
|
||||||
shouldHideImageUrl: boolean;
|
shouldHideImageUrl: boolean;
|
||||||
// markdown 解析开关
|
|
||||||
shouldUseMarkdownParser: boolean;
|
shouldUseMarkdownParser: boolean;
|
||||||
|
|
||||||
// Editor setting
|
// Editor setting
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
// 验证器
|
// Validator
|
||||||
// * 主要用于验证表单
|
// * use for validating form data
|
||||||
const chineseReg = /[\u3000\u3400-\u4DBF\u4E00-\u9FFF]/;
|
const chineseReg = /[\u3000\u3400-\u4DBF\u4E00-\u9FFF]/;
|
||||||
|
|
||||||
export interface ValidatorConfig {
|
export interface ValidatorConfig {
|
||||||
// 最小长度
|
// min length
|
||||||
minLength: number;
|
minLength: number;
|
||||||
// 最大长度
|
// max length
|
||||||
maxLength: number;
|
maxLength: number;
|
||||||
// 无空格
|
// no space
|
||||||
noSpace: boolean;
|
noSpace: boolean;
|
||||||
// 无中文
|
// no chinese
|
||||||
noChinese: boolean;
|
noChinese: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ export function validate(text: string, config: Partial<ValidatorConfig>): { resu
|
|||||||
if (text.length < config.minLength) {
|
if (text.length < config.minLength) {
|
||||||
return {
|
return {
|
||||||
result: false,
|
result: false,
|
||||||
reason: "长度过短",
|
reason: "Too short",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -27,7 +27,7 @@ export function validate(text: string, config: Partial<ValidatorConfig>): { resu
|
|||||||
if (text.length > config.maxLength) {
|
if (text.length > config.maxLength) {
|
||||||
return {
|
return {
|
||||||
result: false,
|
result: false,
|
||||||
reason: "长度超出",
|
reason: "Too long",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,14 +35,14 @@ export function validate(text: string, config: Partial<ValidatorConfig>): { resu
|
|||||||
if (config.noSpace && text.includes(" ")) {
|
if (config.noSpace && text.includes(" ")) {
|
||||||
return {
|
return {
|
||||||
result: false,
|
result: false,
|
||||||
reason: "不应含有空格",
|
reason: "Don't allow space",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.noChinese && chineseReg.test(text)) {
|
if (config.noChinese && chineseReg.test(text)) {
|
||||||
return {
|
return {
|
||||||
result: false,
|
result: false,
|
||||||
reason: "不应含有中文字符",
|
reason: "Don't allow chinese",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@ export interface Store<S extends State, A extends Action> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 简单实现的 Redux
|
* Toy-Redux
|
||||||
* @param preloadedState 初始 state
|
* @param preloadedState initial state
|
||||||
* @param reducer reducer pure function
|
* @param reducer reducer pure function
|
||||||
* @returns store
|
* @returns store
|
||||||
*/
|
*/
|
||||||
|
@ -114,13 +114,13 @@ const MemoTrash: React.FC<Props> = () => {
|
|||||||
<img className="icon-img" src="/icons/menu.svg" alt="menu" />
|
<img className="icon-img" src="/icons/menu.svg" alt="menu" />
|
||||||
</button>
|
</button>
|
||||||
</Only>
|
</Only>
|
||||||
<span className="normal-text">回收站</span>
|
<span className="normal-text">Recycle Bin</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MemoFilter />
|
<MemoFilter />
|
||||||
{loadingState.isLoading ? (
|
{loadingState.isLoading ? (
|
||||||
<div className="tip-text-container">
|
<div className="tip-text-container">
|
||||||
<p className="tip-text">努力请求数据中...</p>
|
<p className="tip-text">fetching data...</p>
|
||||||
</div>
|
</div>
|
||||||
) : deletedMemos.length === 0 ? (
|
) : deletedMemos.length === 0 ? (
|
||||||
<div className="tip-text-container">
|
<div className="tip-text-container">
|
||||||
|
@ -30,7 +30,7 @@ const Setting: React.FC<Props> = () => {
|
|||||||
<img className="icon-img" src="/icons/menu.svg" alt="menu" />
|
<img className="icon-img" src="/icons/menu.svg" alt="menu" />
|
||||||
</button>
|
</button>
|
||||||
</Only>
|
</Only>
|
||||||
<span className="normal-text">账号与设置</span>
|
<span className="normal-text">Settings</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -55,13 +55,13 @@ const Signin: React.FC<Props> = () => {
|
|||||||
|
|
||||||
const usernameValidResult = validate(username, validateConfig);
|
const usernameValidResult = validate(username, validateConfig);
|
||||||
if (!usernameValidResult.result) {
|
if (!usernameValidResult.result) {
|
||||||
toastHelper.error("用户名 " + usernameValidResult.reason);
|
toastHelper.error("Username: " + usernameValidResult.reason);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const passwordValidResult = validate(password, validateConfig);
|
const passwordValidResult = validate(password, validateConfig);
|
||||||
if (!passwordValidResult.result) {
|
if (!passwordValidResult.result) {
|
||||||
toastHelper.error("密码 " + passwordValidResult.reason);
|
toastHelper.error("Password: " + passwordValidResult.reason);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ const Signin: React.FC<Props> = () => {
|
|||||||
if (user) {
|
if (user) {
|
||||||
locationService.replaceHistory("/");
|
locationService.replaceHistory("/");
|
||||||
} else {
|
} else {
|
||||||
toastHelper.error("😟 登录失败");
|
toastHelper.error("😟 Login failed");
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -107,7 +107,7 @@ const Signin: React.FC<Props> = () => {
|
|||||||
if (user) {
|
if (user) {
|
||||||
locationService.replaceHistory("/");
|
locationService.replaceHistory("/");
|
||||||
} else {
|
} else {
|
||||||
toastHelper.error("😟 登录失败");
|
toastHelper.error("😟 Login failed");
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -121,7 +121,7 @@ const Signin: React.FC<Props> = () => {
|
|||||||
<div className="page-container">
|
<div className="page-container">
|
||||||
<div className="page-header-container">
|
<div className="page-header-container">
|
||||||
<p className="title-text">
|
<p className="title-text">
|
||||||
登录 Memos <span className="icon-text">✍️</span>
|
Login to Memos <span className="icon-text">✍️</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{showAutoSigninAsGuest ? (
|
{showAutoSigninAsGuest ? (
|
||||||
@ -132,13 +132,13 @@ const Signin: React.FC<Props> = () => {
|
|||||||
className={`btn guest-signin ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
className={`btn guest-signin ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
||||||
onClick={handleAutoSigninAsGuestBtnClick}
|
onClick={handleAutoSigninAsGuestBtnClick}
|
||||||
>
|
>
|
||||||
👉 快速登录进行体验
|
👉 Login as Guest quickly
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`btn ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
className={`btn ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
||||||
onClick={handleSwitchAccountSigninBtnClick}
|
onClick={handleSwitchAccountSigninBtnClick}
|
||||||
>
|
>
|
||||||
已有账号,我要自己登录
|
I have an account
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@ -146,11 +146,11 @@ const Signin: React.FC<Props> = () => {
|
|||||||
<>
|
<>
|
||||||
<div className="page-content-container">
|
<div className="page-content-container">
|
||||||
<div className="form-item-container input-form-container">
|
<div className="form-item-container input-form-container">
|
||||||
<span className={"normal-text " + (username === "" ? "" : "not-null")}>账号</span>
|
<span className={"normal-text " + (username === "" ? "" : "not-null")}>Usernmae</span>
|
||||||
<input type="text" autoComplete="off" value={username} onChange={handleUsernameInputChanged} />
|
<input type="text" autoComplete="off" value={username} onChange={handleUsernameInputChanged} />
|
||||||
</div>
|
</div>
|
||||||
<div className="form-item-container input-form-container">
|
<div className="form-item-container input-form-container">
|
||||||
<span className={"normal-text " + (password === "" ? "" : "not-null")}>密码</span>
|
<span className={"normal-text " + (password === "" ? "" : "not-null")}>Password</span>
|
||||||
<input type="password" autoComplete="off" value={password} onChange={handlePasswordInputChanged} />
|
<input type="password" autoComplete="off" value={password} onChange={handlePasswordInputChanged} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -161,14 +161,14 @@ const Signin: React.FC<Props> = () => {
|
|||||||
className={`btn ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
className={`btn ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
||||||
onClick={handleAutoSigninAsGuestBtnClick}
|
onClick={handleAutoSigninAsGuestBtnClick}
|
||||||
>
|
>
|
||||||
体验一下
|
Login as Guest
|
||||||
</button>
|
</button>
|
||||||
<span className="split-text">/</span>
|
<span className="split-text">/</span>
|
||||||
<button
|
<button
|
||||||
className={`btn signin-btn ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
className={`btn signin-btn ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
||||||
onClick={() => handleSigninBtnsClick("signup")}
|
onClick={() => handleSigninBtnsClick("signup")}
|
||||||
>
|
>
|
||||||
注册
|
Sign up
|
||||||
</button>
|
</button>
|
||||||
<span className="split-text">/</span>
|
<span className="split-text">/</span>
|
||||||
<button
|
<button
|
||||||
@ -176,7 +176,7 @@ const Signin: React.FC<Props> = () => {
|
|||||||
className={`btn signin-btn ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
className={`btn signin-btn ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
||||||
onClick={() => handleSigninBtnsClick("signin")}
|
onClick={() => handleSigninBtnsClick("signin")}
|
||||||
>
|
>
|
||||||
登录
|
Login
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,7 +10,7 @@ class ResourceService {
|
|||||||
const { name: filename, size } = file;
|
const { name: filename, size } = file;
|
||||||
|
|
||||||
if (size > 5 << 20) {
|
if (size > 5 << 20) {
|
||||||
return Promise.reject("超过最大文件大小 5Mb");
|
return Promise.reject("overload max size: 5Mb");
|
||||||
}
|
}
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
2
web/src/types/basic.d.ts
vendored
2
web/src/types/basic.d.ts
vendored
@ -1,9 +1,7 @@
|
|||||||
type BasicType = undefined | null | boolean | number | string | Record<string, unknown> | Array<BasicType>;
|
type BasicType = undefined | null | boolean | number | string | Record<string, unknown> | Array<BasicType>;
|
||||||
|
|
||||||
// 日期戳
|
|
||||||
type DateStamp = number;
|
type DateStamp = number;
|
||||||
|
|
||||||
// 时间戳
|
|
||||||
type TimeStamp = number;
|
type TimeStamp = number;
|
||||||
|
|
||||||
type FunctionType = (...args: unknown[]) => unknown;
|
type FunctionType = (...args: unknown[]) => unknown;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user