You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@answer.apache.org by sh...@apache.org on 2024/12/27 04:12:30 UTC
(incubator-answer) branch feat/1.4.3/ui updated (c32f99b3 -> 7a15e24f)
This is an automated email from the ASF dual-hosted git repository.
shuai pushed a change to branch feat/1.4.3/ui
in repository https://gitbox.apache.org/repos/asf/incubator-answer.git
from c32f99b3 fix: translate list view model
add b31d644b fix: Fix render comments with line breaks
add 6203b5b4 fix: use TrimSpace
add 30534ad0 feat: Check HTML comments in question, answer or comments
new 1dd7a8d7 Merge branch 'dev' into feat/1.4.3/ui
new 7a15e24f feat: add a copy button to the code block #1211
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
i18n/en_US.yaml | 8 +++
i18n/zh_CN.yaml | 9 +++-
internal/base/reason/reason.go | 3 ++
internal/schema/answer_schema.go | 14 ++++++
internal/schema/comment_schema.go | 14 ++++++
internal/schema/question_schema.go | 29 +++++++++++
pkg/converter/markdown.go | 7 +--
ui/src/components/Editor/Viewer.tsx | 7 ++-
ui/src/components/Editor/utils/index.ts | 58 +++++++++++++++++++++-
ui/src/index.scss | 21 ++++++++
ui/src/pages/Legal/Privacy/index.tsx | 5 +-
ui/src/pages/Legal/Tos/index.tsx | 5 +-
.../Questions/Detail/components/Answer/index.tsx | 9 +++-
.../Questions/Detail/components/Question/index.tsx | 5 +-
ui/src/pages/Questions/EditAnswer/index.tsx | 5 +-
.../pages/Review/components/FlagContent/index.tsx | 5 +-
.../Review/components/QueuedContent/index.tsx | 5 +-
ui/src/pages/Tags/Info/index.tsx | 5 +-
18 files changed, 199 insertions(+), 15 deletions(-)
(incubator-answer) 02/02: feat: add a copy button to the code block #1211
Posted by sh...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
shuai pushed a commit to branch feat/1.4.3/ui
in repository https://gitbox.apache.org/repos/asf/incubator-answer.git
commit 7a15e24f7bebb78d486725eabb9b4c5b6ea62968
Author: shuai <li...@sifou.com>
AuthorDate: Fri Dec 27 12:12:12 2024 +0800
feat: add a copy button to the code block #1211
---
i18n/en_US.yaml | 2 +
i18n/zh_CN.yaml | 3 +-
ui/src/components/Editor/Viewer.tsx | 7 ++-
ui/src/components/Editor/utils/index.ts | 58 +++++++++++++++++++++-
ui/src/index.scss | 21 ++++++++
ui/src/pages/Legal/Privacy/index.tsx | 5 +-
ui/src/pages/Legal/Tos/index.tsx | 5 +-
.../Questions/Detail/components/Answer/index.tsx | 9 +++-
.../Questions/Detail/components/Question/index.tsx | 5 +-
ui/src/pages/Questions/EditAnswer/index.tsx | 5 +-
.../pages/Review/components/FlagContent/index.tsx | 5 +-
.../Review/components/QueuedContent/index.tsx | 5 +-
ui/src/pages/Tags/Info/index.tsx | 5 +-
13 files changed, 123 insertions(+), 12 deletions(-)
diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml
index 0b123b33..b11ba3a6 100644
--- a/i18n/en_US.yaml
+++ b/i18n/en_US.yaml
@@ -2300,5 +2300,7 @@ ui:
user_deleted: This user has been deleted.
badge_activated: This badge has been activated.
badge_inactivated: This badge has been inactivated.
+ copy: Copy to clipboard
+ copied: Copied
diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml
index 7b32c82b..9eb0254a 100644
--- a/i18n/zh_CN.yaml
+++ b/i18n/zh_CN.yaml
@@ -2262,5 +2262,6 @@ ui:
user_deleted: 此用户已被删除
badge_activated: 此徽章已被激活。
badge_inactivated: 此徽章已被禁用。
-
+ copy: 复制
+ copied: 已复制
diff --git a/ui/src/components/Editor/Viewer.tsx b/ui/src/components/Editor/Viewer.tsx
index aedf6125..3f41831d 100644
--- a/ui/src/components/Editor/Viewer.tsx
+++ b/ui/src/components/Editor/Viewer.tsx
@@ -25,6 +25,7 @@ import {
memo,
useImperativeHandle,
} from 'react';
+import { useTranslation } from 'react-i18next';
import { markdownToHtml } from '@/services';
import ImgViewer from '@/components/ImgViewer';
@@ -37,6 +38,7 @@ let renderTimer;
const Index = ({ value }, ref) => {
const [html, setHtml] = useState('');
const previewRef = useRef<HTMLDivElement>(null);
+ const { t } = useTranslation('translation', { keyPrefix: 'messages' });
const renderMarkdown = (markdown) => {
clearTimeout(renderTimer);
@@ -59,7 +61,10 @@ const Index = ({ value }, ref) => {
previewRef.current?.scrollTo(0, scrollTop);
- htmlRender(previewRef.current);
+ htmlRender(previewRef.current, {
+ copySuccessText: t('copied', { keyPrefix: 'messages' }),
+ copyText: t('copy', { keyPrefix: 'messages' }),
+ });
}, [html]);
useImperativeHandle(ref, () => {
diff --git a/ui/src/components/Editor/utils/index.ts b/ui/src/components/Editor/utils/index.ts
index 759033cc..6c7a3ac4 100644
--- a/ui/src/components/Editor/utils/index.ts
+++ b/ui/src/components/Editor/utils/index.ts
@@ -24,6 +24,8 @@ import { EditorState, Compartment } from '@codemirror/state';
import { EditorView, placeholder } from '@codemirror/view';
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
import { languages } from '@codemirror/language-data';
+import copy from 'copy-to-clipboard';
+import Tooltip from 'bootstrap/js/dist/tooltip';
import { Editor } from '../types';
import { isDarkTheme } from '@/utils/common';
@@ -31,8 +33,16 @@ import { isDarkTheme } from '@/utils/common';
import createEditorUtils from './extension';
const editableCompartment = new Compartment();
-export function htmlRender(el: HTMLElement | null) {
+interface htmlRenderConfig {
+ copyText: string;
+ copySuccessText: string;
+}
+export function htmlRender(el: HTMLElement | null, config?: htmlRenderConfig) {
if (!el) return;
+ const { copyText = '', copySuccessText = '' } = config || {
+ copyText: 'Copy to clipboard',
+ copySuccessText: 'Copied!',
+ };
// Replace all br tags with newlines
// Fixed an issue where the BR tag in the editor block formula HTML caused rendering errors.
el.querySelectorAll('p').forEach((p) => {
@@ -69,6 +79,52 @@ export function htmlRender(el: HTMLElement | null) {
a.rel = 'nofollow';
}
});
+
+ // Add copy button to all pre tags
+ el.querySelectorAll('pre').forEach((pre) => {
+ // Create copy button
+ const codeWrap = document.createElement('div');
+ codeWrap.className = 'position-relative a-code-wrap';
+ const codeTool = document.createElement('div');
+ codeTool.className = 'a-code-tool';
+ const uniqueId = `a-copy-code-${Date.now().toString().substring(5)}-${Math.floor(Math.random() * 10)}${Math.floor(Math.random() * 10)}${Math.floor(Math.random() * 10)}`;
+ const str = `
+ <button type="button" class="btn btn-dark rounded-0 a-copy-code" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="${copyText}" id="${uniqueId}">
+ <i class="br bi-copy"></i>
+ </button>
+ `;
+ codeTool.innerHTML = str;
+
+ // Add copy button to pre tag
+ pre.style.position = 'relative';
+
+ // 将 codeTool 和 pre 插入到 codeWrap 中, 并且使用 codeWrap 替换 pre
+ codeWrap.appendChild(codeTool);
+ pre.parentNode?.replaceChild(codeWrap, pre);
+ codeWrap.appendChild(pre);
+
+ const tooltipTriggerList = el.querySelectorAll('.a-copy-code');
+
+ console.log('tooltipTriggerList', Array.from(tooltipTriggerList).length);
+ Array.from(tooltipTriggerList)?.map(
+ (tooltipTriggerEl) => new Tooltip(tooltipTriggerEl),
+ );
+
+ // Copy pre content on button click
+ const copyBtn = codeTool.querySelector('.a-copy-code');
+ copyBtn?.addEventListener('click', () => {
+ const textToCopy = pre.textContent || '';
+ copy(textToCopy);
+ // Change tooltip text on copy success
+ const tooltipInstance = Tooltip.getOrCreateInstance(`#${uniqueId}`);
+ tooltipInstance?.setContent({ '.tooltip-inner': copySuccessText });
+ const myTooltipEl = document.querySelector(`#${uniqueId}`);
+ myTooltipEl?.addEventListener('hidden.bs.tooltip', () => {
+ console.log('hidden.bs.tooltip');
+ tooltipInstance.setContent({ '.tooltip-inner': copyText });
+ });
+ });
+ });
}
export const useEditor = ({
diff --git a/ui/src/index.scss b/ui/src/index.scss
index bcbf3c7d..dfbe6094 100644
--- a/ui/src/index.scss
+++ b/ui/src/index.scss
@@ -374,3 +374,24 @@ img[src=""] {
.mb-12 {
margin-bottom: 12px;
}
+
+.a-code-wrap:hover .a-code-tool {
+ display: block;
+}
+.a-code-tool {
+ position: absolute;
+ top: 0;
+ right: 0;
+ font-size: 16px;
+ z-index: 1;
+ display: none;
+ .a-copy-code {
+ line-height: 24px;
+ width: 1.5rem;
+ height: 1.5rem;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 0;
+ }
+}
diff --git a/ui/src/pages/Legal/Privacy/index.tsx b/ui/src/pages/Legal/Privacy/index.tsx
index 326c3730..5454a415 100644
--- a/ui/src/pages/Legal/Privacy/index.tsx
+++ b/ui/src/pages/Legal/Privacy/index.tsx
@@ -38,7 +38,10 @@ const Index: FC = () => {
if (!fmt) {
return;
}
- htmlRender(fmt);
+ htmlRender(fmt, {
+ copySuccessText: t('copied', { keyPrefix: 'messages' }),
+ copyText: t('copy', { keyPrefix: 'messages' }),
+ });
}, [privacy?.privacy_policy_parsed_text]);
try {
diff --git a/ui/src/pages/Legal/Tos/index.tsx b/ui/src/pages/Legal/Tos/index.tsx
index fd9b5045..6e996546 100644
--- a/ui/src/pages/Legal/Tos/index.tsx
+++ b/ui/src/pages/Legal/Tos/index.tsx
@@ -38,7 +38,10 @@ const Index: FC = () => {
if (!fmt) {
return;
}
- htmlRender(fmt);
+ htmlRender(fmt, {
+ copySuccessText: t('copied', { keyPrefix: 'messages' }),
+ copyText: t('copy', { keyPrefix: 'messages' }),
+ });
}, [tos?.terms_of_service_parsed_text]);
try {
diff --git a/ui/src/pages/Questions/Detail/components/Answer/index.tsx b/ui/src/pages/Questions/Detail/components/Answer/index.tsx
index 931b1a75..825f698d 100644
--- a/ui/src/pages/Questions/Detail/components/Answer/index.tsx
+++ b/ui/src/pages/Questions/Detail/components/Answer/index.tsx
@@ -76,8 +76,13 @@ const Index: FC<Props> = ({
return;
}
- htmlRender(answerRef.current.querySelector('.fmt'));
+ htmlRender(answerRef.current.querySelector('.fmt'), {
+ copySuccessText: t('copied', { keyPrefix: 'messages' }),
+ copyText: t('copy', { keyPrefix: 'messages' }),
+ });
+ }, [answerRef.current]);
+ useEffect(() => {
if (aid === data.id) {
setTimeout(() => {
const element = answerRef.current;
@@ -87,7 +92,7 @@ const Index: FC<Props> = ({
}
}, 100);
}
- }, [data.id, answerRef.current]);
+ }, [data.id]);
if (!data?.id) {
return null;
diff --git a/ui/src/pages/Questions/Detail/components/Question/index.tsx b/ui/src/pages/Questions/Detail/components/Question/index.tsx
index 0a76c2f9..081c8759 100644
--- a/ui/src/pages/Questions/Detail/components/Question/index.tsx
+++ b/ui/src/pages/Questions/Detail/components/Question/index.tsx
@@ -79,7 +79,10 @@ const Index: FC<Props> = ({ data, initPage, hasAnswer, isLogged }) => {
return;
}
- htmlRender(ref.current);
+ htmlRender(ref.current, {
+ copySuccessText: t('copied', { keyPrefix: 'messages' }),
+ copyText: t('copy', { keyPrefix: 'messages' }),
+ });
}, [ref.current]);
if (!data?.id) {
diff --git a/ui/src/pages/Questions/EditAnswer/index.tsx b/ui/src/pages/Questions/EditAnswer/index.tsx
index 831aa5a2..3be7befc 100644
--- a/ui/src/pages/Questions/EditAnswer/index.tsx
+++ b/ui/src/pages/Questions/EditAnswer/index.tsx
@@ -96,7 +96,10 @@ const Index = () => {
if (!questionContentRef?.current) {
return;
}
- htmlRender(questionContentRef.current);
+ htmlRender(questionContentRef.current, {
+ copySuccessText: t('copied', { keyPrefix: 'messages' }),
+ copyText: t('copy', { keyPrefix: 'messages' }),
+ });
}, [questionContentRef]);
usePromptWithUnload({
diff --git a/ui/src/pages/Review/components/FlagContent/index.tsx b/ui/src/pages/Review/components/FlagContent/index.tsx
index 44ac1aeb..95bdc85e 100644
--- a/ui/src/pages/Review/components/FlagContent/index.tsx
+++ b/ui/src/pages/Review/components/FlagContent/index.tsx
@@ -120,7 +120,10 @@ const Index: FC<IProps> = ({ refreshCount }) => {
}
setTimeout(() => {
- htmlRender(ref.current);
+ htmlRender(ref.current, {
+ copySuccessText: t('copied', { keyPrefix: 'messages' }),
+ copyText: t('copy', { keyPrefix: 'messages' }),
+ });
}, 70);
}, [ref.current]);
diff --git a/ui/src/pages/Review/components/QueuedContent/index.tsx b/ui/src/pages/Review/components/QueuedContent/index.tsx
index f8fa36d5..66470e5f 100644
--- a/ui/src/pages/Review/components/QueuedContent/index.tsx
+++ b/ui/src/pages/Review/components/QueuedContent/index.tsx
@@ -87,7 +87,10 @@ const Index: FC<IProps> = ({ refreshCount }) => {
}
setTimeout(() => {
- htmlRender(ref.current);
+ htmlRender(ref.current, {
+ copySuccessText: t('copied', { keyPrefix: 'messages' }),
+ copyText: t('copy', { keyPrefix: 'messages' }),
+ });
}, 70);
}, [ref.current]);
diff --git a/ui/src/pages/Tags/Info/index.tsx b/ui/src/pages/Tags/Info/index.tsx
index 7c620715..1c32e249 100644
--- a/ui/src/pages/Tags/Info/index.tsx
+++ b/ui/src/pages/Tags/Info/index.tsx
@@ -80,7 +80,10 @@ const TagIntroduction = () => {
if (!fmt) {
return;
}
- htmlRender(fmt);
+ htmlRender(fmt, {
+ copySuccessText: t('copied', { keyPrefix: 'messages' }),
+ copyText: t('copy', { keyPrefix: 'messages' }),
+ });
}, [tagInfo?.parsed_text]);
if (!tagInfo) {
(incubator-answer) 01/02: Merge branch 'dev' into feat/1.4.3/ui
Posted by sh...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
shuai pushed a commit to branch feat/1.4.3/ui
in repository https://gitbox.apache.org/repos/asf/incubator-answer.git
commit 1dd7a8d7392e6ac46f4873dc80471008525e8f52
Merge: c32f99b3 30534ad0
Author: shuai <li...@sifou.com>
AuthorDate: Wed Dec 25 09:58:08 2024 +0800
Merge branch 'dev' into feat/1.4.3/ui
i18n/en_US.yaml | 6 ++++++
i18n/zh_CN.yaml | 6 ++++++
internal/base/reason/reason.go | 3 +++
internal/schema/answer_schema.go | 14 ++++++++++++++
internal/schema/comment_schema.go | 14 ++++++++++++++
internal/schema/question_schema.go | 29 +++++++++++++++++++++++++++++
pkg/converter/markdown.go | 7 ++++---
7 files changed, 76 insertions(+), 3 deletions(-)