From 38388c13468c5980df896e30f0d1cb56d8c3bece Mon Sep 17 00:00:00 2001 From: f97 Date: Fri, 16 Sep 2022 12:44:44 +0000 Subject: [PATCH 1/6] feat: add vietnamese --- api/user_setting.go | 2 +- .../Settings/PreferencesSection.tsx | 4 + web/src/labs/i18n/useI18n.ts | 2 + web/src/locales/vi.json | 126 ++++++++++++++++++ web/src/pages/Auth.tsx | 4 + web/src/types/i18n.d.ts | 2 +- 6 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 web/src/locales/vi.json diff --git a/api/user_setting.go b/api/user_setting.go index 8c7cacd77fa88..9aeb153e2e248 100644 --- a/api/user_setting.go +++ b/api/user_setting.go @@ -30,7 +30,7 @@ func (key UserSettingKey) String() string { } var ( - UserSettingLocaleValue = []string{"en", "zh"} + UserSettingLocaleValue = []string{"en", "zh", "vi"} UserSettingMemoVisibilityValue = []Visibility{Privite, Protected, Public} UserSettingEditorFontStyleValue = []string{"normal", "mono"} ) diff --git a/web/src/components/Settings/PreferencesSection.tsx b/web/src/components/Settings/PreferencesSection.tsx index 10e1e8be6ddcc..6024335a2b935 100644 --- a/web/src/components/Settings/PreferencesSection.tsx +++ b/web/src/components/Settings/PreferencesSection.tsx @@ -15,6 +15,10 @@ const localeSelectorItems = [ text: "中文", value: "zh", }, + { + text: "Tiếng Việt", + value: "vi", + }, ]; const editorFontStyleSelectorItems = [ diff --git a/web/src/labs/i18n/useI18n.ts b/web/src/labs/i18n/useI18n.ts index cfa6f0d7ae109..3f74b3e5586dc 100644 --- a/web/src/labs/i18n/useI18n.ts +++ b/web/src/labs/i18n/useI18n.ts @@ -2,10 +2,12 @@ import { useEffect, useState } from "react"; import i18nStore from "./i18nStore"; import enLocale from "../../locales/en.json"; import zhLocale from "../../locales/zh.json"; +import viLocale from "../../locales/vi.json"; const resources: Record = { en: enLocale, zh: zhLocale, + vi: viLocale, }; const useI18n = () => { diff --git a/web/src/locales/vi.json b/web/src/locales/vi.json new file mode 100644 index 0000000000000..aabdea33fdc3b --- /dev/null +++ b/web/src/locales/vi.json @@ -0,0 +1,126 @@ +{ + "common": { + "about": "Thông Tin", + "email": "Email", + "password": "Mật khẩu", + "new-password": "Mật khẩu mới", + "repeat-new-password": "Nhập lại mật khẩu mới", + "username": "Tên đăng nhập", + "save": "Lưu", + "cancel": "Hủy", + "create": "Tạo mới", + "change": "Thay đổi", + "reset": "Khôi phục", + "language": "Ngôn Ngữ", + "version": "Phiên bản", + "pin": "Gắn lên trên cùng", + "unpin": "Gỡ khỏi vị trị trên cùng", + "edit": "Chỉnh sửa", + "restore": "Khôi phục", + "delete": "Xóa", + "null": "Null", + "share": "Chia sẻ", + "mark": "Đánh dấu", + "archive": "Lưu trữ", + "basic": "Cơ bản", + "admin": "Admin", + "explore": "Khám phá", + "sign-in": "Đăng nhập", + "sign-out": "Đăng xuất", + "back-to-home": "Quay về trang chủ", + "type": "Kiểu", + "shortcuts": "Lối tắt", + "shortcut-title": "Tên lối tắt", + "title": "Tên", + "filter": "Bộ lọc", + "tags": "Thẻ", + "no-memos": "Không có memo nào 🌃", + "memos-ready": "Tất cả memo ở đây rồi 🎉", + "yourself": "Của bạn", + "archived-at": "Lưu trữ lúc", + "restored-successfully": "Đã khôi phục thành công", + "memo-updated-datetime":"Memo đã thay đổi ngày giờ tạo.", + "invalid-created-datetime": "Ngày giờ tạo không hợp lệ.", + "change-memo-created-time": "Thay đổi thời gian tạo memo", + "memo-not-found": "Không tìm thấy memo.", + "fill-all": "Vui lòng nhập tất cả các mục.", + "new-password-not-match": "Mật khẩu mới không giống nhau.", + "changed": "đã thay đổi", + "image-load-failed": "Tải ảnh bị lỗi", + "fill-form": "Please fill out this form", + "login-failed": "Đăng nhập thất bại", + "signup-failed": "Đăng ký thất bại", + "user-not-found": "Không tìm thấy người dùng này" + }, + "slogan": "Một mã nguồn mở, tự lưu lại mọi thứ bạn biết dựa trên SQLite db.", + "auth": { + "signup-as-host": "Đăng ký như chủ nhân", + "host-tip": "Bạn đang đăng ký với tư cách chủ nhân của trang web này.", + "not-host-tip": "Nếu chưa có tài khoản, bạn cần phải liên hệ chủ nhân của trang web." + }, + "sidebar": { + "daily-review": "Đánh giá hàng ngày", + "resources": "Tài nguyên", + "setting": "Cài đặt", + "archived": "Lưu" + }, + "daily-review": { + "oops-nothing": "Oops, chẳng có gì ở đây cả." + }, + "resources": { + "description": "Xem toàn bộ tài nguyên của trang web. ví dụ hình ảnh", + "no-resources": "Không có tài nguyên.", + "fetching-data": "đang tải dữ liệu...", + "upload": "Tải lên", + "preview": "Xem trước", + "copy-link": "Sao chép" + }, + "archived": { + "archived-memos": "Memo đã lưu trữ", + "no-archived-memos": "Không có memo nào cả.", + "fetching-data": "đang tải dữ liệu..." + }, + "editor": { + "editing": "Đang chỉnh sửa...", + "save": "Lưu", + "placeholder": "Bất cứ gì bạn đang nghĩ...", + "only-image-supported": "Chỉ hỗ trợ hình ảnh.", + "cant-empty": "Nội dung không thể trống" + }, + "memo": { + "view-story": "Xem nội dung" + }, + "memo-list": { + "fetching-data": "đang tải dữ liệu..." + }, + "shortcut-list": { + "create-shortcut": "Tạo lối tắt", + "edit-shortcut": "Chỉnh sửa lối tắt", + "new-filter": "Bộ lọc mới", + "eligible-memo": "memo đạt chuẩn", + "fill-previous": "Vui lòng nhập vào giá trị của bộ lọc trước đó", + "title-required": "Tiêu đề là bắt buộc" + }, + "tag-list": { + "tip-text": "Nhập `#tag` để tạo mới" + }, + "search": { + "quickly-filter": "Lọc nhanh" + }, + "setting": { + "my-account": "Tài khoản", + "preference": "Sở thích", + "member": "Thành viên", + "member-list": "Danh sách thành viên", + "account-section": { + "title": "Thông tin tài khoản" + }, + "preference-section": { + "default-memo-visibility": "Chế độ memo mặc định", + "editor-font-style": "Thay đổi font cho trình soạn thảo" + }, + "member-section": { + "create-a-member": "Tạo một thành viên" + } + } +} diff --git a/web/src/pages/Auth.tsx b/web/src/pages/Auth.tsx index ff8246d57cc48..6125a5b0daa71 100644 --- a/web/src/pages/Auth.tsx +++ b/web/src/pages/Auth.tsx @@ -162,6 +162,10 @@ const Auth = () => { handleLocaleItemClick("zh")}> 中文 + / + handleLocaleItemClick("vi")}> + Tiếng Việt + diff --git a/web/src/types/i18n.d.ts b/web/src/types/i18n.d.ts index 32c3be6e0e740..78ab4f5b5721f 100644 --- a/web/src/types/i18n.d.ts +++ b/web/src/types/i18n.d.ts @@ -1 +1 @@ -type Locale = "en" | "zh"; +type Locale = "en" | "zh" | "vi"; From 3da2a3b366dbdc78008b4ce91818fb2df930f840 Mon Sep 17 00:00:00 2001 From: f97 Date: Fri, 16 Sep 2022 16:29:02 +0000 Subject: [PATCH 2/6] feat: add emoji picker in editor --- web/package.json | 1 + web/src/components/Editor/EmojiPicker.tsx | 41 +++++++++++++++++++++++ web/src/components/MemoEditor.tsx | 28 ++++++++++++++++ web/src/less/memo-editor.less | 8 +++++ web/vite.config.ts | 3 ++ web/yarn.lock | 5 +++ 6 files changed, 86 insertions(+) create mode 100644 web/src/components/Editor/EmojiPicker.tsx diff --git a/web/package.json b/web/package.json index 71937e1ff7d09..ce563f127ca85 100644 --- a/web/package.json +++ b/web/package.json @@ -12,6 +12,7 @@ "axios": "^0.27.2", "copy-to-clipboard": "^3.3.2", "dayjs": "^1.11.3", + "emoji-picker-react": "^3.6.2", "lodash-es": "^4.17.21", "qs": "^6.11.0", "react": "^18.1.0", diff --git a/web/src/components/Editor/EmojiPicker.tsx b/web/src/components/Editor/EmojiPicker.tsx new file mode 100644 index 0000000000000..9efce83d4b5b8 --- /dev/null +++ b/web/src/components/Editor/EmojiPicker.tsx @@ -0,0 +1,41 @@ +import { forwardRef, useEffect } from "react"; +import Picker, { IEmojiData, IEmojiPickerProps } from "emoji-picker-react"; + +export type EmojiPickerElement = HTMLDivElement; + +interface Props { + isShowEmojiPicker: boolean; + onEmojiClick: IEmojiPickerProps["onEmojiClick"]; + handleChangeIsShowEmojiPicker: (status: boolean) => void; +} + +export const EmojiPicker = forwardRef((props: Props, ref) => { + const { isShowEmojiPicker, onEmojiClick, handleChangeIsShowEmojiPicker } = props; + + useEffect(() => { + if (isShowEmojiPicker) { + const handleClickOutside = (event: MouseEvent) => { + const emojiWrapper = document.querySelector(".emoji-picker-react"); + const isContains = emojiWrapper?.contains(event.target as Node); + if (!isContains) { + handleChangeIsShowEmojiPicker(false); + } + }; + document.addEventListener("mousedown", handleClickOutside); + return () => { + // Unbind the event listener on clean up + document.removeEventListener("mousedown", handleClickOutside); + }; + } + }); + + return ( +
+ +
+ ); +}); + +EmojiPicker.displayName = "EmojiPicker"; + +export default EmojiPicker; diff --git a/web/src/components/MemoEditor.tsx b/web/src/components/MemoEditor.tsx index d23af53bdd580..ef451e123bd4d 100644 --- a/web/src/components/MemoEditor.tsx +++ b/web/src/components/MemoEditor.tsx @@ -7,11 +7,13 @@ import * as storage from "../helpers/storage"; import Icon from "./Icon"; import toastHelper from "./Toast"; import Editor, { EditorRefActions } from "./Editor/Editor"; +import EmojiPicker from "./Editor/EmojiPicker"; import "../less/memo-editor.less"; interface State { isUploadingResource: boolean; fullscreen: boolean; + isShowEmojiPicker: boolean; } const MemoEditor = () => { @@ -22,6 +24,7 @@ const MemoEditor = () => { const [state, setState] = useState({ isUploadingResource: false, fullscreen: false, + isShowEmojiPicker: false, }); const editorRef = useRef(null); const prevGlobalStateRef = useRef(editorState); @@ -244,6 +247,21 @@ const MemoEditor = () => { } }, []); + const handleChangeIsShowEmojiPicker = (status: boolean) => { + setState({ + ...state, + isShowEmojiPicker: status, + }); + }; + + const handleEmojiClick = (event: any, emojiObject: any) => { + if (!editorRef.current) { + return; + } + editorRef.current?.insertText(`${emojiObject.emoji}`); + handleChangeIsShowEmojiPicker(false) + }; + const isEditing = Boolean(editorState.editMemoId && editorState.editMemoId !== UNKNOWN_ID); const editorConfig = useMemo( @@ -300,12 +318,22 @@ const MemoEditor = () => { Uploading + } /> + {state.isShowEmojiPicker && ( + + )} ); }; diff --git a/web/src/less/memo-editor.less b/web/src/less/memo-editor.less index 34cda7da1b377..52e649a6eeb4d 100644 --- a/web/src/less/memo-editor.less +++ b/web/src/less/memo-editor.less @@ -62,4 +62,12 @@ } } } + + .emoji-picker-react { + @apply absolute; + + li.emoji::before { + @apply hidden; + } + } } diff --git a/web/vite.config.ts b/web/vite.config.ts index 3f640eee661fd..6d594be3cb42b 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -28,4 +28,7 @@ export default defineConfig({ "@/": `${resolve(__dirname, "src")}/`, }, }, + define: { + global: {}, + }, }); diff --git a/web/yarn.lock b/web/yarn.lock index 7d0dc45d48851..d4eea8893813d 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -880,6 +880,11 @@ electron-to-chromium@^1.4.172: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.185.tgz#3432d7944f1c5fe20664bb45d9cced2151405ce2" integrity sha512-9kV/isoOGpKkBt04yYNaSWIBn3187Q5VZRtoReq8oz5NY/A4XmU6cAoqgQlDp7kKJCZMRjWZ8nsQyxfpFHvfyw== +emoji-picker-react@^3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/emoji-picker-react/-/emoji-picker-react-3.6.2.tgz#e414971bf9421b0825484f3b82623fef995c3c4b" + integrity sha512-PK3dfljGxeyN8fDz2FAsDYKPYGgo6/tkRyzJjLVaw0fksJg7jA3OJPIlHq2IIzTmlC/NKhcI/oaf0uEo5azYGA== + errno@^0.1.1: version "0.1.8" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" From c3d7d9becc4f2fb1bda02fead5b2873ce14b83c0 Mon Sep 17 00:00:00 2001 From: f97 Date: Fri, 16 Sep 2022 16:32:07 +0000 Subject: [PATCH 3/6] fix failing checks --- web/index.html | 1 + web/src/components/Editor/EmojiPicker.tsx | 2 +- web/src/components/MemoEditor.tsx | 2 +- web/vite.config.ts | 3 --- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/web/index.html b/web/index.html index 925ec06638825..4b39ee8340778 100644 --- a/web/index.html +++ b/web/index.html @@ -6,6 +6,7 @@ + Memos diff --git a/web/src/components/Editor/EmojiPicker.tsx b/web/src/components/Editor/EmojiPicker.tsx index 9efce83d4b5b8..1cb7524f4e853 100644 --- a/web/src/components/Editor/EmojiPicker.tsx +++ b/web/src/components/Editor/EmojiPicker.tsx @@ -1,5 +1,5 @@ import { forwardRef, useEffect } from "react"; -import Picker, { IEmojiData, IEmojiPickerProps } from "emoji-picker-react"; +import Picker, { IEmojiPickerProps } from "emoji-picker-react"; export type EmojiPickerElement = HTMLDivElement; diff --git a/web/src/components/MemoEditor.tsx b/web/src/components/MemoEditor.tsx index ef451e123bd4d..3798b624859b8 100644 --- a/web/src/components/MemoEditor.tsx +++ b/web/src/components/MemoEditor.tsx @@ -259,7 +259,7 @@ const MemoEditor = () => { return; } editorRef.current?.insertText(`${emojiObject.emoji}`); - handleChangeIsShowEmojiPicker(false) + handleChangeIsShowEmojiPicker(false); }; const isEditing = Boolean(editorState.editMemoId && editorState.editMemoId !== UNKNOWN_ID); diff --git a/web/vite.config.ts b/web/vite.config.ts index 6d594be3cb42b..3f640eee661fd 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -28,7 +28,4 @@ export default defineConfig({ "@/": `${resolve(__dirname, "src")}/`, }, }, - define: { - global: {}, - }, }); From 6e11ea91794c800bc93635ca1c8f3c3f6c50eef9 Mon Sep 17 00:00:00 2001 From: f97 Date: Fri, 16 Sep 2022 16:35:56 +0000 Subject: [PATCH 4/6] move emoji button before upload button --- web/src/components/Editor/EmojiPicker.tsx | 2 +- web/src/components/MemoEditor.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web/src/components/Editor/EmojiPicker.tsx b/web/src/components/Editor/EmojiPicker.tsx index 1cb7524f4e853..efa2c9f6f4145 100644 --- a/web/src/components/Editor/EmojiPicker.tsx +++ b/web/src/components/Editor/EmojiPicker.tsx @@ -27,7 +27,7 @@ export const EmojiPicker = forwardRef((props: Props, document.removeEventListener("mousedown", handleClickOutside); }; } - }); + }, [isShowEmojiPicker]); return (
diff --git a/web/src/components/MemoEditor.tsx b/web/src/components/MemoEditor.tsx index 3798b624859b8..8cd9b65d9c0b6 100644 --- a/web/src/components/MemoEditor.tsx +++ b/web/src/components/MemoEditor.tsx @@ -315,11 +315,11 @@ const MemoEditor = () => {