Skip to content

Commit a9a4b53

Browse files
authored
feat: support opening logs from about page (#915)
* feat: support opening logs from about page * docs: update changelog * refactor: update * refactor: update i18n
1 parent 6523fef commit a9a4b53

5 files changed

Lines changed: 145 additions & 40 deletions

File tree

docs/content.en/docs/release-notes/_index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Information about release notes of Coco App is provided here.
1414
### 🚀 Features
1515

1616
feat: support switching groups via keyboard shortcuts #911
17+
feat: support opening logs from about page #915
1718

1819
### 🐛 Bug fix
1920

Lines changed: 116 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,135 @@
1-
import { Globe, Github } from "lucide-react";
1+
import {
2+
Globe,
3+
Github,
4+
Rocket,
5+
BookOpen,
6+
MessageCircleReply,
7+
ScrollText,
8+
SquareArrowOutUpRight,
9+
} from "lucide-react";
210
import { useTranslation } from "react-i18next";
311

412
import { OpenURLWithBrowser } from "@/utils";
5-
import logoLight from "@/assets/images/logo-text-light.svg";
6-
import logoDark from "@/assets/images/logo-text-dark.svg";
13+
import lightLogo from "@/assets/images/logo-text-light.svg";
14+
import darkLogo from "@/assets/images/logo-text-dark.svg";
715
import { useThemeStore } from "@/stores/themeStore";
16+
import { cloneElement, ReactElement, useMemo } from "react";
17+
import platformAdapter from "@/utils/platformAdapter";
18+
19+
interface Link {
20+
icon: ReactElement;
21+
label: string;
22+
url?: string;
23+
onPress?: () => void;
24+
}
825

926
export default function AboutView() {
1027
const { t } = useTranslation();
11-
const isDark = useThemeStore((state) => state.isDark);
28+
const { isDark } = useThemeStore();
29+
30+
const links = useMemo<Link[]>(() => {
31+
return [
32+
{
33+
icon: <Rocket />,
34+
label: t("settings.about.labels.changelog"),
35+
url: "https://coco.rs/en/roadmap",
36+
},
37+
{
38+
icon: <BookOpen />,
39+
label: t("settings.about.labels.docs"),
40+
url: "https://docs.infinilabs.com/coco-app/main",
41+
},
42+
{
43+
icon: <Github />,
44+
label: "GitHub",
45+
url: "https://github.com/infinilabs/coco-app",
46+
},
47+
{
48+
icon: <Globe />,
49+
label: t("settings.about.labels.officialWebsite"),
50+
url: "https://coco.rs",
51+
},
52+
{
53+
icon: <MessageCircleReply />,
54+
label: t("settings.about.labels.submitFeedback"),
55+
url: "https://github.com/infinilabs/coco-app/issues",
56+
},
57+
{
58+
icon: <ScrollText />,
59+
label: t("settings.about.labels.runningLog"),
60+
onPress: platformAdapter.openLogDir,
61+
},
62+
];
63+
}, [t]);
64+
65+
const handleClick = (link: Link) => {
66+
const { url, onPress } = link;
1267

13-
const logo = isDark ? logoDark : logoLight;
68+
if (url) {
69+
return OpenURLWithBrowser(url);
70+
}
71+
72+
onPress?.();
73+
};
1474

1575
return (
16-
<div className="flex justify-center items-center flex-col h-[calc(100vh-170px)]">
17-
<div>
76+
<div className="flex h-[calc(100vh-170px)]">
77+
<div className="flex flex-col items-center justify-center w-[70%] pr-10 text-[#999] text-sm">
1878
<img
19-
src={logo}
20-
className="w-48 dark:text-white"
79+
src={isDark ? darkLogo : lightLogo}
80+
className="h-14"
2181
alt={t("settings.about.logo")}
2282
/>
83+
84+
<div className="mt-4 text-base font-medium text-[#333] dark:text-white/80">
85+
{t("settings.about.slogan")}
86+
</div>
87+
88+
<div className="mt-10">
89+
{t("settings.about.version", {
90+
version: process.env.VERSION || "N/A",
91+
})}
92+
</div>
93+
94+
<div className="mt-3">
95+
{t("settings.about.copyright", { year: new Date().getFullYear() })}
96+
</div>
2397
</div>
24-
<div className="mt-8 font-medium text-gray-900 dark:text-gray-100">
25-
{t("settings.about.slogan")}
26-
</div>
27-
<div className="flex justify-center items-center mt-10">
28-
<button
29-
onClick={() => OpenURLWithBrowser("https://coco.rs")}
30-
className="w-6 h-6 mr-2.5 flex justify-center rounded-[6px] border-[1px] gray-200 dark:border-gray-700"
31-
aria-label={t("settings.about.website")}
32-
>
33-
<Globe className="w-3 text-blue-500" />
34-
</button>
35-
<button
36-
onClick={() =>
37-
OpenURLWithBrowser("https://github.com/infinilabs/coco-app")
38-
}
39-
className="w-6 h-6 flex justify-center rounded-[6px] border-[1px] gray-200 dark:border-gray-700"
40-
aria-label={t("settings.about.github")}
41-
>
42-
<Github className="w-3 text-blue-500" />
43-
</button>
44-
</div>
45-
<div className="mt-8 text-sm text-gray-500 dark:text-gray-400">
46-
{t("settings.about.version", {
47-
version: process.env.VERSION || "N/A",
98+
99+
<div className="flex-1 flex flex-col items-center justify-center gap-4 pl-10 border-l border-[#e5e5e5] dark:border-[#4e4e56]">
100+
{links.map((item) => {
101+
const { icon, label } = item;
102+
103+
return (
104+
<div
105+
key={label}
106+
className="flex items-center justify-between w-full"
107+
>
108+
<div className="flex items-center gap-2">
109+
{cloneElement(icon, {
110+
className: "size-4 text-[#999]",
111+
})}
112+
113+
<span
114+
className="text-[#333] dark:text-white/80 cursor-pointer hover:text-[#027FFE] transition"
115+
onClick={() => {
116+
handleClick(item);
117+
}}
118+
>
119+
{label}
120+
</span>
121+
</div>
122+
123+
<SquareArrowOutUpRight
124+
className="text-[#027FFE] size-4 cursor-pointer"
125+
onClick={() => {
126+
handleClick(item);
127+
}}
128+
/>
129+
</div>
130+
);
48131
})}
49132
</div>
50-
<div className="mt-4 text-sm text-gray-500 dark:text-gray-400">
51-
{t("settings.about.copyright", { year: new Date().getFullYear() })}
52-
</div>
53133
</div>
54134
);
55135
}

src/locales/en/translation.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,14 @@
3939
"website": "Visit Website",
4040
"github": "Visit GitHub",
4141
"version": "Version {{version}}",
42-
"copyright": "©{{year}} INFINI Labs, All Rights Reserved."
42+
"copyright": "©{{year}} INFINI Labs, All Rights Reserved.",
43+
"labels": {
44+
"changelog": "What’s New",
45+
"docs": "Docs",
46+
"officialWebsite": "Official Website",
47+
"submitFeedback": "Submit Feedback",
48+
"runningLog": "View App Logs"
49+
}
4350
},
4451
"advanced": {
4552
"startup": {

src/locales/zh/translation.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,14 @@
3939
"website": "访问官网",
4040
"github": "访问 GitHub",
4141
"version": "版本 {{version}}",
42-
"copyright": "©{{year}} INFINI Labs,保留所有权利。"
42+
"copyright": "©{{year}} INFINI Labs,保留所有权利。",
43+
"labels": {
44+
"changelog": "更新日志",
45+
"docs": "帮助文档",
46+
"officialWebsite": "官方网站",
47+
"submitFeedback": "提交反馈",
48+
"runningLog": "运行日志"
49+
}
4350
},
4451
"advanced": {
4552
"startup": {

src/utils/tauriAdapter.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface TauriPlatformAdapter extends BasePlatformAdapter {
2424
) => Promise<string | string[] | null>;
2525
metadata: typeof metadata;
2626
error: typeof error;
27+
openLogDir: () => Promise<void>;
2728
}
2829

2930
// Create Tauri adapter functions
@@ -277,14 +278,14 @@ export const createTauriAdapter = (): TauriPlatformAdapter => {
277278
return textarea.dispatchEvent(event);
278279
}
279280

280-
// View extension should be handled separately as it needs frontend to open
281+
// View extension should be handled separately as it needs frontend to open
281282
// a page
282283
const onOpened = data?.on_opened;
283284
if (onOpened?.Extension?.ty?.View) {
284285
const { setViewExtensionOpened } = useSearchStore.getState();
285286
const viewData = onOpened.Extension.ty.View;
286287
const extensionPermission = onOpened.Extension.permission;
287-
288+
288289
setViewExtensionOpened([viewData.page, extensionPermission]);
289290
return;
290291
}
@@ -352,5 +353,14 @@ export const createTauriAdapter = (): TauriPlatformAdapter => {
352353
const window = await windowWrapper.getWebviewWindow();
353354
return window.label;
354355
},
356+
357+
async openLogDir() {
358+
const { appLogDir } = await import("@tauri-apps/api/path");
359+
const { revealItemInDir } = await import("@tauri-apps/plugin-opener");
360+
361+
const logDir = await appLogDir();
362+
363+
revealItemInDir(logDir);
364+
},
355365
};
356366
};

0 commit comments

Comments
 (0)