Skip to content

Instantly share code, notes, and snippets.

@jbis9051
Created May 6, 2019 21:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbis9051/4ba0e085d8a3deaa84c5d0424087b7f0 to your computer and use it in GitHub Desktop.
Save jbis9051/4ba0e085d8a3deaa84c5d0424087b7f0 to your computer and use it in GitHub Desktop.
import React, {Component} from 'react';
import './css/App.css';
import './css/box.css';
import './css/settings.css';
import './css/pastDownloads.css';
import './css/standard_prompt.css';
import './css/tooltip.css';
import './css/downloads.css';
import Tool from './components/tool';
import Checkbox from './components/checkbox';
import WindowFrame from './components/windowframe';
import Alert from './components/alert';
import {$} from './components/utils';
import DownloadCarrier from './download-carrier';
import Download from "./Download";
import * as url_lib from "url";
Array.prototype.switch = function (condition, goto, fallback) {
// a ternary operator for arrays.
// if the condition evaluates to true, return the first condition
// otherwise, the second
if (!condition.bind(this)(this))
return typeof fallback === "function" ? fallback(this) : fallback;
else
return typeof goto === "function" ? goto(this) : goto;
};
Array.prototype.flip = function (shouldFlip) {
if (shouldFlip)
return this.reverse();
else
return this;
};
Array.prototype.except = function (excluder) {
return this.filter(i => i !== excluder);
};
const path = window.require('path');
const os = window.require('os');
const fs = window.require('fs');
const https = window.require('https');
const Mousetrap = window.Mousetrap;
const _electron = window.require('electron');
const version = _electron.ipcRenderer.sendSync('version');
const remote = _electron.remote;
const {ipcRenderer} = window.require('electron');
const currentWindow = remote.getCurrentWindow();
let platform = remote.require('os').platform();
if (platform !== "win32" && platform !== "darwin")
platform = "other";
ipcRenderer.on('menu-about', function (event) {
window.App.about();
});
ipcRenderer.on('menu-settings', function (event) {
window.App.showSettings();
});
ipcRenderer.on('menu-new_download', function (event) {
window.App.show();
});
ipcRenderer.on('menu-close', function (event) {
window.App.close();
});
ipcRenderer.on('menu-contact', function (event) {
window.App.contact();
});
ipcRenderer.on('check-update', function (event) {
window.App.CheckForUpdate(true);
});
export default class App extends Component {
constructor(...args) {
super(...args);
document.title = "Quick Downloader";
// this.me = React.createRef();
this.state = {
downloadNums: 0,
downloads: [],
promptShowing: false,
downloadName: "",
downloadURL: "",
boxes: [],
settingsVisible: false,
currentSelection: -1,
latestDownloadProgress: 0,
pastDownloadsVisible: false,
customHeaders: "",
showActive: true,
filters: {name: true},
showError: false,
filterValue: "",
sortBy: "stats.eta",
cookieURL: "",
};
this.CheckForUpdate(false);
App.loadSettings();
}
static loadSettings() {
window.localStorage.theme = window.localStorage.getItem("theme") || "dark";
window.localStorage.saveLocation = window.localStorage.getItem("saveLocation") || path.join(os.homedir(), 'Downloads');
window.localStorage.proxySettings = window.localStorage.getItem("proxySettings") || "none";
window.localStorage.proxyRequiresCredentials = window.localStorage.getItem("proxyRequiresCredentials") || false;
window.localStorage.partsToCreate = Number(window.localStorage.getItem("partsToCreate")) || 10;
window.localStorage.preferredUnit = window.localStorage.getItem("preferredUnit") || (platform === "darwin" ? "dec" : "bin");
window.localStorage.allowNotifications = window.localStorage.getItem("allowNotifications") || "true";
window.localStorage.autoHideMenuBar = window.localStorage.getItem("autoHideMenuBar") || "false";
window.localStorage.showAdvancedDetails = window.localStorage.getItem("showAdvancedDetails") || "true";
}
alert(box) {
this.setState(prev => ({boxes: [...prev.boxes, box]}));
}
showDownloadPrompt() {
if (!this.state.promptShowing) {
this.setState(prevState => ({promptShowing: !prevState.promptShowing}));
}
}
closeDownloadPrompt() {
this.setState({promptShowing: false});
}
clearDownloadPrompt() {
this.setState({downloadName: "", downloadURL: ""});
}
generateUniqueFileName(name, saveLocation, url) {
const fullLocation = Download.getFileName(name, saveLocation, url);
return new Promise(resolve => {
if (fs.existsSync(fullLocation)) {
let box;
this.alert(<Alert noClose={true} ref={dialog => box = dialog}
key={new Date().getTime().toLocaleString()}
header={"File already exists"}>
<div>
The file "{fullLocation.split('/').pop()}" already exists. You can replace it or keep it or
rename
the
download.
<br/>
<br/>
<div className={"right"}>
<button onClick={async () => {
let num = 2;
while (fs.existsSync(Download.getFileName(name + " " + num, saveLocation, url))) {
num++;
}
this.setState({showing: false});
resolve(name + " " + num);
box.setState({showing: false});
}}>Rename
</button>
<button onClick={async () => {
fs.unlinkSync(fullLocation);
resolve(name);
this.setState({showing: false});
box.setState({showing: false});
}}>Overwrite
</button>
<button onClick={() => {
resolve(false);
this.setState({showing: false});
box.setState({showing: false});
}}>Cancel
</button>
</div>
</div>
</Alert>);
} else {
resolve(name);
}
});
}
filter(downloads) { // challenge, turn this into a one-liner
const filtered = [];
for (let i of downloads) {
let matchesCriteria = false;
for (let j in this.state.filters) {
if (this.state.filters[j]) {
if (!matchesCriteria)
matchesCriteria = i[j].toLowerCase().indexOf(this.state.filterValue.toLowerCase()) > -1;
}
}
if (matchesCriteria)
filtered.push(i);
}
return filtered.sort((downloadA, downloadB) => {
if (downloadA[this.state.sortBy] > downloadB[this.state.sortBy])
return 1;
else if (downloadA[this.state.sortBy] < downloadB[this.state.sortBy])
return -1;
else
return 0;
});
}
changeSelection(dir) {
if (this.state.focused) {
if (this.state.focused.classList.contains('dl-name'))
this.setState(prev => {
const maxSelection = this.getDownloadNames().filter(i => !!i).length;
return {currentSelection: (maxSelection + (prev.currentSelection + dir) % maxSelection) % maxSelection};
});
else if (this.state.focused.classList.contains('dl-url'))
this.setState(prev => {
const maxSelection = this.getDownloadUrls().filter(i => !!i).length;
return {currentSelection: (maxSelection + (prev.currentSelection + dir) % maxSelection) % maxSelection};
});
else if (this.state.focused.classList.contains('dl-headers'))
this.setState(prev => {
const maxSelection = this.getDownloadHeaders().filter(i => !!i).length;
return {currentSelection: (maxSelection + (prev.currentSelection + dir) % maxSelection) % maxSelection};
});
}
}
async addToDownloadHistory(url = this.state.downloadURL, name = this.state.downloadName, headers = this.state.customHeaders) {
const _downloadHistory = JSON.parse(window.localStorage.downloadHistory || "[]");
_downloadHistory.unshift({url, name, headers});
window.localStorage.downloadHistory = JSON.stringify(_downloadHistory);
}
getDownloads() {
const downloads = JSON.parse(window.localStorage.downloadHistory || "[]");
return downloads.map((i, a) => downloads.slice(0, a).findIndex(j => j.url === downloads[a].url && j.name === downloads[a].name && j.headers === downloads[a].headers) === -1 ? downloads[a] : null);
}
getDownloadNames() {
return this.getDownloads().map(i => i ? i.name || "" : i);
}
getDownloadUrls() {
return this.getDownloads().map(i => i ? i.url || "" : i);
}
getDownloadHeaders() {
return this.getDownloads().map(i => i ? i.headers || "" : i);
}
filterSuggestion(i) {
return (i.name || "").toLowerCase().indexOf((this.state.downloadName || "").toLowerCase()) >= 0
&& (i.url || "").toLowerCase().indexOf((this.state.downloadURL || "").toLowerCase()) >= 0
&& (i.headers || "").toLowerCase().indexOf((this.state.customHeaders || "").toLowerCase()) >= 0
}
componentDidMount() {
remote.getCurrentWindow().setProgressBar(-1);
if (!window.localStorage.downloadHistory)
window.localStorage.downloadHistory = JSON.stringify([]);
try {
Mousetrap.bind('mod+n', () => this.showDownloadPrompt());
Mousetrap.bind('esc', () => {
// this.forceUpdate();
this.closeDownloadPrompt();
this.clearDownloadPrompt();
this.setState(prev => ({
settingsVisible: false,
pastDownloadsVisible: false,
boxes: prev.boxes.filter(i => i.props.noClose)
}));
});
Mousetrap.bind('mod+j', () => this.pastDownloads());
Mousetrap.bind('f11', () => currentWindow.setFullScreen(!currentWindow.isFullScreen()));
Mousetrap.bind('up', () => this.changeSelection(-1) || false);
Mousetrap.bind('down', () => this.changeSelection(1) || false);
// Mousetrap.bind('enter', () => {
// if (this.state.promptShowing) this.acceptSuggestion(this.state.currentSelection)
// });
Mousetrap.bind("ctrl+tab", () => {
this.setState(prev => ({
showActive: !prev.showActive
}))
})
} catch (e) {
this.alert(<Alert key={new Date().toLocaleString()} header={"An error has occurred"}
body={"A dependency has failed to load, keyboard shortcuts will be disabled. Otherwise, everything else should work."}/>)
}
window.App = {
show: () => this.showDownloadPrompt(),
showPastDownloads: () => this.pastDownloads(),
close: () => App.confirmExit(),
toggleFullScreen: () => remote.getCurrentWindow().setFullScreen(!remote.getCurrentWindow().isFullScreen()),
about: () => this.about(),
showSettings: () => this.showSettings(),
alert: (box) => this.alert(box),
contact: () => this.contact(),
CheckForUpdate: (displayFailPrompt) => this.CheckForUpdate(displayFailPrompt)
};
}
acceptSuggestion(number) {
const names = this.getDownloadNames(),
urls = this.getDownloadUrls(),
headers = this.getDownloadHeaders();
this.setState({
downloadURL: urls[number],
downloadName: names[number],
customHeaders: headers[number]
});
this.setState({
stopSave: true
});
$('.suggestions').style.display = "none";
this.setState({
currentSelection: -1,
focused: null
})
}
showSettings() {
this.setState(prev => ({settingsVisible: !prev.settingsVisible}));
}
static confirmExit() {
window.close();
}
pastDownloads() {
this.setState(prev => ({pastDownloadsVisible: !prev.pastDownloadsVisible}));
}
changePath() {
window.localStorage.saveLocation = _electron.ipcRenderer.sendSync('pickDir') || window.localStorage.saveLocation;
this.forceUpdate();
}
contact() {
this.alert(<Alert key={new Date().toLocaleString()} header={"About"} body={
<ul>
<li><a target={"_blank"}
onClick={() => _electron.ipcRenderer.send('openURL', "https://joshbrown.info/#contact")}>Joshua
Brown</a>
</li>
<li><a target={"_blank"}
onClick={() => _electron.ipcRenderer.send('openURL', "https://www.jacob-schneider.ga/contact.html")}>Jacob
Schneider</a>
</li>
<br/>
<b>Please submit issues to Github.</b>
</ul>
}/>)
}
about() {
this.alert(<Alert key={new Date().toLocaleString()} header={"About"} body={<div>
<ul className={"about-details"}>
<li>
<b>Quick Downloader Version: </b>
<span>{version}</span>
</li>
<li>
<b>Node Version: </b>
<span>{window.process.versions.node}</span>
</li>
<li>
<b>Electron Version: </b>
<span>{window.process.versions.electron}</span>
</li>
<li>
<b>Chromium Version: </b>
<span>{window.process.versions.chrome}</span>
</li>
<li>
<b>V8 Version: </b>
<span>{window.process.versions.v8}</span>
</li>
<li>
<b>React Version: </b>
<span>{React.version}</span>
</li>
</ul>
</div>}/>)
}
async createDownload(url, name, headers) {
url = url || "";
name = name || "";
headers = headers || "{}";
this.closeDownloadPrompt();
this.clearDownloadPrompt();
const newName = await this.generateUniqueFileName(name, window.localStorage.saveLocation, url);
if (!newName) {
return false;
}
const download = new DownloadCarrier(url, newName, headers);
download.download.on("init-complete", function () {
download.status = 3;
this.forceUpdate();
});
download.download.on("download_all", () => {
download.status = 0;
this.forceUpdate();
});
download.download.on("downloads_complete", () => {
download.status = 6;
this.next();
this.forceUpdate();
});
download.download.on("complete", () => {
download.done = true;
download.status = 2;
this.next();
this.forceUpdate();
});
download.on("update", info => {
this.forceUpdate();
});
download.on("error", err => {
download.status = 1;
this.forceUpdate();
console.error(err);
download.cancel();
});
download.on("cancel", () => {
if (download.status !== 1) {
download.status = 5;
this.forceUpdate();
}
this.next();
});
download.on("remove", () => {
this.setState({
downloads: [...this.state.downloads.filter(d => d !== download)],
});
});
download.on("retry", async () => {
download.constructor(download.url, download.name, download.customHeaders);
download.status = 3;
await download.initiateDownload();
if (this.getActive().length === 0) {
this.next();
}
});
return download;
}
addDownload(download) {
if (download) {
this.setState({
downloads: [
...this.state.downloads,
download
]
});
}
}
getDisplayDownloads() {
return this.filter(this.state.downloads.filter(i => !i.done));
}
getAllDownloads() {
return this.filter(this.state.downloads);
}
getActive() {
return this.filter(this.state.downloads.filter(i =>
i.status === 0
));
}
getReady() {
return this.filter(this.state.downloads.filter(i =>
i.status === 3
));
}
getInactive() {
return this.filter(this.state.downloads.filter(i => i.done));
}
next() {
const downloads = this.getReady();
if (downloads[0] && this.getActive().length === 0) {
const download = downloads[0];
download.startDownload().catch(err => {
console.error(err);
});
}
this.forceUpdate();
}
async CheckForUpdate(displayFailPrompt) {
const url = "https://raw.githubusercontent.com/jbis9051/quick_download/master/package.json";
const q = url_lib.parse(url);
const currentVersion = await new Promise((resolve, reject) => {
let data = "";
const request = https.get({
path: q.path,
host: q.hostname,
port: 443,
}, res => {
res.on("data", (chunk) => {
data += chunk;
});
res.on('error', (e) => {
console.error(e);
resolve(false);
});
res.on('end', () => {
resolve(JSON.parse(data).version);
});
});
request.on('error', (e) => {
console.error(e);
resolve(false);
});
});
if (!currentVersion) {
if (displayFailPrompt) {
let box;
this.alert(<Alert noClose={false}
ref={dialog => box = dialog}
key={new Date().getTime().toLocaleString()}
header={"Update Check Failed"}>
<div>
An Error occurred while checking for an update.
</div>
</Alert>);
}
return false;
}
console.log("Current Version: " + currentVersion);
console.log("This Version: " + version);
if (version !== currentVersion) {
let box;
this.alert(<Alert noClose={true}
ref={dialog => box = dialog}
key={new Date().getTime().toLocaleString()}
header={"Update Available"}>
<div>
An update is available. You are currently on version {version}. The current version
is {currentVersion}. Would you like to update?
<div className={"right"}>
<button onClick={() => {
this.setState({showing: false});
box.setState({
showing: false,
});
}
}>No
</button>
<button onClick={() => {
window.localStorage.downloadHistory = "[]";
this.setState({showing: false});
box.setState({
showing: false,
});
_electron.ipcRenderer.send('openURL', 'https://jbis9051.github.io/quick_download/');
}}>Yes
</button>
</div>
</div>
</Alert>);
} else {
if (displayFailPrompt) {
let box;
this.alert(<Alert noClose={false}
ref={dialog => box = dialog}
key={new Date().getTime().toLocaleString()}
header={"Current Version"}>
<div>
You have the current version, {version}.
</div>
</Alert>);
}
}
return true;
}
render() {
return (
<div className="wrapper">
<WindowFrame contact={e => this.contact()} update={e => this.CheckForUpdate(true)}
about={e => this.about()} download={e => this.showDownloadPrompt()}/>
<div className={"menu_buttons_container"}>
<div className={"menu_buttons_wrapper"}>
<Tool tooltip={"New download"} className="icon_button" shortcut="+"
onClick={e => this.showDownloadPrompt()}
icon={"fas fa-plus"}/>
<Tool tooltip={"Settings"} className="icon_button"
shortcut="*"
onClick={() => this.setState(prev => ({settingsVisible: !prev.settingsVisible}))}
icon={"fas fa-cog"}/>
<Tool tooltip={"Show download history"}
className="icon_button"
onClick={() => this.setState(prev => ({pastDownloadsVisible: !prev.pastDownloadsVisible}))}
icon={"fas fa-history"}/>
</div>
</div>
<div className="App">
<div className={"download-tabs"}>
<span onClick={() => {
this.setState({showActive: true});
}} className={"tab"}
id={this.state.showActive ? "active" : ""}>Queue</span>
<span onClick={() => this.setState({showActive: false})} className={"tab"}
id={!this.state.showActive ? "active" : ""}>Complete</span>
</div>
<div className={"downloads-display-options"}>
<input value={this.state.filterValue}
onChange={async text => (await this.setState({filterValue: text.target.value}) || console.log(this.state.filters, this.state.filterValue))}
className={"input_standard"} placeholder={"Filter downloads"}/>
<Tool left={true} tooltip={"Search by"} icon={"fas fa-search"}
menu={{
"Name": () => this.setState(prev => ({
filters: {
...prev.filters,
name: !prev.filters.name
}
})),
"URL": () => this.setState(prev => ({
filters: {
...prev.filters,
url: !prev.filters.url
}
})),
"Status": () => this.setState(prev => ({
filters: {
...prev.filters,
statusName: !prev.filters.statusName
}
}))
}} getActive={() => ({
Name: this.state.filters.name,
URL: this.state.filters.url,
Status: this.state.filters.statusName
})}/>
<Tool left={true} tooltip={"Sort By"} icon={"fas fa-sort-amount-down"}
menu={{
"Name": () => this.setState({sortBy: "name"}),
"URL": () => this.setState({sortBy: "url"}),
"Completion Time": () => this.setState({sortBy: "stats.eta"})
}} getActive={() => ({
"Name": this.state.sortBy === "name",
"URL": this.state.sortBy === "url",
"Completion Time": this.state.sortBy === "stats.eta"
})}/>
<Tool onClick={() => this.setState(prev => ({reversed: !prev.reversed}))} left={true}
tooltip={"Reverse List"}
icon={!this.state.reversed ? "fas fa-chevron-down" : "fas fa-chevron-up"}/>
</div>
<div className={"download-tabs-content"}>
<div className={"downloads active"} id={this.state.showActive ? "active" : ""}>
{this.getDisplayDownloads().switch(i => i.length > 0, i => i.map((i, a) => i.render(`download${a}`)), "Press the + button to start a download")}
</div>
<div className={"downloads inactive"} id={!this.state.showActive ? "active" : ""}>
{this.getInactive().switch(i => i.length > 0, i => i.map((i, a) => i.render(`download${a}`)), "Wait until a download completes to see it here")}
</div>
</div>
{/* ------------------------------------------------------------------------------------------------New Download Prompt------------------------------------------------------------------------------------------------ */}
{this.state.promptShowing ?
<div className={"prompt_wrapper"}>
<div className={"prompt_content_container"}>
<div className={"prompt_content_wrapper"}>
<header className={"prompt_header"}>
<h1>New Download</h1>
<div className={"prompt_close_button"}>
<Tool left={true} tooltip={"Close the prompt"} icon={"fas fa-times"}
onClick={e => this.closeDownloadPrompt() && this.clearDownloadPrompt()}/>
</div>
</header>
<div className={"formItem"}>
<label htmlFor={"dl-name"}>The file name of the download</label>
<input autoFocus={true}
onFocus={field => this.setState({focused: field.target})}
onBlur={() => this.setState({focused: null})}
value={this.state.downloadName || ""}
onChange={e => void ((() => {
if (this.state.stopSave)
this.setState({
stopSave: false
});
})()) || this.setState({downloadName: e.target.value})}
className={"mousetrap dl-name input_standard"}
id={"dl-name"}
placeholder={"Download Name"}/>
<div className={"suggestions"}>
{
this.getDownloads().map((i, a, x) => {
if (i && i.name.length > 1 && this.filterSuggestion(i)) {
return (<div key={a}
onClick={() => this.acceptSuggestion(a)}
className={"suggestion"}>
<span>{i.name}</span>
<br/>
</div>);
}
})
}
</div>
</div>
<div className={"formItem"}>
<label htmlFor={"dl-url"}>The location of the file to download</label>
<input onFocus={field => this.setState({focused: field.target})}
onBlur={() => this.setState({focused: null})}
value={this.state.downloadURL || ""}
onChange={e => void ((() => {
if (this.state.stopSave)
this.setState({
stopSave: false
});
})()) || this.setState({downloadURL: e.target.value})}
className={"input_standard dl-url mousetrap url"}
id={"dl-url"}
placeholder={"Download URL"}/>
<div className={"suggestions"}>
{
this.getDownloads().map((i, a, x) => {
if (i && i.url.length > 1 && this.filterSuggestion(i)) {
return (<div key={a}
onClick={() => this.acceptSuggestion(a)}
className={"suggestion"}>
<span>{i.url}</span>
<br/>
</div>);
}
})
}
</div>
</div>
<div className={"formItem"}>
<label htmlFor={"dl-headers"}>Custom Headers (JSON)</label>
<textarea onFocus={field => this.setState({focused: field.target})}
onBlur={() => this.setState({focused: null})}
value={this.state.customHeaders}
onChange={e => void ((() => {
if (this.state.stopSave)
this.setState({
stopSave: false
});
})()) || this.setState({customHeaders: e.target.value})
}
className={"input_standard dl-headers standard_code mousetrap url"}
id={"dl-headers"}
placeholder={'Download Headers (JSON)'}
/>
<div className={"suggestions"}>
{this.getDownloads().map((i, a, x) => {
if (i && i.headers.length > 1 && this.filterSuggestion(i)) {
return (<div key={a}
onClick={() => this.acceptSuggestion(a)}
className={"suggestion"}>
<span>{i.headers}</span>
<br/>
</div>);
}
})}
</div>
</div>
<button className={"confirm-btn"}
onClick={async () => {
let url = await new Promise(resolve => {
this.closeDownloadPrompt();
let box;
this.alert(<Alert noClose={true}
ref={dialog => box = dialog}
key={new Date().getTime().toLocaleString()}
header={"Enter A URL"}>
<div>
<input
onFocus={field => this.setState({focused: field.target})}
onBlur={() => this.setState({focused: null})}
value={this.state.cookieURL}
onChange={e => {
//this.setState({cookieURL: e.target.value});
this.state.cookieURL += e.target.value;
this.forceUpdate();
console.log(this.state.cookieURL);
}}
className={"input_standard dl-url mousetrap url"}
placeholder={"Browse URL"}/>
<div className={"right"}>
<button onClick={() => {
this.setState({
showing: false
});
resolve(this.state.cookieURL);
box.setState({
showing: false,
});
}
}>Ok
</button>
<button onClick={() => {
this.setState({showing: false});
resolve(false);
box.setState({
showing: false,
});
}}>Cancel
</button>
</div>
</div>
</Alert>);
});
this.showDownloadPrompt();
if (url) {
let cookies = ipcRenderer.sendSync('get-browser-cookies', url);
this.setState({
customHeaders: `{'Cookie': '${cookies.map(el => `${el.name}=${el.value}`).join(';')}'}`,
});
}
}}>Get Cookies
</button>
<div className={"right-align"}>
<Tool left={true} tooltip={"Begin download"} className={"confirm-btn"}
icon={"fas fa-check"}
onClick={async () => {
if (this.state.downloadName && this.state.downloadURL) {
let shouldContinue = true;
if (!DownloadCarrier.JSONparse(this.state.customHeaders)) {
this.closeDownloadPrompt();
shouldContinue = await new Promise(resolve => {
let box;
this.alert(<Alert noClose={true}
ref={dialog => box = dialog}
key={new Date().getTime().toLocaleString()}
header={"Invalid JSON"}>
<div>
The custom headers input is not valid JSON. Would
you like to continue
download <b>without</b> custom headers or cancel?
<div className={"right"}>
<button onClick={() => {
this.setState({
showing: false
});
this.setState({
customHeaders: "",
});
resolve(true);
box.setState({
showing: false,
});
}
}>Clear Custom Headers
</button>
<button onClick={() => {
this.setState({showing: false});
this.showDownloadPrompt();
resolve(false);
box.setState({
showing: false,
});
}}>Cancel
</button>
</div>
</div>
</Alert>);
});
}
if (shouldContinue) {
await this.addToDownloadHistory();
const download = await this.createDownload(this.state.downloadURL, this.state.downloadName, this.state.customHeaders);
if (download) {
this.addDownload(download);
await download.initiateDownload();
this.next();
}
}
}
}}/>
</div>
</div>
</div>
</div>
: undefined
}
{/*------------------------------------------------------------------------------------------------Settings Prompt------------------------------------------------------------------------------------------------*/}
{this.state.settingsVisible ?
<div className={"prompt_wrapper"}>
<div className={"prompt_content_container"}>
<div className={"prompt_content_wrapper"}>
<header className={"settings_header prompt_header"}>
<h1>Settings</h1>
<div className={"prompt_close_button"}>
<Tool left={true} tooltip={"Close the prompt"} icon={"fas fa-times"}
onClick={e => this.setState({settingsVisible: false})}/>
</div>
</header>
<h2>Appearance</h2>
<div className={"settings-group"}>
<div className={"setting"}>
<label htmlFor="dark">Dark Theme</label>
<input
onChange={field => {
if (field.target.value === "on")
window.localStorage.theme = "dark";
this.forceUpdate();
}}
className={"standard_radio right_aligned"}
name={"theme"}
id={"dark"}
type={"radio"}
checked={window.localStorage.getItem('theme') === 'dark'}/>
</div>
<div className={"setting"}>
<label htmlFor="light">Light Theme</label>
<input
onChange={field => {
if (field.target.value === "on")
window.localStorage.theme = "dark";
this.forceUpdate();
}}
className={"standard_radio right_aligned"}
name={"theme"}
id={"light"}
type={"radio"}
checked={window.localStorage.getItem('theme') === 'light'}/>
</div>
</div>
<Checkbox checked={window.localStorage.getItem('showAdvancedDetails') === "true"}
onChange={value => void window.localStorage.setItem('showAdvancedDetails', value) || this.forceUpdate()}
text={"Show Advanced Download Details"}/>
<br/>
<h2>General</h2>
<div className={"settingsGroup"}>
{/*<div className={"setting"}>*/}
<label htmlFor={"save-location"}>Save Location</label>
<label onClick={() => this.changePath()} htmlFor="save-location"
className={"standard_path_input"}>{window.localStorage.saveLocation}</label>
<label htmlFor={"numOfParts"}>How many parts to use during download</label>
<br/>
<input id={"numOfParts"}
placeholder={"Number of parts to use during download"}
type={"number"}
min={0}
max={50}
className={"inline_input"}
value={window.localStorage.getItem("partsToCreate")}
onChange={field => void (window.localStorage.partsToCreate = (Number(field.target.value))) || this.forceUpdate()}
/>
{/* //TODO: Add reference to docs explaining how to find the optimum part number */}
{/*{platform === "win32" ? <div><br/><Checkbox*/}
{/* checked={window.localStorage.getItem('autoHideMenuBar') === true}*/}
{/* text={`Auto-hide the menu bar (reveal by pressing Alt`}*/}
{/* onChange={value => void window.localStorage.setItem('autoHideMenuBar', value) || this.forceUpdate()}/>*/}
{/*</div> : null}*/}
<br/>
<br/>
<h3>Units</h3>
<hr/>
<div className={"setting"}>
<input type={"radio"} className={"standard_radio right_aligned"}
name={"unit"}
onChange={field => {
if (field.target.value === "on") window.localStorage.preferredUnit = "bin";
this.forceUpdate();
}} id={"bin"}
checked={window.localStorage.getItem('preferredUnit') === "bin"}/>
<label htmlFor={"bin"}>Binary Units (MiB = 1024 KiB)</label>
</div>
<div className={"setting"}>
<input type={"radio"} className={"standard_radio right_aligned"}
name={"unit"}
onChange={field => {
if (field.target.value === "on") window.localStorage.preferredUnit = "dec";
this.forceUpdate();
}} id={"dec"}
checked={window.localStorage.getItem('preferredUnit') === "dec"}/>
<label htmlFor={"dec"}>Decimal Units (MB = 1000 KB)</label>
</div>
<hr/>
<br/>
<Checkbox checked={window.localStorage.getItem('allowNotifications') === true}
text={"Allow Notifications"}
onChange={value => void window.localStorage.setItem('allowNotifications', value) || this.forceUpdate()}/>
<br/>
<hr/>
<input type={"button"} className={"standard_full_button"} onClick={() => {
if (_electron.ipcRenderer.sendSync('confirmClear'))
window.localStorage.clear();
App.loadSettings();
this.forceUpdate();
}} value={"Reset to default settings"}/>
</div>
<br/>
<h2>Network</h2>
<div className={"setting"}>
<label htmlFor={"none"}>None</label>
<input className={"standard_radio right_aligned"} type={"radio"}
name={"proxy-auth-type"}
checked={window.localStorage.getItem('proxySettings') === 'none'}
id={"none"}
onChange={field => {
if (field.target.value === "on") {
window.localStorage.setItem('proxySettings', 'none');
}
this.forceUpdate();
}}/>
</div>
{(() => false)() ? (
<div className={"setting"}>
<label htmlFor={"none"}>Pac Script</label>
<input className={"standard_radio right_aligned"} type={"radio"}
name={"proxy-auth-type"}
checked={window.localStorage.getItem('proxySettings') === 'pac'}
id={"pac"}
onChange={field => {
if (field.target.value === "on") {
window.localStorage.setItem('proxySettings', 'pac');
}
this.forceUpdate();
}}/>
</div>)
: null} {/* <-- won't render anything */}
<div className={"setting"}>
<label htmlFor={"none"}>HTTPS Proxy</label>
<input className={"standard_radio right_aligned"}
type={"radio"}
name={"proxy-auth-type"}
checked={window.localStorage.getItem('proxySettings') === 'auth'}
id={"auth"}
onChange={field => {
if (field.target.value === "on") {
window.localStorage.setItem('proxySettings', 'auth');
}
this.forceUpdate();
}}/>
</div>
{
window.localStorage.getItem('proxySettings') === "pac" ?
<div>
<input placeholder={"https://example.com/proxy/proxy.pac"}
className={"input_standard"}
value={window.localStorage.getItem('pacFile') || ""}
onChange={field => void window.localStorage.setItem('pacFile', field.target.value) || this.forceUpdate()}
id={"pac-location"}/>
<label htmlFor={"pac-location"}>Pac Script Location</label></div> :
(window.localStorage.getItem('proxySettings') === "auth" ? (
<div>
<label htmlFor={"proxy-host"}>Proxy Host</label>
<input placeholder={"proxy.example.com"}
className={"input_standard"}
value={window.localStorage.getItem('proxyHost') || ""}
onChange={field => void window.localStorage.setItem('proxyHost', field.target.value) || this.forceUpdate()}
id={"proxy-host"}/>
<label htmlFor={"proxy-port"}>Proxy Port</label>
<br/>
<input placeholder={8080}
className={"inline_input"}
type={"number"}
value={window.localStorage.getItem('proxyPort') || ""}
onChange={field => void window.localStorage.setItem('proxyPort', field.target.value) || this.forceUpdate()}
id={"proxy-port"}/>
<br/>
<br/>
<Checkbox
checked={window.localStorage.proxyRequiresCredentials === "true"}
onChange={value => (void window.localStorage.setItem("proxyRequiresCredentials", value)) || this.forceUpdate()}
text={"Proxy Requires Credentials"}/>
{(window.localStorage.proxyRequiresCredentials === "true" ?
<div>
<input placeholder={"Proxy Username"}
type={"text"}
className={"input_standard"}
onChange={field => void (window.localStorage.proxyUsername = field.target.value) || this.forceUpdate()}
value={window.localStorage.proxyUsername || ""}
id={"proxy-username"}/>
<input placeholder={"Proxy Password"}
type={"password"}
className={"input_standard"}
onChange={field => void (window.localStorage.proxyPassword = field.target.value) || this.forceUpdate()}
value={window.localStorage.proxyPassword || ""}
id={"proxy-password"}/>
</div> : null)}
</div>
) : null)
}
</div>
</div>
</div>
: null}
{/*------------------------------------------------------------------------------------------------Past Downloads------------------------------------------------------------------------------------------------*/}
{this.state.pastDownloadsVisible ?
<div className={"prompt_wrapper"}>
<div className={"prompt_content_container"}>
<div className={"prompt_content_wrapper"}>
<header className={"prompt_header"}>
<h1>History</h1>
<div className={"flex"}>
{JSON.parse(window.localStorage.downloadHistory).length > 1 ?
<Tool left={true} tooltip={"Clear all history"} icon={"fas fa-trash"}
onClick={async e => {
this.setState({pastDownloadsVisible: false});
await new Promise(resolve => {
let box;
this.alert(<Alert noClose={true}
ref={dialog => box = dialog}
key={new Date().getTime().toLocaleString()}
header={"Clear History"}>
<div>
Are you sure you would like to clear all past
downloads from history and suggestions? This
cannot be undone.
<div className={"right"}>
<button onClick={() => {
this.setState({showing: false});
resolve();
box.setState({
showing: false,
});
}
}>No
</button>
<button onClick={() => {
window.localStorage.downloadHistory = "[]";
this.setState({showing: false});
resolve();
box.setState({
showing: false,
});
}}>Yes
</button>
</div>
</div>
</Alert>);
});
this.setState({pastDownloadsVisible: true});
}
}/> : null}
{/*<div className={"prompt_close_button"}>*/}
<Tool left={true} tooltip={"Close the prompt"} icon={"fas fa-times"}
onClick={e => this.setState({pastDownloadsVisible: false})}/>
{/*</div>*/}
</div>
</header>
<div className={"prompt_content"}>
{JSON.parse(window.localStorage.getItem('downloadHistory')).map((i, a) =>
<div
key={a}
className={"past-download"}>
<div className={"download-details"}>
<div className={"download-name"}>{i.name}:</div>
<div className={"download-url"}>{i.url}</div>
</div>
<div className={"delete"}>
<Tool left={true} tooltip={"Remove item from history"}
icon={"fas fa-trash"}
onClick={() => {
const history = JSON.parse(window.localStorage.downloadHistory);
history.splice(a, 1);
window.localStorage.downloadHistory = JSON.stringify(history);
this.forceUpdate();
}}/>
</div>
</div>)}
</div>
</div>
</div>
</div>
: null}
</div>
<div className={"box-display-area"}>
{this.state.boxes}
</div>
</div>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment