Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ai gen content #33

Merged
merged 1 commit into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions scripts/wiki/template.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ id: "<%= id %>"
hide_table_of_contents: true
---

import {ArticleMeta} from '@site/src/components/article-meta'
import { ArticleMeta } from "@site/src/components/article-meta";
import { AIContent } from "@site/src/components/ai-content";

# <%= title %>

<ArticleMeta id={<%= id %>} updatedAt={'<%=contentUpdatedAt%>'} />
<div className='border-solid border-b border-t-0 my-4 border-[var(--ifm-color-gray-300)]' />

<%= jsxBody || jsxDesc %>
<%= jsxDesc %>

<AIContent content={`<%=jsxBody%>`} id={<%= id %>} />
23 changes: 14 additions & 9 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ import axios, { AxiosInstance, ResponseType } from "axios";
const defaultOptions = {
timeout: 60 * 1000,
responseType: "json" as ResponseType,
withCredentials: true
withCredentials: true,
};


const axiosInstance: AxiosInstance = axios.create(defaultOptions);
axiosInstance.interceptors.response.use(
response => {
(response) => {
const res = response.data;

// code === 0 为正常返回
Expand All @@ -24,15 +23,21 @@ axiosInstance.interceptors.response.use(
// 其它未知错误,原样返回
return Promise.reject(res);
},
error => {
(error) => {
return Promise.reject(error);
}
);
export function post(url, params = {}) {
return axiosInstance.request({
url,
method: "post",
params
}
);
url,
method: "post",
params,
});
}
export function get(url, params = {}) {
return axiosInstance.request({
url,
method: "GET",
params,
});
}
64 changes: 64 additions & 0 deletions src/components/ai-content/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import { get } from "@site/src/api";
import React, { useEffect, useState } from "react";
import { useDefaultLocale } from "@site/src/utils";

interface AIContentProps {
content?: string;
id: number;
}
const LocalesMap = {
aiGen: {
"zh-CN": "以下内容是 AI 的进一步解读",
"zh-HK": "以下內容是 AI 的進一步解讀",
en: "The following content is a further interpretation by AI.",
},
disclaimer: {
"zh-CN": "免责声明",
"zh-HK": "免責聲明",
en: "Disclaimer",
},
};
export const AIContent: React.FC<AIContentProps> = ({ content, id }) => {
const [AIContent, setAIContent] = useState(content);
const locale = useDefaultLocale() || "en";

const {
siteConfig: {
customFields: { apiProxyUrl },
},
} = useDocusaurusContext();

useEffect(() => {
if (!apiProxyUrl || !id || AIContent) return;

get(`${apiProxyUrl}/v1/social/wiki/content`, { id }).then((resp) => {
const { data } = resp;
if (data?.content) {
setAIContent(data.content);
}
});
}, [apiProxyUrl, id]);

if (!AIContent) return <></>;
return (
<div className="ai-content pt-8 rounded-lg">
<div className="flex items-center space-x-4 pb-3 mb-3 border-solid border-b border-t-0 border-r-0 border-l-0 border-[var(--ifm-color-gray-300)]">
<img
src="https://assets.lbctrl.com/uploads/831466be-e4f8-46a7-aa0b-efcda7a424ff/portai.png"
alt="port-ai"
className="w-7 h-7 rounded-full"
/>
<span className="text-sm">{LocalesMap.aiGen[locale]}</span>
</div>
<div dangerouslySetInnerHTML={{ __html: AIContent }}></div>
<a
href="https://support.longbridgewhale.com/topics/misc/portaidisclaimer"
className="text-sm text-[#37a0ff]"
target="_blank"
>
{LocalesMap.disclaimer[locale]}
</a>
</div>
);
};
90 changes: 78 additions & 12 deletions src/components/article-meta/index.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,48 @@
import React, { FC, useCallback, useEffect, useState } from "react";
import { post } from "@site/src/api";
import { post, get } from "@site/src/api";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import { translate } from "@docusaurus/Translate";
import { useDefaultLocale } from "@site/src/utils";

interface IArticleMetaProps {
updatedAt: string | null;
id: number;
}

const LocalesMap = {
peopleLearned: {
"zh-CN": "人学过",
"zh-HK": "人學過",
en: "People learned",
},
aiGen: {
"zh-CN": "AI 解读",
"zh-HK": "AI 解读",
en: "AI analysis",
},
aiGenUnderReview: {
"zh-CN": "审核中,通过后可查看新内容",
"zh-HK": "審核中,通過後可查看新內容",
en: "Under review, content will be available after approval.",
},
};

export const ArticleMeta: FC<IArticleMetaProps> = (props) => {
const { updatedAt, id } = props;
const [viewCount, setViewCount] = useState(0);
const [AIContent, setAIContent] = useState("");
const [aiGen, setAIGen] = useState(false);
const [aiGenLoading, setAIGenLoading] = useState(false);
const locale = useDefaultLocale() || "en";
// translate({
// id: "theme.docs.paginator.navAriaLabel",
// message: "Docs pages",
// description: "The ARIA label for the docs pagination"
// });
const {
siteConfig: { customFields: { apiProxyUrl } }
siteConfig: {
customFields: { apiProxyUrl },
},
} = useDocusaurusContext();

useEffect(() => {
Expand All @@ -28,21 +53,34 @@ export const ArticleMeta: FC<IArticleMetaProps> = (props) => {
if (data?.pv) {
setViewCount(data.pv);
}
if (data?.body) {
setAIContent(data.body);
}
});
}, [apiProxyUrl, id]);

const onClickCopySection = useCallback(() => {
const postAI = () => {
setAIGenLoading(true);
setAIGen(true);
get(`${apiProxyUrl}/v1/social/wiki/content`, { id })
.then((resp) => {
const { data } = resp;
})
.finally(() => {
setAIGenLoading(false);
});
};

}, []);
const onClickCopySection = useCallback(() => {}, []);

useEffect(() => {
// @ts-ignore
const clipboard = new ClipboardJS("#copy_trigger");
clipboard.on("success", function(e) {
clipboard.on("success", function (e) {
e.clearSelection();
});

clipboard.on("error", function(e) {
clipboard.on("error", function (e) {
console.error("Action:", e.action);
console.error("Trigger:", e.trigger);
});
Expand All @@ -51,17 +89,45 @@ export const ArticleMeta: FC<IArticleMetaProps> = (props) => {
return (
<div className={"article-meta flex items-center justify-between text-sm"}>
<div className="info">
{!!viewCount &&
<span className={"view-count"}>{viewCount} 人学过 .</span>
}
<span className={"updated-at"}> {translate({
id: "article.meta.updated_at"
}, { datetime: updatedAt })}</span>
{!!viewCount && (
<span className={"view-count"}>
{viewCount} {LocalesMap.peopleLearned[locale]} .
</span>
)}
<span className={"updated-at"}>
{" "}
{translate(
{
id: "article.meta.updated_at",
},
{ datetime: updatedAt }
)}
</span>
</div>
<div className="actions">
{/*<div className="copy-section" id={"copy_trigger"} onClick={onClickCopySection}>*/}
{/* <div className="copy-link" />*/}
{/*</div>*/}

{/* 没有 AI 内容并且没点击过生成 */}
{/* {!AIContent && aiGen && (
<div className="text-xs">{LocalesMap.aiGenUnderReview[locale]}</div>
)}
{!AIContent && !aiGen && (
<div className="relative group">
<span className="px-2 py-1 hover:bg-gray-200 rounded cursor-pointer flex items-center hover:text-white justify-center">
···
</span>
<div className="absolute z-[-1] opacity-0 group-hover:z-50 group-hover:opacity-100 whitespace-nowrap">
<button
className="outline-none bg-white px-2 py-1 rounded border-[var(--ifm-color-gray-300)] border-solid border hover:bg-opacity-80 cursor-pointer"
onClick={postAI}
>
{LocalesMap.aiGen[locale]}
</button>
</div>
</div>
)} */}
</div>
</div>
);
Expand Down
Loading
Loading