11import React , { useState , useEffect } from 'react' ;
2- import { Smartphone , Github , ShieldCheck , Bell , ChevronRight , Palette , Layers , Info , ExternalLink , Globe , MessageSquare , Youtube , Languages , RefreshCcw , CheckCircle , AlertTriangle , Download , HardDriveDownload } from 'lucide-react' ;
2+ import { Smartphone , Github , ShieldCheck , Bell , ChevronRight , Palette , Layers , Info , ExternalLink , Globe , MessageSquare , Youtube , Languages , RefreshCcw , CheckCircle , AlertTriangle , HardDriveDownload } from 'lucide-react' ;
33import { ThemeId , Theme , Language } from '../types' ;
44import { THEMES } from '../utils/theme' ;
55import { TRANSLATIONS } from '../utils/translations' ;
@@ -58,16 +58,19 @@ export const Settings: React.FC<SettingsProps> = ({ currentTheme, setTheme, lang
5858 if ( ! response . ok ) throw new Error ( "Failed to fetch version" ) ;
5959
6060 const text = await response . text ( ) ;
61- // Parse format: [ 0.2 ]
62- // Regex looks for brackets and captures content inside
63- const match = text . match ( / \[ \s * ( [ \d . ] + ) \s * \] / ) ;
61+
62+ // Robust regex to find version number.
63+ // Matches: "0.3", "v0.3", "[ 0.3 ]", "ver: 0.3"
64+ // It captures the digits and dots.
65+ const match = text . match ( / (?: v | v e r | v e r s i o n | \[ ) ? \s * ( [ \d . ] + ) \s * (?: \] ) ? / i) ;
6466
6567 if ( match && match [ 1 ] ) {
6668 const remoteVer = match [ 1 ] ;
6769 setRemoteVersion ( remoteVer ) ;
6870
69- // Simple string comparison for versions (assuming simplified standard numbering)
70- if ( parseFloat ( remoteVer ) > parseFloat ( APP_VERSION ) ) {
71+ // Simple comparison: if remote string is different and "larger" (lexicographically for now)
72+ // Ideally use semver comparison, but for simple app versions this suffices
73+ if ( remoteVer !== APP_VERSION && parseFloat ( remoteVer ) > parseFloat ( APP_VERSION ) ) {
7174 setCheckStatus ( 'update_available' ) ;
7275 } else {
7376 setCheckStatus ( 'uptodate' ) ;
@@ -87,14 +90,11 @@ export const Settings: React.FC<SettingsProps> = ({ currentTheme, setTheme, lang
8790 const apkUrl = `https://github.com/HackerOS-Linux-System/HackerOS-App/releases/download/v${ remoteVersion } /HackerOS-App-${ remoteVersion } .apk` ;
8891
8992 await Toast . show ( {
90- text : 'Starting download... Check your notifications.' ,
93+ text : 'Starting download... Check notifications.' ,
9194 duration : 'long'
9295 } ) ;
9396
94- // We use window.open to trigger the system's download manager.
95- // On Android, downloading an APK via the system browser/manager allows the "Install" prompt
96- // to appear when the user clicks the notification. This handles the "Install" step
97- // by delegating it to the OS Package Installer.
97+ // Open system browser to handle download and installation prompt
9898 window . open ( apkUrl , '_system' ) ;
9999 } ;
100100
@@ -269,7 +269,7 @@ export const Settings: React.FC<SettingsProps> = ({ currentTheme, setTheme, lang
269269 </ div >
270270 </ section >
271271
272- { /* About Section */ }
272+ { /* About & Updates Section */ }
273273 < section className = "px-4" >
274274 < div className = "bg-card/50 backdrop-blur-md border border-white/5 rounded-2xl overflow-hidden shadow-xl" >
275275 < div className = "p-4 border-b border-white/5 flex items-center gap-3" >
@@ -296,50 +296,61 @@ export const Settings: React.FC<SettingsProps> = ({ currentTheme, setTheme, lang
296296 < ChevronRight size = { 16 } className = "text-muted" />
297297 </ a >
298298
299- < div className = "p-4 flex items-center justify-between flex-wrap gap-3 " >
300- < div className = "flex items-center gap-3 " >
301- < div className = "p-2 rounded-lg bg-background text-muted " >
302- < ShieldCheck size = { 18 } / >
303- </ div >
304- < div >
305- < p className = "text-sm font-medium text-text" > App Version </ p >
306- < p className = "text-xs text-muted" >
307- { checkStatus === 'update_available'
308- ? ` ${ t . settings_version_latest } : v ${ remoteVersion } `
309- : `v ${ APP_VERSION } ` }
310- </ p >
299+ < div className = "p-4 flex flex-col gap-4 " >
300+ < div className = "flex items-center justify-between " >
301+ < div className = "flex items-center gap-3 " >
302+ < div className = "p-2 rounded-lg bg-background text-muted" >
303+ < ShieldCheck size = { 18 } / >
304+ </ div >
305+ < div >
306+ < p className = "text-sm font-medium text-text" > System Version </ p >
307+ < p className = "text-xs text-muted font-mono" >
308+ v { APP_VERSION }
309+ </ p >
310+ </ div >
311311 </ div >
312+
313+ { /* Status Badge */ }
314+ { checkStatus !== 'idle' && (
315+ < div className = { `px-2 py-0.5 rounded text-[10px] font-bold border uppercase tracking-wider
316+ ${ checkStatus === 'checking' ? 'border-primary/50 text-primary' : '' }
317+ ${ checkStatus === 'uptodate' ? 'border-green-500/50 text-green-500' : '' }
318+ ${ checkStatus === 'update_available' ? 'border-amber-500/50 text-amber-500' : '' }
319+ ${ checkStatus === 'error' ? 'border-red-500/50 text-red-500' : '' }
320+ ` } >
321+ { checkStatus === 'checking' && 'SCANNING...' }
322+ { checkStatus === 'uptodate' && 'LATEST' }
323+ { checkStatus === 'update_available' && 'OUTDATED' }
324+ { checkStatus === 'error' && 'ERROR' }
325+ </ div >
326+ ) }
312327 </ div >
313-
314- { checkStatus === 'update_available' ? (
328+
329+ { /* Action Button */ }
330+ { checkStatus === 'update_available' ? (
315331 < button
316332 onClick = { performUpdate }
317- className = "ml-auto px-4 py-2 rounded-lg text-xs font-bold bg-primary text-background border border-primary/50 shadow-[0_0_15px_-5px_rgb(var(--color-primary))] flex items-center gap-2 animate-pulse hover:animate-none hover:bg-primary/90 transition-all"
333+ className = "w-full py-3 rounded-lg text-xs font-bold bg-primary text-background border border-primary/50 shadow-[0_0_15px_-5px_rgb(var(--color-primary))] flex items-center justify -center gap-2 animate-pulse hover:animate-none hover:bg-primary/90 transition-all"
318334 >
319- < HardDriveDownload size = { 14 } />
335+ < HardDriveDownload size = { 16 } />
320336 < span > UPDATE TO v{ remoteVersion } </ span >
321337 </ button >
322338 ) : (
323339 < button
324340 onClick = { checkForUpdates }
325341 disabled = { checkStatus === 'checking' }
326342 className = { `
327- ml-auto px-3 py-1.5 rounded-lg text-xs font-bold border transition-all flex items-center gap-2
328- ${ checkStatus === 'checking' ? 'bg-white/5 border-white/10 text-muted cursor-wait' : '' }
329- ${ checkStatus === 'idle' ? 'bg-white/5 border-white/10 text-primary hover:bg-white/10' : '' }
330- ${ checkStatus === 'uptodate' ? 'bg-green-500/10 border-green-500/20 text-green-500' : '' }
331- ${ checkStatus === 'error' ? 'bg-red-500/10 border-red-500/20 text-red-500' : '' }
343+ w-full py-2.5 rounded-lg text-xs font-bold border transition-all flex items-center justify-center gap-2
344+ ${ checkStatus === 'checking' ? 'bg-white/5 border-white/10 text-muted cursor-wait' : 'bg-white/5 border-white/10 text-primary hover:bg-white/10' }
332345 ` }
333346 >
334- { checkStatus === 'checking' && < RefreshCcw size = { 12 } className = "animate-spin" /> }
335- { checkStatus === 'uptodate' && < CheckCircle size = { 12 } /> }
336- { checkStatus === 'error' && < AlertTriangle size = { 12 } /> }
337-
347+ { checkStatus === 'checking' ? (
348+ < RefreshCcw size = { 14 } className = "animate-spin" />
349+ ) : (
350+ < RefreshCcw size = { 14 } />
351+ ) }
338352 < span >
339- { checkStatus === 'idle' && t . settings_check_update }
340- { checkStatus === 'checking' && t . settings_checking }
341- { checkStatus === 'uptodate' && t . settings_up_to_date }
342- { checkStatus === 'error' && t . settings_update_error }
353+ { checkStatus === 'checking' ? t . settings_checking : t . settings_check_update }
343354 </ span >
344355 </ button >
345356 ) }
0 commit comments