Skip to content

Commit 680dce2

Browse files
committed
Merge branch 'develop' into articles
2 parents 986b333 + fd3c22e commit 680dce2

File tree

9 files changed

+746
-106
lines changed

9 files changed

+746
-106
lines changed

frontend/i18next-parser.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export default {
4747
lineEnding: 'auto',
4848
// Control the line ending. See options at https://github.com/ryanve/eol
4949

50-
locales: ['en', 'no', 'sv'],
50+
locales: ['en', 'no', 'sv', 'da', 'de'],
5151
// An array of the locales in your applications
5252

5353
namespaceSeparator: ':',

frontend/src/components/app-sidebar.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
SidebarRail,
1313
} from "@/components/ui/sidebar"
1414
import {EventSwitcher} from "@/components/event-switcher.tsx";
15+
import { VersionChecker } from "./version-checker"
1516

1617
export function AppSidebar({...props}: React.ComponentProps<typeof Sidebar>) {
1718
return (
@@ -26,6 +27,9 @@ export function AppSidebar({...props}: React.ComponentProps<typeof Sidebar>) {
2627
</SidebarContent>
2728
<SidebarFooter>
2829
<NavUser/>
30+
<div className="flex flex-col items-center border-t border-gray-200 py-2 bg-background/50 dark:bg-gray-800 -mb-2 -mx-2 text-xs text-gray-400 dark:text-gray-500 hover:text-muted-foreground dark:hover:text-secondary-foreground dark:border-gray-700 dark:bg-background/10">
31+
<VersionChecker/>
32+
</div>
2933
</SidebarFooter>
3034
<SidebarRail/>
3135
</Sidebar>

frontend/src/components/layout/organiser-panel-layout.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {NavThemeSwitch} from "@/components/nav-theme-switch.tsx";
55
import * as React from "react";
66
import {Toaster} from "@/components/ui/toaster.tsx";
77
import { NavLanguageSwitch } from "../nav-language-switch.tsx";
8-
import { VersionChecker } from "../version-checker.tsx";
98

109
export default function OrganiserPanelLayout({children}: { children: React.ReactNode }) {
1110
return (
@@ -23,9 +22,9 @@ export default function OrganiserPanelLayout({children}: { children: React.React
2322
<div className="m-6">
2423
{children}
2524
</div>
26-
<footer className="relative bottom-0 p-4 text-center flex items-center justify-center gap-4">
27-
<VersionChecker />
28-
</footer>
25+
{/* <footer className="relative bottom-0 p-4 text-center flex items-center justify-center gap-4">
26+
27+
</footer> */}
2928
</main>
3029
<Toaster />
3130
</SidebarProvider>

frontend/src/components/nav-language-switch.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/compon
1212
const languages = [
1313
{code: "en", name: "English"},
1414
{code: "no", name: "Norsk"},
15-
{code: "sv", name: "Svenska"}
15+
{code: "sv", name: "Svenska"},
16+
{code: "da", name: "Dansk"},
17+
{code: "de", name: "Deutsch"}
1618
]
1719

1820
export function NavLanguageSwitch() {
@@ -37,6 +39,7 @@ export function NavLanguageSwitch() {
3739
<DropdownMenuItem
3840
key={lang.code}
3941
onClick={() => i18n.changeLanguage(lang.code)}
42+
className={i18n.language === lang.code ? "bg-secondary text-secondary-foreground" : ""}
4043
>
4144
{lang.name}
4245
</DropdownMenuItem>

frontend/src/components/theme-provider.tsx

Lines changed: 0 additions & 88 deletions
This file was deleted.

frontend/src/components/ui/badge.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const badgeVariants = cva(
1616
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
1717
outline: "text-foreground",
1818
prerelease: "bg-orange-50 dark:bg-orange-950 text-orange-400 dark:text-orange-400 border-orange-200 dark:border-orange-800 font-normal",
19+
update: "bg-green-50 dark:bg-green-950 text-green-800 dark:text-green-400 border-green-200 dark:border-green-800 font-normal hover:bg-green-200 dark:hover:bg-green-800",
1920
},
2021
},
2122
defaultVariants: {

frontend/src/components/version-checker.tsx

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@ import { CURRENT_VERSION, GITHUB_API_URL, GITHUB_REPO } from '@/env';
44
import { useTranslation } from 'react-i18next';
55

66
import { Badge } from './ui/badge';
7+
import { ShootingStar } from '@phosphor-icons/react';
78

89
interface GithubRelease {
910
tag_name: string;
1011
html_url: string;
1112
}
1213

14+
const CACHE_KEY = 'version_check';
15+
const CACHE_DURATION = 12 * 60 * 60 * 1000; // 12 hours in milliseconds
16+
1317
export function VersionChecker() {
1418
const { t } = useTranslation();
1519
const [hasUpdate, setHasUpdate] = useState(false);
@@ -18,40 +22,99 @@ export function VersionChecker() {
1822
useEffect(() => {
1923
const checkVersion = async () => {
2024
try {
21-
const response = await fetch(`${GITHUB_API_URL}/releases`);
25+
// Check cache first
26+
const cachedData = localStorage.getItem(CACHE_KEY);
27+
if (cachedData) {
28+
const { timestamp, data } = JSON.parse(cachedData);
29+
if (Date.now() - timestamp < CACHE_DURATION) {
30+
setLatestVersion(data.version);
31+
setHasUpdate(compareVersions(data.version, CURRENT_VERSION) > 0);
32+
return;
33+
}
34+
}
35+
36+
// Make API call with headers to avoid rate limiting
37+
const response = await fetch(`${GITHUB_API_URL}/releases`, {
38+
headers: {
39+
'Accept': 'application/vnd.github.v3+json',
40+
'If-None-Match': localStorage.getItem('version_etag') || ''
41+
}
42+
});
43+
44+
// Handle 304 Not Modified
45+
if (response.status === 304) {
46+
return;
47+
}
48+
49+
// Save ETag for future requests
50+
const etag = response.headers.get('ETag');
51+
if (etag) {
52+
localStorage.setItem('version_etag', etag);
53+
}
54+
2255
const data: GithubRelease[] = await response.json();
23-
const latest = data[0].tag_name.replace('v', '');
24-
setLatestVersion(latest);
56+
if (data.length > 0) {
57+
const latest = data[0].tag_name.replace('v', '');
58+
setLatestVersion(latest);
59+
const needsUpdate = compareVersions(latest, CURRENT_VERSION) > 0;
60+
setHasUpdate(needsUpdate);
2561

26-
if (compareVersions(latest, CURRENT_VERSION) > 0) {
27-
setHasUpdate(true);
62+
// Cache the result
63+
localStorage.setItem(CACHE_KEY, JSON.stringify({
64+
timestamp: Date.now(),
65+
data: { version: latest }
66+
}));
2867
}
2968
} catch (error) {
3069
console.error('Failed to check version:', error);
70+
// On error, use cached data if available
71+
const cachedData = localStorage.getItem(CACHE_KEY);
72+
if (cachedData) {
73+
const { data } = JSON.parse(cachedData);
74+
setLatestVersion(data.version);
75+
setHasUpdate(compareVersions(data.version, CURRENT_VERSION) > 0);
76+
}
3177
}
3278
};
3379

3480
checkVersion();
35-
// Check every 24 hours
36-
const interval = setInterval(checkVersion, 24 * 60 * 60 * 1000);
81+
// Check every 12 hours
82+
const interval = setInterval(checkVersion, CACHE_DURATION);
3783
return () => clearInterval(interval);
3884
}, [CURRENT_VERSION]);
3985

4086
return (
4187
<>
42-
<a href='https://lanms.net' target='_blank' rel='noopener noreferrer' className='text-xs text-gray-300 dark:text-gray-500 hover:text-muted-foreground dark:hover:text-secondary-foreground'>LANMS {CURRENT_VERSION}</a>
88+
<a href='https://lanms.net' target='_blank' rel='noopener noreferrer' className='text-xs text-muted-foreground hover:text-primary dark:text-muted-foreground dark:hover:text-primary cursor-pointer'>LANMS {CURRENT_VERSION}</a>
4389
{hasUpdate && (
44-
<Badge variant="outline">
90+
<Badge variant="update" className='flex items-center justify-center my-2'>
4591
<a
4692
href={`https://github.com/${GITHUB_REPO}/releases/latest`}
4793
target="_blank"
4894
rel="noopener noreferrer"
49-
className="ml-2"
95+
className="relative flex items-center justify-center w-full"
96+
onMouseEnter={(e) => {
97+
const target = e.currentTarget;
98+
target.querySelector('.version-default')?.classList.add('opacity-0');
99+
target.querySelector('.version-hover')?.classList.remove('opacity-0');
100+
}}
101+
onMouseLeave={(e) => {
102+
const target = e.currentTarget;
103+
target.querySelector('.version-default')?.classList.remove('opacity-0');
104+
target.querySelector('.version-hover')?.classList.add('opacity-0');
105+
}}
50106
>
51-
{t('version.newAvailable')}: {latestVersion}
107+
<div className="version-default flex items-center justify-center transition-opacity duration-200">
108+
<ShootingStar className="size-4 mr-1" weight="fill" />
109+
<span>{t('common.update_available')}</span>
110+
</div>
111+
<div className="version-hover absolute inset-0 flex items-center justify-center opacity-0 transition-opacity duration-200">
112+
<ShootingStar className="size-4 mr-1" weight="fill" />
113+
<span>{latestVersion}</span>
114+
</div>
52115
</a>
53116
</Badge>
54117
)}
55118
</>
56119
);
57-
}
120+
}

0 commit comments

Comments
 (0)