Skip to content

Commit 905a7e5

Browse files
authored
Merge pull request #7 from N3koSempai/start
[release] github/gitlab start in appdetails
2 parents 6fe92d3 + 75722c7 commit 905a7e5

12 files changed

Lines changed: 602 additions & 28 deletions

io.github.N3kosempai.klia-store.metainfo.xml

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,16 @@
3737
<binary>klia-store</binary>
3838
</provides>
3939
<releases>
40-
<release version="2.0.3" date="2025-12-02">
40+
<release version="2.1.0" date="2025-12-06">
4141
<description>
42-
<p>Developer profiles and UI improvements:</p>
42+
<p>New features:</p>
4343
<ul>
44-
<li>Added developer profile pages - click on a developer name to view all their apps</li>
45-
<li>Improved developer name extraction from app IDs for better accuracy</li>
46-
<li>Redesigned developer badge with blue accent color and user icon for better visibility</li>
47-
<li>Version information moved to footer as technical metadata</li>
48-
<li>Automatic fallback to correct developer name if initial search returns no results</li>
44+
<li>View GitHub and GitLab star counts directly on app details pages</li>
45+
<li>Visual badges with different styles based on popularity (Indie/Solid/Epic/Legendary)</li>
46+
<li>Click on star badges to open the project repository in your browser</li>
47+
<li>Works with apps from GitHub, GitLab, and GNOME GitLab</li>
4948
</ul>
5049
</description>
5150
</release>
52-
<release version="2.0.2" date="2025-11-27">
53-
<description>
54-
<p>Navigation and scroll restoration improvements:</p>
55-
<ul>
56-
<li>Fixed category apps navigation to properly redirect to app details page</li>
57-
<li>Implemented scroll position restoration when navigating back from app details</li>
58-
<li>Improved navigation stack system to prevent view overlapping issues</li>
59-
</ul>
60-
</description>
61-
</release>
62-
</releases>
6351
<content_rating type="oars-1.1"/>
6452
</component>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "klia-store",
33
"private": true,
4-
"version": "2.0.3",
4+
"version": "2.1.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "klia-store"
3-
version = "2.0.3"
3+
version = "2.1.0"
44
description = "A Tauri App"
55
authors = ["you"]
66
edition = "2021"

