import React from "react";
import {Link, NavLink} from "react-router";
import {Dropdown, Icon as SIcon, Menu} from "semantic-ui-react";
import {Media, SettingsContext, StatusContext, ThemeContext} from "../contexts/contexts";
import {DarkModeToggle, HotspotStatusIcon, useLocalStorage} from "./Common";
import {ShareButton} from "./Share";
import {useCPUTemperature, useIOStats, useLoad, useMemoryStats, usePowerStats, useWROLMode} from "../hooks/customHooks";
import {useReorganizationStatus} from "../contexts/FileWorkerStatusContext";
import {SearchIconButton} from "./Search";
import {Icon, Popup} from "./Theme";
import {HELP_VIEWER_URI, NAME, semanticUIColorMap} from "./Vars";
import _ from "lodash";
function updateFavicon(colorName) {
// Fall back to violet if invalid color
const safeColor = colorName in semanticUIColorMap ? colorName : 'violet';
const faviconPath = `/favicon-${safeColor}.svg`;
// Update all favicon links
const iconLinks = document.querySelectorAll('link[rel="icon"], link[rel="shortcut icon"]');
iconLinks.forEach(link => {
link.href = faviconPath;
});
// Also update the 32x32 and 16x16 specific icons if they exist
const icon32 = document.querySelector('link[sizes="32x32"]');
const icon16 = document.querySelector('link[sizes="16x16"]');
if (icon32) icon32.href = faviconPath;
if (icon16) icon16.href = faviconPath;
// Update theme-color meta tag for iOS/Android status bar
const hexColor = semanticUIColorMap[safeColor];
const themeColorMeta = document.querySelector('meta[name="theme-color"]');
if (themeColorMeta) themeColorMeta.content = hexColor;
}
const links = [
{text: 'Videos', to: '/videos', key: 'videos'},
{text: 'Archive', to: '/archive', key: 'archive'},
{text: 'Map', to: '/map', key: 'map'},
{text: 'Zim', to: '/zim', key: 'zim'},
{text: 'Files', to: '/files', key: 'files'},
{
text: 'More', key: 'more',
links: [
{text: 'Inventory', to: '/inventory', key: 'inventory'},
{to: '/more/otp', text: 'One Time Pad', end: true},
{to: '/more/vin', text: 'VIN Decoder', end: true},
{to: '/more/calculators', text: 'Calculators', end: true},
{to: '/more/statistics', text: 'Statistics', end: true},
]
},
];
const help = {to: HELP_VIEWER_URI, text: 'Help', key: 'help', target: '_blank'};
const admin = {to: '/admin', text: 'Admin', key: 'admin'};
const rightLinks = [help, admin];
const collapsedLinks = links.concat([help, admin]);
function DropdownLinks({link}) {
return
{link.links.map(l => )}
}
function MenuLink({link}) {
// Wrapper around NavLink to handle Navlink/Dropdown change.
if (link.links) {
return
} else {
const end = link.end ? {end: true} : {end: undefined};
const target = link.target ? {target: link.target} : {};
return
{link.text}
}
}
function NavIconWrapper({children}) {
if (children) {
return
{children}
} else {
// Do not use navbar space if children is empty.
return
}
}
function useNavColorSetting() {
// Use localstorage to avoid flickering navbar color on startup.
const {settings} = React.useContext(SettingsContext);
const [navColor, setNavColor] = useLocalStorage('nav_color', 'violet');
React.useEffect(() => {
if (!_.isEmpty(settings)) {
setNavColor(settings.nav_color);
}
}, [settings.nav_color]);
// Update favicon to match nav color
React.useEffect(() => {
if (navColor) {
updateFavicon(navColor);
}
}, [navColor]);
return navColor
}
export function NavBar() {
const wrolModeEnabled = useWROLMode();
const {status} = React.useContext(StatusContext);
const navColor = useNavColorSetting();
const wrolpiIcon = ;
const name = {NAME || wrolpiIcon};
const topNavText = wrolModeEnabled ? <>{name} > : name;
const {i} = React.useContext(ThemeContext);
const homeLink =
{topNavText}
;
// Red/Yellow colors will blend in to some navbar colors, swap the colors for high contrast.
const conflictingColors = ['red', 'orange', 'yellow', 'olive', 'pink'];
const [lowWarningColor, highWarningColor] = conflictingColors.includes(navColor)
? [null, 'black']
: ['yellow', 'red'];
// Generic system load, the least important warning.
const {minute_1, mediumLoad, highLoad} = useLoad();
let systemLoadIcon;
if (mediumLoad || highLoad) {
// System load is high, display a warning icon.
const color = highLoad ? highWarningColor : lowWarningColor;
const icon =
;
systemLoadIcon =
}
// RAM consumption.
const {percent: memoryPercent} = useMemoryStats();
let memoryIcon;
if (memoryPercent > 80) {
const color = memoryPercent > 90 ? highWarningColor : lowWarningColor;
const icon =
;
memoryIcon =
}
// Any disk is busy and processes are waiting.
const {percentIOWait} = useIOStats();
let diskWaitIcon;
if (percentIOWait >= 50) {
// Processes are waiting on disk, display a warning icon.
const color = percentIOWait > 75 ? highWarningColor : lowWarningColor;
const icon =
;
diskWaitIcon =
}
// CPU temperature.
const {temperature, highTemperature, criticalTemperature} = useCPUTemperature();
let temperatureIcon;
if (temperature && temperature >= highTemperature) {
// CPU temperature is high, display a warning icon.
const color = temperature >= criticalTemperature ? highWarningColor : lowWarningColor;
const name = temperature >= criticalTemperature ? 'thermometer' : 'thermometer half';
const icon =
const link = {icon};
temperatureIcon = ;
}
// Power issues, this is always displayed if detected.
const {underVoltage, overCurrent} = usePowerStats();
let powerIcon;
if (underVoltage || overCurrent) {
const name = underVoltage ? 'power cord' : 'lightning';
const icon = ;
const message = underVoltage
? 'Under-voltage detected! Your power supply is insufficient!'
: 'Over-current detected! Your peripherals are using too much power!';
powerIcon = ;
}
// Display the temperature icon first because it can cause the system to throttle. The rest are in order of effects
// that will slow down the system and the user should address. Generic load is last because it is probably not an
// issue.
const warningIcon = temperatureIcon || diskWaitIcon || memoryIcon || systemLoadIcon;
const {isReorganizing, taskType, collectionId, collectionKind} = useReorganizationStatus();
let processingLink;
if (status && status.flags) {
if (status.flags.file_worker_busy) {
// Smart navigation based on what the file worker is doing
if (isReorganizing && collectionId && collectionKind) {
// Single collection reorganization - navigate to that collection
if (collectionKind === 'channel') {
processingLink = `/videos/channel/${collectionId}/edit`;
} else if (collectionKind === 'domain') {
processingLink = `/archive/domain/${collectionId}/edit`;
} else {
processingLink = '/files';
}
} else if (isReorganizing && taskType === 'batch_reorganize' && collectionKind) {
// Batch reorganization - navigate to settings page for that kind
if (collectionKind === 'channel') {
processingLink = '/videos/settings';
} else if (collectionKind === 'domain') {
processingLink = '/archive/settings';
} else {
processingLink = '/files';
}
} else {
processingLink = '/files';
}
} else if (status.flags.map_importing) {
processingLink = '/map/manage';
}
}
const processingIcon = processingLink &&
;
let apiDownIcon;
if (window.apiDown) {
apiDownIcon = }
/>
}
// Upgrade available notification - only show on native (non-Docker) installs and when WROL Mode is disabled
let upgradeIcon;
if (status?.update_available && !status?.dockerized && !wrolModeEnabled) {
const commitsBehind = status.commits_behind || 0;
const branch = status.git_branch || 'unknown';
const icon =
;
upgradeIcon = ;
}
const icons = {apiDownIcon}{processingIcon}{upgradeIcon}{powerIcon}{warningIcon};
return <>
>
}