diff --git a/src/App.jsx b/src/App.jsx
index 16c7390..0cfe1a4 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,216 +1,258 @@
-import React, { useState, useEffect } from 'react';
-import { Globe, ChevronDown } from 'lucide-react';
+import React, {useState, useEffect} from 'react';
+import {Globe, ChevronDown} from 'lucide-react';
import ReactMarkdown from 'react-markdown';
const LegalDocuments = () => {
- const [currentDoc, setCurrentDoc] = useState('terms');
- const [language, setLanguage] = useState('zh-CN');
- const [showLangMenu, setShowLangMenu] = useState(false);
- const [hideHeader, setHideHeader] = useState(true);
- const [content, setContent] = useState('');
- const [loading, setLoading] = useState(true);
+ const [currentDoc, setCurrentDoc] = useState('terms');
+ const [language, setLanguage] = useState('zh-CN');
+ const [showLangMenu, setShowLangMenu] = useState(false);
+ const [hideHeader, setHideHeader] = useState(true);
+ const [content, setContent] = useState('');
+ const [loading, setLoading] = useState(true);
+ const [initialized, setInitialized] = useState(false); // 添加初始化标志
- // 动态导入 Markdown 文件
- useEffect(() => {
- const loadMarkdown = async () => {
- setLoading(true);
- try {
- const docPath = currentDoc === 'terms' ? 'terms' : 'privacy';
- const response = await import(`./data/${docPath}/${language}.md?raw`);
- setContent(response.default);
- } catch (error) {
- console.error('Error loading markdown:', error);
- setContent('内容加载失败,请稍后重试。');
- } finally {
- setLoading(false);
- }
- };
+ // 解析URL并设置初始状态(优先执行)
+ useEffect(() => {
+ const searchParams = new URLSearchParams(window.location.search);
+ const docType = searchParams.get('content');
+ const lang = searchParams.get('language');
- loadMarkdown();
- }, [currentDoc, language]);
+ if (docType === null && lang === null) {
+ setHideHeader(false);
+ return;
+ }
- // 解析URL并设置初始状态
- // 解析 URL query 并设置初始状态
- useEffect(() => {
- const searchParams = new URLSearchParams(window.location.search);
- const docType = searchParams.get('content') ;
- const lang = searchParams.get('language') ;
-
-
- console.log(lang);
- if (docType === null && lang === null) {
- setHideHeader(false);
- return;
- }
-
- // 映射文档类型
- const docMap = {
- 'user-agreement': 'terms',
- 'terms': 'terms',
- 'privacy-policy': 'privacy',
- 'privacy': 'privacy'
- };
-
- // 映射语言类型
- const langMap = {
- 'zh': 'zh-CN',
- 'zh-FT': 'zh-TW',
- 'en': 'en',
- 'ms': 'ms',
- }
-
- if (docMap[docType.toLowerCase()]) {
- setCurrentDoc(docMap[docType.toLowerCase()]);
- }
-
- if (langMap[lang]) {
- setLanguage(langMap[lang]);
- }
-
- if (docMap[docType.toLowerCase()] && langMap[lang]) {
- setHideHeader(true);
- } else {
- setHideHeader(false);
- }
- }, []);
-
- const languages = {
- 'zh-CN': '简体中文',
- 'zh-TW': '繁體中文',
- 'en': 'English',
- 'ms': 'Bahasa Melayu'
+ // 映射文档类型
+ const docMap = {
+ 'user-agreement': 'terms',
+ 'terms': 'terms',
+ 'privacy-policy': 'privacy',
+ 'osn': 'osn',
};
- const navItems = {
- 'zh-CN': { terms: '用户协议', privacy: '隐私政策' },
- 'zh-TW': { terms: '用戶協議', privacy: '隱私政策' },
- 'en': { terms: 'Terms', privacy: 'Privacy' },
- 'ms': { terms: 'Terma', privacy: 'Privasi' }
+ // 映射语言类型
+ const langMap = {
+ 'zh': 'zh-CN',
+ 'zh-FT': 'zh-TW',
+ 'en': 'en',
+ 'ms': 'ms',
};
- const lastUpdated = {
- 'zh-CN': '最后更新时间',
- 'zh-TW': '最後更新時間',
- 'en': 'Last Updated',
- 'ms': 'Kemaskini Terakhir'
+ if (docType && docMap[docType.toLowerCase()]) {
+ setCurrentDoc(docMap[docType.toLowerCase()]);
+ }
+
+ if (lang && langMap[lang]) {
+ setLanguage(langMap[lang]);
+ }
+
+ if (
+ docType &&
+ lang &&
+ docMap[docType.toLowerCase()] &&
+ langMap[lang]
+ ) {
+ setHideHeader(true);
+ } else {
+ setHideHeader(false);
+ }
+
+ // 标记初始化完成
+ setInitialized(true);
+ }, []);
+
+ // 动态导入 Markdown 文件(只在初始化完成后执行)
+ useEffect(() => {
+ if (!initialized) return; // 等待 URL 解析完成
+
+ const loadMarkdown = async () => {
+ setLoading(true);
+ try {
+ const response = await import(`./data/${currentDoc}/${language}.md?raw`);
+ setContent(response.default);
+ } catch (error) {
+ console.error('Error loading markdown:', error);
+ setContent('内容加载失败,请稍后重试。');
+ } finally {
+ setLoading(false);
+ }
};
- const titles = {
- terms: {
- 'zh-CN': '用户协议',
- 'zh-TW': '用戶協議',
- 'en': 'Terms of Service',
- 'ms': 'Perjanjian Pengguna'
- },
- privacy: {
- 'zh-CN': '隐私政策',
- 'zh-TW': '隱私政策',
- 'en': 'Privacy Policy',
- 'ms': 'Dasar Privasi'
- }
- };
+ loadMarkdown();
+ }, [currentDoc, language, initialized]);
- return (
-
- {/* Header - 根据 hideHeader 状态决定是否显示 */}
- {!hideHeader && (
-
-
-
- {/* Logo */}
-
-
- S
-
-
+ const languages = {
+ 'zh-CN': '简体中文',
+ 'zh-TW': '繁體中文',
+ 'en': 'English',
+ 'ms': 'Bahasa Melayu',
+ };
+
+ const navItems = {
+ 'zh-CN': {
+ terms: '用户协议',
+ privacy: '隐私政策',
+ osn: '开源组件说明',
+ },
+ 'zh-TW': {
+ terms: '用戶協議',
+ privacy: '隱私政策',
+ osn: '開源組件說明',
+ },
+ 'en': {
+ terms: 'Terms',
+ privacy: 'Privacy',
+ osn: 'Open Source Notices',
+ },
+ 'ms': {
+ terms: 'Terma',
+ privacy: 'Privasi',
+ osn: 'Notis Sumber Terbuka',
+ },
+ };
+
+ const lastUpdated = {
+ 'zh-CN': '最后更新时间',
+ 'zh-TW': '最後更新時間',
+ 'en': 'Last Updated',
+ 'ms': 'Kemaskini Terakhir',
+ };
+
+ const titles = {
+ terms: {
+ 'zh-CN': '用户协议',
+ 'zh-TW': '用戶協議',
+ 'en': 'Terms of Service',
+ 'ms': 'Perjanjian Pengguna',
+ },
+ privacy: {
+ 'zh-CN': '隐私政策',
+ 'zh-TW': '隱私政策',
+ 'en': 'Privacy Policy',
+ 'ms': 'Dasar Privasi',
+ },
+ osn: {
+ 'zh-CN': '第三方开源组件与资源声明',
+ 'zh-TW': '第三方開源組件與資源聲明',
+ 'en': 'Third-Party Open Source Components and Resources Notice',
+ 'ms': 'Notis Komponen dan Sumber Sumber Terbuka Pihak Ketiga',
+ },
+ };
+
+
+ return (
+
+ {/* Header - 根据 hideHeader 状态决定是否显示 */}
+ {!hideHeader && (
+
+
+
- {/* Navigation */}
-
- setCurrentDoc('terms')}
- className={`px-3 sm:px-4 py-2 rounded-lg text-sm sm:text-base font-medium transition-all ${
- currentDoc === 'terms'
- ? 'bg-gray-900 text-white shadow-lg'
- : 'text-gray-600 hover:bg-gray-100'
- }`}
- >
- {navItems[language].terms}
-
- setCurrentDoc('privacy')}
- className={`px-3 sm:px-4 py-2 rounded-lg text-sm sm:text-base font-medium transition-all ${
- currentDoc === 'privacy'
- ? 'bg-gray-900 text-white shadow-lg'
- : 'text-gray-600 hover:bg-gray-100'
- }`}
- >
- {navItems[language].privacy}
-
+ {/* Navigation */}
+
+ setCurrentDoc('terms')}
+ className={`px-3 sm:px-4 py-2 rounded-lg text-sm sm:text-base font-medium transition-all ${
+ currentDoc === 'terms'
+ ? 'bg-gray-900 text-white shadow-lg'
+ : 'text-gray-600 hover:bg-gray-100'
+ }`}
+ >
+ {navItems[language].terms}
+
+ setCurrentDoc('privacy')}
+ className={`px-3 sm:px-4 py-2 rounded-lg text-sm sm:text-base font-medium transition-all ${
+ currentDoc === 'privacy'
+ ? 'bg-gray-900 text-white shadow-lg'
+ : 'text-gray-600 hover:bg-gray-100'
+ }`}
+ >
+ {navItems[language].privacy}
+
+ setCurrentDoc('osn')}
+ className={`px-3 sm:px-4 py-2 rounded-lg text-sm sm:text-base font-medium transition-all ${
+ currentDoc === 'osn'
+ ? 'bg-gray-900 text-white shadow-lg'
+ : 'text-gray-600 hover:bg-gray-100'
+ }`}
+ >
+ {navItems[language].osn}
+
- {/* Language Selector */}
-
-
setShowLangMenu(!showLangMenu)}
- className="flex items-center space-x-1 sm:space-x-2 px-2 sm:px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-100 transition-all"
- >
-
-
+ {/* Language Selector */}
+
+
setShowLangMenu(!showLangMenu)}
+ className="flex items-center space-x-1 sm:space-x-2 px-2 sm:px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-100 transition-all"
+ >
+
+
{languages[language]}
-
-
+
+
- {showLangMenu && (
-
- {Object.entries(languages).map(([code, name]) => (
- {
- setLanguage(code);
- setShowLangMenu(false);
- }}
- className={`w-full text-left px-4 py-2.5 text-sm transition-colors ${
- language === code
- ? 'bg-blue-50 text-blue-600 font-medium'
- : 'text-gray-700 hover:bg-gray-50'
- }`}
- >
- {name}
-
- ))}
-
- )}
-
-
-
+ {showLangMenu && (
+
+ {Object.entries(languages).map(([code, name]) => (
+ {
+ setLanguage(code);
+ setShowLangMenu(false);
+ }}
+ className={`w-full text-left px-4 py-2.5 text-sm transition-colors ${
+ language === code
+ ? 'bg-blue-50 text-blue-600 font-medium'
+ : 'text-gray-700 hover:bg-gray-50'
+ }`}
+ >
+ {name}
+
+ ))}
-
- )}
+ )}
+
+
+
+
+
+ )}
- {/* Main Content */}
-
-
- {/* Title */}
-
-
- {titles[currentDoc][language]}
-
-
- {lastUpdated[language]}:
- 2025-12-01
-
-
+ {/* Main Content */}
+
+
+ {/* Title */}
+
+
+ {titles[currentDoc][language]}
+
+
+ {lastUpdated[language]}:
+ 2025-12-01
+
+
- {/* Content */}
- {loading ? (
-
- ) : (
-
+ ) : (
+
-
,
- h1: ({node, ...props}) => ,
- h2: ({node, ...props}) => ,
- h3: ({node, ...props}) => ,
- ul: ({node, ...props}) => ,
- ol: ({node, ...props}) => ,
- }}
- >
- {content}
-
-
- )}
-
+
,
+ h1: ({node, ...props}) => ,
+ h2: ({node, ...props}) => ,
+ h3: ({node, ...props}) => ,
+ ul: ({node, ...props}) => ,
+ ol: ({node, ...props}) => ,
+ code: ({node, ...props}) => ,
- {/* Footer */}
-
-
+ // code: ({node, inline, ...props}) =>
+ // inline ? (
+ //
+ // ) : (
+ //
+ // ),
+ }}
+ >
+ {content}
+
+
+ )}
+
- {/* Click outside to close language menu */}
- {showLangMenu && (
-
setShowLangMenu(false)}
- />
- )}
-
- );
+ {/* Footer */}
+
+
+
+ {/* Click outside to close language menu */}
+ {showLangMenu && (
+
setShowLangMenu(false)}
+ />
+ )}
+
+ );
};
export default LegalDocuments;
\ No newline at end of file
diff --git a/src/data/osn/en.md b/src/data/osn/en.md
new file mode 100644
index 0000000..d9de45c
--- /dev/null
+++ b/src/data/osn/en.md
@@ -0,0 +1,13 @@
+**License**: Apache License 2.0
+
+> **Attribution Requirement**: The animated emoji resources used in this project are from the Google Noto Emoji Animation project,
+
+> protected under the Apache License 2.0. When using these resources, please retain the following attribution information:
+:
+>
+> ```
+> Animated emoji provided by Google Noto Emoji Animation
+>
+> https://googlefonts.github.io/noto-emoji-animation/
+>
+> Licensed under Apache License 2.0
diff --git a/src/data/osn/ms.md b/src/data/osn/ms.md
new file mode 100644
index 0000000..def5f5a
--- /dev/null
+++ b/src/data/osn/ms.md
@@ -0,0 +1,13 @@
+**Lesen**: Apache License 2.0
+
+> **Keperluan Pencatatan Nama**: Sumber emoji animasi yang digunakan dalam projek ini berasal dari projek Google Noto Emoji Animation
+,
+> dilindungi di bawah Lesen Apache License 2.0. Apabila menggunakan sumber ini, sila simpan maklumat pencatatan nama berikut:
+
+>
+> ```
+> Animated emoji provided by Google Noto Emoji Animation
+>
+> https://googlefonts.github.io/noto-emoji-animation/
+>
+> Licensed under Apache License 2.0
diff --git a/src/data/osn/zh-CN.md b/src/data/osn/zh-CN.md
new file mode 100644
index 0000000..23e76f1
--- /dev/null
+++ b/src/data/osn/zh-CN.md
@@ -0,0 +1,11 @@
+**许可证**: Apache License 2.0
+
+> **署名要求**: 本项目使用的动画表情资源来自 Google Noto Emoji Animation 项目,
+> 受 Apache License 2.0 许可证保护。使用时需保留以下署名信息:
+>
+> ```
+> Animated emoji provided by Google Noto Emoji Animation
+>
+> https://googlefonts.github.io/noto-emoji-animation/
+>
+> Licensed under Apache License 2.0
diff --git a/src/data/osn/zh-TW.md b/src/data/osn/zh-TW.md
new file mode 100644
index 0000000..8af1fc7
--- /dev/null
+++ b/src/data/osn/zh-TW.md
@@ -0,0 +1,13 @@
+**授權條款**: Apache License 2.0
+
+> **署名要求**: 本專案使用的動畫表情資源來自 Google Noto Emoji Animation 專案
+,
+> 受 Apache License 2.0 授權條款保護。使用時需保留以下署名資訊:
+:
+>
+> ```
+> Animated emoji provided by Google Noto Emoji Animation
+>
+> https://googlefonts.github.io/noto-emoji-animation/
+>
+> Licensed under Apache License 2.0