src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://schema.tauri.app/config/2",
33
"productName": "klia-store",
4-
"version": "2.0.3",
4+
"version": "2.1.0",
55
"identifier": "io.github.N3kosempai.klia-store",
66
"build": {
77
"beforeDevCommand": "npm run dev",

src/components/GitHubStarBadge.tsx

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import AutoAwesomeIcon from "@mui/icons-material/AutoAwesome";
2+
import StarBorderRoundedIcon from "@mui/icons-material/StarBorderRounded";
3+
import StarRoundedIcon from "@mui/icons-material/StarRounded";
4+
import { Box, type SxProps, type Theme } from "@mui/material";
5+
import { openUrl } from "@tauri-apps/plugin-opener";
6+
import type React from "react";
7+
8+
interface GitHubStarBadgeProps {
9+
count: number;
10+
url: string;
11+
}
12+
13+
type TierStyle = {
14+
icon: React.ReactElement;
15+
containerSx: SxProps<Theme>;
16+
textSx: SxProps<Theme>;
17+
iconSx: SxProps<Theme>;
18+
};
19+
20+
const getTierStyle = (count: number): TierStyle => {
21+
// Legendary (> 6k)
22+
if (count > 6000) {
23+
return {
24+
icon: <AutoAwesomeIcon />,
25+
containerSx: {
26+
border: "1px solid rgba(255, 215, 0, 0.5)",
27+
background:
28+
"linear-gradient(90deg, rgba(255,215,0,0.1), rgba(255,69,0,0.15))",
29+
animation: "pulse 2s ease-in-out infinite",
30+
"@keyframes pulse": {
31+
"0%, 100%": {
32+
transform: "scale(1)",
33+
},
34+
"50%": {
35+
transform: "scale(1.05)",
36+
},
37+
},
38+
},
39+
textSx: {
40+
background: "linear-gradient(135deg, #FFD700 0%, #FF4500 100%)",
41+
WebkitBackgroundClip: "text",
42+
WebkitTextFillColor: "transparent",
43+
backgroundClip: "text",
44+
},
45+
iconSx: {
46+
background: "linear-gradient(135deg, #FFD700 0%, #FF4500 100%)",
47+
WebkitBackgroundClip: "text",
48+
WebkitTextFillColor: "transparent",
49+
backgroundClip: "text",
50+
},
51+
};
52+
}
53+
54+
// Epic (1k - 6k)
55+
if (count >= 1000) {
56+
return {
57+
icon: <StarRoundedIcon />,
58+
containerSx: {
59+
border: "1px solid #FFB11B",
60+
bgcolor: "rgba(255, 177, 27, 0.15)",
61+
boxShadow: "0 0 12px rgba(255, 177, 27, 0.4)",
62+
},
63+
textSx: {
64+
color: "#FFB11B",
65+
},
66+
iconSx: {
67+
color: "#FFB11B",
68+
},
69+
};
70+
}
71+
72+
// Solid (100 - 1k)
73+
if (count >= 100) {
74+
return {
75+
icon: <StarRoundedIcon />,
76+
containerSx: {
77+
border: "1px solid rgba(246, 211, 45, 0.3)",
78+
bgcolor: "rgba(246, 211, 45, 0.1)",
79+
},
80+
textSx: {
81+
color: "#E6EDF3",
82+
},
83+
iconSx: {
84+
color: "#F6D32D",
85+
},
86+
};
87+
}
88+
89+
// Indie (< 100)
90+
return {
91+
icon: <StarBorderRoundedIcon />,
92+
containerSx: {
93+
border: "1px solid rgba(255, 255, 255, 0.1)",
94+
bgcolor: "transparent",
95+
},
96+
textSx: {
97+
color: "#8B949E",
98+
},
99+
iconSx: {
100+
color: "#8B949E",
101+
},
102+
};
103+
};
104+
105+
export const GitHubStarBadge = ({ count, url }: GitHubStarBadgeProps) => {
106+
const formatStars = (starCount: number): string => {
107+
if (starCount >= 1000) {
108+
return `${(starCount / 1000).toFixed(1)}k`;
109+
}
110+
return starCount.toString();
111+
};
112+
113+
const handleClick = async () => {
114+
try {
115+
await openUrl(url);
116+
} catch (error) {
117+
console.error("Error opening repository URL:", error);
118+
}
119+
};
120+
121+
const tierStyle = getTierStyle(count);
122+
123+
return (
124+
<Box
125+
onClick={handleClick}
126+
sx={{
127+
display: "inline-flex",
128+
alignItems: "center",
129+
gap: 0.75,
130+
px: 1.5,
131+
py: 0.5,
132+
borderRadius: 1.5,
133+
cursor: "pointer",
134+
transition: "all 0.3s ease",
135+
fontFamily: "JetBrains Mono, monospace",
136+
fontWeight: 700,
137+
fontSize: "0.875rem",
138+
"&:hover": {
139+
transform: "translateY(-2px)",
140+
opacity: 0.9,
141+
},
142+
...tierStyle.containerSx,
143+
}}
144+
>
145+
<Box
146+
component="span"
147+
sx={{
148+
display: "flex",
149+
alignItems: "center",
150+
fontSize: "1rem",
151+
...tierStyle.iconSx,
152+
}}
153+
>
154+
{tierStyle.icon}
155+
</Box>
156+
<Box component="span" sx={tierStyle.textSx}>
157+
{formatStars(count)}
158+
</Box>
159+
</Box>
160+
);
161+
};

src/hooks/useAppScreenshots.ts

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import { useEffect, useState } from "react";
22
import { apiService } from "../services/api";
33
import type { AppStream } from "../types";
44
import { imageCacheManager } from "../utils/imageCache";
5+
import { hasAnyValidRepoUrl } from "../utils/repoValidation";
56

67
interface UseAppScreenshotsReturn {
78
screenshots: AppStream["screenshots"];
9+
urls: AppStream["urls"];
810
isLoading: boolean;
911
error: Error | null;
1012
}
@@ -13,15 +15,35 @@ export const useAppScreenshots = (app: AppStream): UseAppScreenshotsReturn => {
1315
const [screenshots, setScreenshots] = useState<AppStream["screenshots"]>(
1416
app.screenshots,
1517
);
18+
const [urls, setUrls] = useState<AppStream["urls"]>(app.urls);
1619
const [isLoading, setIsLoading] = useState(false);
1720
const [error, setError] = useState<Error | null>(null);
1821

1922
useEffect(() => {
2023
let isMounted = true;
2124

2225
const loadScreenshots = async () => {
23-
// Si ya tenemos screenshots, no necesitamos hacer nada
24-
if (app.screenshots && app.screenshots.length > 0) {
26+
// Update URLs from app.urls if they exist
27+
if (app.urls && isMounted) {
28+
setUrls(app.urls);
29+
}
30+
31+
// Check if we need to fetch data
32+
const hasScreenshots = app.screenshots && app.screenshots.length > 0;
33+
const hasValidUrls = hasAnyValidRepoUrl(app.urls);
34+
const hasAnyUrls =
35+
app.urls &&
36+
(app.urls.vcs_browser ||
37+
app.urls.bugtracker ||
38+
app.urls.contribute ||
39+
app.urls.help);
40+
41+
// Determine if we need to fetch
42+
const needsScreenshots = !hasScreenshots;
43+
const needsUrls = !hasValidUrls && !hasAnyUrls; // Only if we don't have any URLs at all
44+
45+
// If we don't need screenshots AND we don't need URLs, no need to fetch
46+
if (!needsScreenshots && !needsUrls) {
2547
return;
2648
}
2749

@@ -61,6 +83,16 @@ export const useAppScreenshots = (app: AppStream): UseAppScreenshotsReturn => {
6183

6284
if (foundInCache && isMounted) {
6385
setScreenshots(cachedScreenshots);
86+
87+
// If we found screenshots in cache but still need URLs, fetch them
88+
if (needsUrls) {
89+
const appStreamData = await apiService.getAppStream(app.id);
90+
91+
if (isMounted) {
92+
setUrls(appStreamData.urls);
93+
}
94+
}
95+
6496
setIsLoading(false);
6597
return;
6698
}
@@ -70,6 +102,7 @@ export const useAppScreenshots = (app: AppStream): UseAppScreenshotsReturn => {
70102

71103
if (isMounted) {
72104
setScreenshots(appStreamData.screenshots);
105+
setUrls(appStreamData.urls);
73106
setIsLoading(false);
74107
}
75108
} catch (err) {
@@ -90,5 +123,5 @@ export const useAppScreenshots = (app: AppStream): UseAppScreenshotsReturn => {
90123
};
91124
}, [app.id, app.screenshots]);
92125

93-
return { screenshots, isLoading, error };
126+
return { screenshots, urls, isLoading, error };
94127
};

0 commit comments

Comments
 (0)