From 83c377d38c276af273f8f2817fa84f778a0f4400 Mon Sep 17 00:00:00 2001 From: Coki Date: Fri, 22 Nov 2024 23:33:53 +0800 Subject: [PATCH 1/6] chore: update casbin dependency to version 5.36.0 --- package.json | 2 +- yarn.lock | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index a751eca..5cdfc2b 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "@radix-ui/react-dropdown-menu": "^2.1.1", "@uiw/codemirror-theme-monokai": "^4.21.22", "@uiw/react-codemirror": "^4.21.22", - "casbin": "^5.31.0", + "casbin": "^5.36.0", "clsx": "^2.1.0", "codemirror": "^6.0.1", "next": "14.1.0", diff --git a/yarn.lock b/yarn.lock index e1886f7..b30f9e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19,6 +19,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@casbin/expression-eval@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@casbin/expression-eval/-/expression-eval-5.3.0.tgz#4721504cbc1fcd7e334c01e44f257ca1f9e2550b" + integrity sha512-mMTHMYXcnBBv/zMvxMpcdVyt2bfw8Y0GnmRLbkFQ1CVJZb4XZp7xWjRh7ymOLuJdsu58rci9gmOOv/99DtJvPA== + dependencies: + jsep "^0.3.0" + "@codemirror/autocomplete@^6.0.0": version "6.16.0" resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.16.0.tgz#595eb30099ba91a835ed65ed8ff7497388f604b3" @@ -1354,15 +1361,15 @@ caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.300015 resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz#4adcb443c8b9c8303e04498318f987616b8fea2e" integrity sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA== -casbin@^5.31.0: - version "5.31.0" - resolved "https://registry.yarnpkg.com/casbin/-/casbin-5.31.0.tgz#bcd76061192956acf07c1e5a224e93ed23322a87" - integrity sha512-hA3be1bCfZEg4/uNOBw/fAJghEzU1YKODT9lIP5y50puyQwzRF5re1wztSP/fPdv8602z3sWBCdAcMZpkQuYAw== +casbin@^5.36.0: + version "5.36.0" + resolved "https://registry.yarnpkg.com/casbin/-/casbin-5.36.0.tgz#1379a32a09fed5735a7048e9848ddaf42e28a994" + integrity sha512-vaH292Ed+Op8WhNFA/VJqMearhnQwFs3TU3qKCOAmUeTtGS9un8+fPvJ4OGQ7luWFAG/EVZAvNWCWTbciU4R+Q== dependencies: + "@casbin/expression-eval" "^5.3.0" await-lock "^2.0.1" buffer "^6.0.3" - csv-parse "^5.3.5" - expression-eval "^5.0.0" + csv-parse "^5.5.6" minimatch "^7.4.2" chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.2: @@ -1533,10 +1540,10 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== -csv-parse@^5.3.5: - version "5.5.6" - resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-5.5.6.tgz#0d726d58a60416361358eec291a9f93abe0b6b1a" - integrity sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A== +csv-parse@^5.5.6: + version "5.6.0" + resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-5.6.0.tgz#219beace2a3e9f28929999d2aa417d3fb3071c7f" + integrity sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q== damerau-levenshtein@^1.0.8: version "1.0.8" @@ -2146,13 +2153,6 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -expression-eval@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/expression-eval/-/expression-eval-5.0.1.tgz#845758fa9ba64d9edc7b6804ae404934a6cfee6b" - integrity sha512-7SL4miKp19lI834/F6y156xlNg+i9Q41tteuGNCq9C06S78f1bm3BXuvf0+QpQxv369Pv/P2R7Hb17hzxLpbDA== - dependencies: - jsep "^0.3.0" - extract-zip@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" From d0a302d2b268f962d8ec5abf905b74752cf9e81b Mon Sep 17 00:00:00 2001 From: Coki Date: Wed, 27 Nov 2024 12:30:30 +0800 Subject: [PATCH 2/6] style: add color classes to status messages in editor hooks --- app/components/editor/hooks/useIndex.tsx | 8 ++-- app/components/editor/hooks/useRunTest.tsx | 4 +- app/components/editor/hooks/useShareInfo.tsx | 6 +-- app/components/editor/index.tsx | 42 +++++++++++++++++--- yarn.lock | 6 +-- 5 files changed, 49 insertions(+), 17 deletions(-) diff --git a/app/components/editor/hooks/useIndex.tsx b/app/components/editor/hooks/useIndex.tsx index a49aff5..586260b 100644 --- a/app/components/editor/hooks/useIndex.tsx +++ b/app/components/editor/hooks/useIndex.tsx @@ -44,7 +44,7 @@ export default function useIndex() { const hash = window.location.hash.slice(1); if (hash && hash !== loadState.current.loadedHash) { loadState.current.loadedHash = hash; - setEcho(
Loading Shared Content...
); + setEcho(
Loading Shared Content...
); fetch(`https://dpaste.com/${hash}.txt`) .then((resp) => { return resp.ok ? resp.text() : Promise.reject(`HTTP error: ${resp.status}`); @@ -54,10 +54,10 @@ export default function useIndex() { loadState.current.content = parsed; const newModelKind = parsed?.modelKind && parsed.modelKind in example ? (parsed.modelKind as ModelKind) : 'basic'; setModelKind(newModelKind); - setEcho(
Shared Content Loaded.
); + setEcho(
Shared Content Loaded.
); }) .catch((error) => { - return setEcho(
Failed to load: {error}
); + return setEcho(
Failed to load: {error}
); }); } }, []); @@ -79,7 +79,7 @@ export default function useIndex() { } else { const currentPath = window.location.origin + window.location.pathname; setShare(v as string); - setEcho(
{`Shared at ${currentPath}#${v}`}
); + setEcho(
{`Shared at ${currentPath}#${v}`}
); } } diff --git a/app/components/editor/hooks/useRunTest.tsx b/app/components/editor/hooks/useRunTest.tsx index 60e2542..1a9bab3 100755 --- a/app/components/editor/hooks/useRunTest.tsx +++ b/app/components/editor/hooks/useRunTest.tsx @@ -181,11 +181,11 @@ async function enforcer(props: RunTestProps) { setError(null); - props.onResponse(
{'Done in ' + (stopTime - startTime).toFixed(2) + 'ms'}
); + props.onResponse(
{'Done in ' + (stopTime - startTime).toFixed(2) + 'ms'}
); props.onResponse(result); } catch (e) { const errorMessage = (e as any).message; - props.onResponse(
{errorMessage}
); + props.onResponse(
{errorMessage}
); props.onResponse([]); setError(errorMessage); } diff --git a/app/components/editor/hooks/useShareInfo.tsx b/app/components/editor/hooks/useShareInfo.tsx index d2aa692..6f782e5 100644 --- a/app/components/editor/hooks/useShareInfo.tsx +++ b/app/components/editor/hooks/useShareInfo.tsx @@ -42,7 +42,7 @@ export default function useShareInfo() { function shareInfo(props: ShareProps) { if (sharing) return; setSharing(true); - props.onResponse(
Sharing...
); + props.onResponse(
Sharing...
); // Create an object that contains only non-null values const shareContent: ShareFormat = { @@ -58,7 +58,7 @@ export default function useShareInfo() { // Check if there are any non-null values to share if (Object.keys(shareContent).length === 0) { setSharing(false); - props.onResponse(
No content to share
); + props.onResponse(
No content to share
); return; } @@ -70,7 +70,7 @@ export default function useShareInfo() { }) .catch((error) => { setSharing(false); - props.onResponse(
Error sharing content: {error.message}
); + props.onResponse(
Error sharing content: {error.message}
); }); } diff --git a/app/components/editor/index.tsx b/app/components/editor/index.tsx index c82405c..5954d62 100755 --- a/app/components/editor/index.tsx +++ b/app/components/editor/index.tsx @@ -54,6 +54,10 @@ export const EditorScreen = () => { return message; }; const { t, lang, theme, toggleTheme } = useLang(); + const [lastClickTime, setLastClickTime] = useState(0); + const [lastValidResult, setLastValidResult] = useState(null); + const [lastRunClickTime, setLastRunClickTime] = useState(0); + const [lastRunResult, setLastRunResult] = useState(null); useEffect(() => { const fetchCasbinVersion = async () => { @@ -399,11 +403,25 @@ export const EditorScreen = () => { 'transition-colors duration-500', )} onClick={() => { + const now = Date.now(); + if (now - lastClickTime < 2000) { + setEcho(
Repeated clicks
); + setTimeout(() => { + setEcho(lastValidResult); + }, 2000); + return; + } + setLastClickTime(now); + try { Config.newConfigFromText(modelText); - setEcho(
Passed
); + const result =
Passed
; + setEcho(result); + setLastValidResult(result); } catch (e) { - setEcho(
{(e as any).message}
); + const result =
{(e as any).message}
; + setEcho(result); + setLastValidResult(result); } }} > @@ -420,6 +438,16 @@ export const EditorScreen = () => { 'transition-colors duration-500', )} onClick={() => { + const now = Date.now(); + if (now - lastRunClickTime < 2000) { + setEcho(
Repeated clicks
); + setTimeout(() => { + setEcho(lastRunResult); + }, 2000); + return; + } + setLastRunClickTime(now); + return enforcer({ modelKind, model: modelText, @@ -430,15 +458,19 @@ export const EditorScreen = () => { onResponse: (v) => { if (isValidElement(v)) { setEcho(v); + setLastRunResult(v); } else if (Array.isArray(v)) { const formattedResults = v.map((res) => { if (typeof res === 'object') { - const reasonString = Array.isArray(res.reason) && res.reason.length > 0 ? ` Reason: ${JSON.stringify(res.reason)}` : ''; + const reasonString = Array.isArray(res.reason) && res.reason.length > 0 + ? ` Reason: ${JSON.stringify(res.reason)}` + : ''; return `${res.okEx}${reasonString}`; } return res; }); - setRequestResult(formattedResults.join('\n')); + const result = formattedResults.join('\n'); + setRequestResult(result); } }, }); @@ -488,7 +520,7 @@ export const EditorScreen = () => { return copy( () => { setShare(''); - setEcho(
{t('Copied')}
); + setEcho(
{t('Copied')}
); }, `${window.location.origin + window.location.pathname}#${share}`, ); diff --git a/yarn.lock b/yarn.lock index b30f9e6..1d78e69 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1357,9 +1357,9 @@ camelcase-css@^2.0.1: integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599: - version "1.0.30001621" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz#4adcb443c8b9c8303e04498318f987616b8fea2e" - integrity sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA== + version "1.0.30001684" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz" + integrity sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ== casbin@^5.36.0: version "5.36.0" From 57da8c96ebba4fdc85211c258b93b8cc2dc640b6 Mon Sep 17 00:00:00 2001 From: Coki Date: Wed, 27 Nov 2024 14:49:32 +0800 Subject: [PATCH 3/6] feat: add content loading state to EditorScreen component --- app/components/editor/index.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/components/editor/index.tsx b/app/components/editor/index.tsx index 5954d62..2c40986 100755 --- a/app/components/editor/index.tsx +++ b/app/components/editor/index.tsx @@ -58,6 +58,7 @@ export const EditorScreen = () => { const [lastValidResult, setLastValidResult] = useState(null); const [lastRunClickTime, setLastRunClickTime] = useState(0); const [lastRunResult, setLastRunResult] = useState(null); + const [isContentLoaded, setIsContentLoaded] = useState(false); useEffect(() => { const fetchCasbinVersion = async () => { @@ -69,7 +70,8 @@ export const EditorScreen = () => { }, []); useEffect(() => { - if (modelKind) { + if (modelKind && modelText) { + setIsContentLoaded(true); enforcer({ modelKind, model: modelText, @@ -403,6 +405,10 @@ export const EditorScreen = () => { 'transition-colors duration-500', )} onClick={() => { + if (!isContentLoaded) { + setEcho(
Waiting for content loading
); + return; + } const now = Date.now(); if (now - lastClickTime < 2000) { setEcho(
Repeated clicks
); From 3fe49c3b32cefe17c1ba73b8f4828591a4a5bea7 Mon Sep 17 00:00:00 2001 From: Coki Date: Thu, 28 Nov 2024 12:15:57 +0800 Subject: [PATCH 4/6] feat: add toast notifications for user feedback in editor component --- app/components/editor/index.tsx | 39 ++++++++------------------------- package.json | 3 ++- yarn.lock | 12 ++++++++++ 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/app/components/editor/index.tsx b/app/components/editor/index.tsx index 2c40986..7ae3709 100755 --- a/app/components/editor/index.tsx +++ b/app/components/editor/index.tsx @@ -25,6 +25,7 @@ import { useLang } from '@/app/context/LangContext'; import LanguageMenu from '@/app/components/LanguageMenu'; import { linter, lintGutter } from '@codemirror/lint'; import { casbinLinter } from '@/app/utils/casbinLinter'; +import { toast, Toaster } from 'react-hot-toast'; export const EditorScreen = () => { const { @@ -54,10 +55,6 @@ export const EditorScreen = () => { return message; }; const { t, lang, theme, toggleTheme } = useLang(); - const [lastClickTime, setLastClickTime] = useState(0); - const [lastValidResult, setLastValidResult] = useState(null); - const [lastRunClickTime, setLastRunClickTime] = useState(0); - const [lastRunResult, setLastRunResult] = useState(null); const [isContentLoaded, setIsContentLoaded] = useState(false); useEffect(() => { @@ -100,6 +97,7 @@ export const EditorScreen = () => { return (
+
{ onClick={() => { if (!isContentLoaded) { setEcho(
Waiting for content loading
); + toast.error('Waiting for content loading'); return; } - const now = Date.now(); - if (now - lastClickTime < 2000) { - setEcho(
Repeated clicks
); - setTimeout(() => { - setEcho(lastValidResult); - }, 2000); - return; - } - setLastClickTime(now); try { Config.newConfigFromText(modelText); - const result =
Passed
; - setEcho(result); - setLastValidResult(result); + setEcho(
Passed
); + toast.success('Syntax validation passed'); } catch (e) { - const result =
{(e as any).message}
; - setEcho(result); - setLastValidResult(result); + setEcho(
{(e as any).message}
); + toast.error((e as any).message); } }} > @@ -444,16 +432,6 @@ export const EditorScreen = () => { 'transition-colors duration-500', )} onClick={() => { - const now = Date.now(); - if (now - lastRunClickTime < 2000) { - setEcho(
Repeated clicks
); - setTimeout(() => { - setEcho(lastRunResult); - }, 2000); - return; - } - setLastRunClickTime(now); - return enforcer({ modelKind, model: modelText, @@ -464,7 +442,6 @@ export const EditorScreen = () => { onResponse: (v) => { if (isValidElement(v)) { setEcho(v); - setLastRunResult(v); } else if (Array.isArray(v)) { const formattedResults = v.map((res) => { if (typeof res === 'object') { @@ -477,6 +454,7 @@ export const EditorScreen = () => { }); const result = formattedResults.join('\n'); setRequestResult(result); + toast.success(`Test completed`); } }, }); @@ -527,6 +505,7 @@ export const EditorScreen = () => { () => { setShare(''); setEcho(
{t('Copied')}
); + toast.success(t('Link copied to clipboard')); }, `${window.location.origin + window.location.pathname}#${share}`, ); diff --git a/package.json b/package.json index 5cdfc2b..56f2c4b 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,8 @@ "codemirror": "^6.0.1", "next": "14.1.0", "react": "^18", - "react-dom": "^18" + "react-dom": "^18", + "react-hot-toast": "^2.4.1" }, "devDependencies": { "@types/node": "^20", diff --git a/yarn.lock b/yarn.lock index 1d78e69..b59572b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2477,6 +2477,11 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +goober@^2.1.10: + version "2.1.16" + resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.16.tgz#7d548eb9b83ff0988d102be71f271ca8f9c82a95" + integrity sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g== + gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -3584,6 +3589,13 @@ react-dom@^18: loose-envify "^1.1.0" scheduler "^0.23.2" +react-hot-toast@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.4.1.tgz#df04295eda8a7b12c4f968e54a61c8d36f4c0994" + integrity sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ== + dependencies: + goober "^2.1.10" + react-is@^16.13.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" From bcfaba4643aca92aeeb72d9ec7370f130b3f8fa0 Mon Sep 17 00:00:00 2001 From: Coki Date: Thu, 28 Nov 2024 13:43:15 +0800 Subject: [PATCH 5/6] feat: enhance editor response handling and error notifications --- app/components/editor/index.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/components/editor/index.tsx b/app/components/editor/index.tsx index 7ae3709..07b53da 100755 --- a/app/components/editor/index.tsx +++ b/app/components/editor/index.tsx @@ -439,9 +439,15 @@ export const EditorScreen = () => { customConfig, request, enforceContextData, - onResponse: (v) => { + onResponse: (v: JSX.Element | any[]) => { if (isValidElement(v)) { setEcho(v); + const props = (v as any).props; + if (props.className?.includes('text-red-500')) { + const errorMessage = props.children; + toast.error(errorMessage); + setRequestResult(errorMessage); + } } else if (Array.isArray(v)) { const formattedResults = v.map((res) => { if (typeof res === 'object') { @@ -454,7 +460,9 @@ export const EditorScreen = () => { }); const result = formattedResults.join('\n'); setRequestResult(result); - toast.success(`Test completed`); + if (result && !result.includes('error')) { + toast.success('Test completed successfully'); + } } }, }); From bd99c6584de9603116f7e9580e60bcc315d41918 Mon Sep 17 00:00:00 2001 From: Coki Date: Fri, 29 Nov 2024 01:49:42 +0800 Subject: [PATCH 6/6] refactor: remove syntax validation button and related translations --- app/components/editor/index.tsx | 30 ------------------------------ app/utils/contentExtractor.ts | 2 +- messages/ar.json | 1 - messages/de.json | 1 - messages/en.json | 1 - messages/es.json | 1 - messages/fr.json | 1 - messages/id.json | 1 - messages/it.json | 1 - messages/ja.json | 1 - messages/ko.json | 1 - messages/ms.json | 1 - messages/pt.json | 1 - messages/ru.json | 1 - messages/tr.json | 1 - messages/vi.json | 1 - messages/zh-Hant.json | 1 - messages/zh.json | 1 - 18 files changed, 1 insertion(+), 47 deletions(-) diff --git a/app/components/editor/index.tsx b/app/components/editor/index.tsx index 07b53da..cdc3064 100755 --- a/app/components/editor/index.tsx +++ b/app/components/editor/index.tsx @@ -11,7 +11,6 @@ import { go } from '@codemirror/legacy-modes/mode/go'; import { EditorView } from '@codemirror/view'; import { CasbinConfSupport } from '@/app/components/editor/casbin-mode/casbin-conf'; import { CasbinPolicySupport } from '@/app/components/editor/casbin-mode/casbin-csv'; -import { Config } from 'casbin'; import { javascriptLanguage } from '@codemirror/lang-javascript'; import useRunTest from '@/app/components/editor/hooks/useRunTest'; import useShareInfo from '@/app/components/editor/hooks/useShareInfo'; @@ -392,35 +391,6 @@ export const EditorScreen = () => {
-