import React, {useContext} from 'react'; import { Confirm, Container, Dimmer, Divider, Icon, Modal, PlaceholderLine, TableBody, TableCell, TableHeader, TableHeaderCell, TableRow } from "semantic-ui-react"; import { clearCompletedDownloads, clearFailedDownloads, getSettings, getStatus, killDownload, postDownload, saveSettings } from "../api"; import TimezoneSelect from 'react-timezone-select'; import { DisableDownloadsToggle, HelpPopup, HotspotToggle, humanFileSize, LoadStatistic, PageContainer, secondsToElapsedPopup, secondsToFrequency, TabLinks, textEllipsis, ThrottleToggle, Toggle, WROLModeMessage } from "./Common"; import {Route, Routes} from "react-router-dom"; import QRCode from "react-qr-code"; import {ThemeContext} from "../contexts/contexts"; import {useDownloads} from "../hooks/customHooks"; import { Button, Form, FormField, FormGroup, FormInput, Header, Loader, Placeholder, Progress, Statistic, StatisticGroup, Table } from "./Theme"; class Settings extends React.Component { constructor(props) { super(props); this.state = { disabled: false, hotspot_encryption: 'WPA', pending: false, qrCodeValue: '', qrOpen: false, ready: false, download_on_startup: null, download_timeout: null, hotspot_device: null, hotspot_on_startup: null, hotspot_password: null, hotspot_ssid: null, hotspot_status: null, throttle_on_startup: null, throttle_status: null, timezone: {timezone: null}, } this.handleSubmit = this.handleSubmit.bind(this); this.handleHotspotChange = this.handleHotspotChange.bind(this); } async componentDidMount() { try { const settings = await getSettings(); this.setState({ ready: true, disabled: settings.wrol_mode, download_on_startup: settings.download_on_startup, download_timeout: settings.download_timeout || '', hotspot_device: settings.hotspot_device, hotspot_on_startup: settings.hotspot_on_startup, hotspot_password: settings.hotspot_password, hotspot_ssid: settings.hotspot_ssid, hotspot_status: settings.hotspot_status, throttle_on_startup: settings.throttle_on_startup, throttle_status: settings.throttle_status, timezone: {value: settings.timezone, label: settings.timezone}, }, this.handleHotspotChange); } catch (e) { console.error(e); } } async handleSubmit(e) { e.preventDefault(); this.setState({disabled: true, pending: true}); let settings = { download_on_startup: this.state.download_on_startup, download_timeout: this.state.download_timeout ? parseInt(this.state.download_timeout) : 0, hotspot_device: this.state.hotspot_device, hotspot_on_startup: this.state.hotspot_on_startup, hotspot_password: this.state.hotspot_password, hotspot_ssid: this.state.hotspot_ssid, throttle_on_startup: this.state.throttle_on_startup, timezone: this.state.timezone.value, } await saveSettings(settings); this.setState({disabled: false, pending: false}); } handleInputChange = async (e, name, value) => { if (e) { e.preventDefault() } this.setState({[name]: value}); } handleHotspotChange = async () => { let {hotspot_ssid, hotspot_encryption, hotspot_password} = this.state; // Special string which allows a mobile device to connect to a specific Wi-Fi. let qrCodeValue = `WIFI:S:${hotspot_ssid};T:${hotspot_encryption};P:${hotspot_password};;`; this.setState({qrCodeValue}); } handleTimeoutChange = async (e, name, value) => { if (e) { e.preventDefault() } // Restrict timeout to numbers. value = value.replace(/[^\d]/, ''); this.setState({[name]: value}); } render() { if (this.state.ready === false) { return } let { disabled, download_on_startup, download_timeout, hotspot_device, hotspot_on_startup, hotspot_password, hotspot_ssid, pending, qrCodeValue, throttle_on_startup, timezone, } = this.state; const qrButton = ; return {({i, t}) =>
The settings for your WROLPi.

Any changes will be written to config/wrolpi.yaml.

this.handleInputChange(null, 'download_on_startup', checked)} />
this.handleInputChange(null, 'hotspot_on_startup', checked)} />
this.handleInputChange(null, 'throttle_on_startup', checked)} />
Download Timeout } value={download_timeout} disabled={disabled || download_timeout === null} onChange={(e, d) => this.handleTimeoutChange(e, 'download_timeout', d.value)} /> this.setState({hotspot_ssid: d.value}, this.handleHotspotChange)} /> this.setState({hotspot_password: d.value}, this.handleHotspotChange)} /> this.handleInputChange(e, 'hotspot_device', d.value)} /> this.setState({qrOpen: false})} onOpen={() => this.setState({qrOpen: true})} open={this.state.qrOpen} trigger={qrButton} > Scan this code to join the hotspot this.handleInputChange(null, 'timezone', i)} />
}
} } class WROLMode extends React.Component { constructor(props) { super(props); this.state = { WROLMode: false, } } async componentDidMount() { try { let settings = await getSettings(); this.setState({ready: true, WROLMode: settings.wrol_mode}); } catch (e) { console.error(e); } } toggleWROLMode = async (checked) => { // Handle WROL Mode toggling by itself so that other settings are not modified. let config = { wrol_mode: checked, } await saveSettings(config); this.setState({disabled: !this.state.WROLMode, WROLMode: checked}); } render() { if (this.state.ready === false) { return } return {({i, t}) =>
WROL Mode
Enable read-only mode. No content can be deleted or modified. Enable this when the SHTF and you want to prevent any potential loss of data.

Note: User settings and favorites can still be modified.

}
} } function DriveInfo({used, size, percent, mount}) { let color; if (percent >= 90) { color = 'red'; } else if (percent >= 80) { color = 'orange'; } return } function CPUTemperatureStatistic({value}) { if (!value) { return } let color; if (value >= 75) { color = 'red'; } else if (value >= 50) { color = 'yellow'; } return } export function CPUUsageProgress({value, label}) { if (value === null) { return } let color = 'black'; if (value >= 90) { color = 'red'; } else if (value >= 70) { color = 'brown'; } else if (value >= 50) { color = 'orange'; } else if (value >= 30) { color = 'green'; } return } class Status extends React.Component { constructor(props) { super(props); this.state = { status: null, } this.fetchStatus = this.fetchStatus.bind(this); } async componentDidMount() { await this.fetchStatus(); this.intervalId = setInterval(this.fetchStatus, 1000 * 5); } componentWillUnmount() { clearInterval(this.intervalId); } fetchStatus = async () => { try { let status = await getStatus(); this.setState({status: status}); } catch (e) { console.error(e); this.setState({status: null}); } } render() { if (this.state.status) { const {cpu_info, load, drives} = this.state.status; const {minute_1, minute_5, minute_15} = load; const {percent, cores, temperature} = cpu_info; return {({i}) => <>
Drive Usage
{drives.map((drive) => )} }
} return {({i}) => Loading system status; } } } function ClearCompleteDownloads({callback}) { const [disabled, setDisabled] = React.useState(false); async function localClearDownloads() { setDisabled(true); try { await clearCompletedDownloads(); } finally { setDisabled(false); if (callback) { callback() } } } return <> } function ClearFailedDownloads({callback}) { const [open, setOpen] = React.useState(false); const [disabled, setDisabled] = React.useState(false); async function localDeleteFailed() { setDisabled(true); try { await clearFailedDownloads(); } finally { setDisabled(false); setOpen(false); if (callback) { callback() } } } return <> setOpen(false)} onConfirm={localDeleteFailed} /> } class DownloadRow extends React.Component { constructor(props) { super(props); this.state = { errorModalOpen: false, } } render() { let {url, frequency, last_successful_download, status, location, next_download, error} = this.props; const {errorModalOpen} = this.state; let positive = false; if (status === 'pending') { positive = true; } const errorModal = this.setState({errorModalOpen: false})} onOpen={() => this.setState({errorModalOpen: true})} open={errorModalOpen} trigger={ ; return {textEllipsis(url, 50)} {secondsToFrequency(frequency)} {last_successful_download ? secondsToElapsedPopup(last_successful_download) : null} {secondsToElapsedPopup(next_download)} {error && errorModal}{location && View} } } class StoppableRow extends React.Component { constructor(props) { super(props); this.state = { stopOpen: false, startOpen: false, errorModalOpen: false, }; } openStop = () => { this.setState({stopOpen: true}); } closeStop = () => { this.setState({stopOpen: false}); } openStart = () => { this.setState({startOpen: true}); } closeStart = () => { this.setState({startOpen: false}); } handleStop = async (e) => { e.preventDefault(); await killDownload(this.props.id); this.closeStop(); await this.props.fetchDownloads(); }; handleStart = async (e) => { e.preventDefault(); let downloader = this.props.downloader || null; await postDownload(`${this.props.url}`, downloader); this.closeStart(); await this.props.fetchDownloads(); }; render() { let {url, last_successful_download, status, location, error} = this.props; let {stopOpen, startOpen, errorModalOpen} = this.state; let completedAtCell = last_successful_download ? secondsToElapsedPopup(last_successful_download) : null; let buttonCell = ; let positive = false; let negative = false; let warning = false; if (status === 'pending' || status === 'new') { positive = status === 'pending'; buttonCell = ( ); } else if (status === 'failed' || status === 'deferred') { negative = status === 'failed'; warning = status === 'deferred'; buttonCell = ( ); } else if (status === 'complete' && location) { buttonCell = ( View ); } if (error) { completedAtCell = ( this.setState({errorModalOpen: false})} onOpen={() => this.setState({errorModalOpen: true})} open={errorModalOpen} trigger={ ) } return ( {textEllipsis(url, 50)} {completedAtCell} {buttonCell} ); } } function Downloads() { const {t} = useContext(ThemeContext); const {onceDownloads, recurringDownloads, fetchDownloads} = useDownloads(); const tablePlaceholder = ; const stoppableHeader = ( URL Completed At Control ); const nonStoppableHeader = ( URL Download Frequency Completed At Next View ); let onceTable = tablePlaceholder; if (onceDownloads && onceDownloads.length === 0) { onceTable =

No downloads are scheduled.

} else if (onceDownloads) { onceTable = ( <> {stoppableHeader} {onceDownloads.map((i) => )}
); } let recurringTable = tablePlaceholder; if (recurringDownloads && recurringDownloads.length === 0) { recurringTable =

No recurring downloads are scheduled.

} else if (recurringDownloads) { recurringTable = {nonStoppableHeader} {recurringDownloads.map((i) => )}
; } return <>
Downloads
{onceTable}
Recurring Downloads
{recurringTable} } export default function Admin(props) { const links = [ {text: 'Downloads', to: '/admin', key: 'admin', end: true}, {text: 'Settings', to: '/admin/settings', key: 'settings'}, {text: 'Status', to: '/admin/status', key: 'status'}, {text: 'WROL Mode', to: '/admin/wrol', key: 'wrol'}, ]; return ( }/> }/> }/> }/> ) }