import React from 'react';
import TranslatedComponent from './TranslatedComponent';
import { Languages } from '../i18n/Language';
import { Insurance } from '../shipyard/Constants';
import Link from './Link';
import ActiveLink from './ActiveLink';
import cn from 'classnames';
import { Cogs, CoriolisLogo, Hammer, Help, Rocket, StatsBars } from './SvgIcons';
import Persist from '../stores/Persist';
import { toDetailedExport } from '../shipyard/Serializer';
import ModalDeleteAll from './ModalDeleteAll';
import ModalExport from './ModalExport';
import ModalHelp from './ModalHelp';
import ModalImport from './ModalImport';
import Slider from './Slider';
import Announcement from './Announcement';
import { outfitURL } from '../utils/UrlGenerators';
import autoBind from 'auto-bind';
import { Factory } from 'ed-forge';
const SIZE_MIN = 0.65;
const SIZE_RANGE = 0.55;
/**
* Normalize percentages to 'clean' values
* @param {Number} val Percentage value
* @return {Number} Normalized value
*/
function normalizePercent(val) {
if (val === '' || isNaN(val)) {
return 0;
}
val = Math.round(val * 1000) / 1000;
return val >= 100 ? 100 : val;
}
/**
* Rounds the value to the nearest quarter (0, 0.25, 0.5, 0.75)
* @param {Number} val Value
* @return {Number} Rounded value
*/
function nearestQtrPct(val) {
return Math.round(val * 4) / 4;
}
/**
* Select all text in a field
* @param {SyntheticEvent} e Event
*/
function selectAll(e) {
e.target.select();
}
/**
* Coriolis App Header section / menus
*/
export default class Header extends TranslatedComponent {
/**
* Constructor
* @param {Object} props React Component properties
* @param {Object} context React Component context
*/
constructor(props, context) {
super(props);
autoBind(this);
this.ships = Factory.getAllShipTypes().sort();
this._openShips = this._openMenu.bind(this, 's');
this._openBuilds = this._openMenu.bind(this, 'b');
this._openComp = this._openMenu.bind(this, 'comp');
this._openAnnounce = this._openMenu.bind(this, 'announce');
this._openSettings = this._openMenu.bind(this, 'settings');
this.languageOptions = [];
this.insuranceOptions = [];
this.state = {
shipDiscount: normalizePercent(Persist.getShipDiscount() * 100),
moduleDiscount: normalizePercent(Persist.getModuleDiscount() * 100),
};
let translate = context.language.translate;
for (let langCode in Languages) {
this.languageOptions.push({Languages[langCode]} );
}
for (let name in Insurance) {
this.insuranceOptions.push({translate(name)} );
}
}
/**
* Update insurance level
* @param {SyntheticEvent} e Event
*/
_setInsurance(e) {
Persist.setInsurance(e.target.value);
}
/**
* Update the Module discount
*/
_setModuleDiscount() {
let moduleDiscount = normalizePercent(this.state.moduleDiscount);
this.setState({ moduleDiscount });
Persist.setModuleDiscount(moduleDiscount / 100); // Decimal value is stored
}
/**
* Update the Ship discount
*/
_setShipDiscount() {
let shipDiscount = normalizePercent(this.state.shipDiscount);
this.setState({ shipDiscount });
Persist.setShipDiscount(shipDiscount / 100); // Decimal value is stored
}
/**
* Input handler for the module discount field
* @param {SyntheticEvent} e Event
*/
_changeModuleDiscount(e) {
let moduleDiscount = e.target.value;
if (e.target.value === '' || e.target.value === '-' || e.target.value === '.') {
this.setState({ moduleDiscount });
} else if (!isNaN(moduleDiscount) && Math.round(moduleDiscount) < 100) {
this.setState({ moduleDiscount });
}
}
/**
* Input handler for the ship discount field
* @param {SyntheticEvent} e Event
*/
_changeShipDiscount(e) {
let shipDiscount = e.target.value;
if (e.target.value === '' || e.target.value === '-' || e.target.value === '.') {
this.setState({ shipDiscount });
} else if (!isNaN(shipDiscount) && Math.round(shipDiscount) < 100) {
this.setState({ shipDiscount });
}
}
/**
* Key down/press handler for ship discount field
* @param {SyntheticEvent} e Event
*/
_kpShipDiscount(e) {
let sd = this.state.shipDiscount * 1;
switch (e.keyCode) {
case 38:
e.preventDefault();
this.setState({ shipDiscount: e.shiftKey ? nearestQtrPct(sd + 0.25) : normalizePercent(sd + 1) });
break;
case 40:
e.preventDefault();
this.setState({ shipDiscount: e.shiftKey ? nearestQtrPct(sd - 0.25) : normalizePercent(sd - 1) });
break;
case 13:
e.preventDefault();
e.target.blur();
}
}
/**
* Key down/press handler for module discount field
* @param {SyntheticEvent} e Event
*/
_kpModuleDiscount(e) {
let md = this.state.moduleDiscount * 1;
switch (e.keyCode) {
case 38:
e.preventDefault();
this.setState({ moduleDiscount: e.shiftKey ? nearestQtrPct(md + 0.25) : normalizePercent(md + 1) });
break;
case 40:
e.preventDefault();
this.setState({ moduleDiscount: e.shiftKey ? nearestQtrPct(md - 0.25) : normalizePercent(md - 1) });
break;
case 13:
e.preventDefault();
e.target.blur();
}
}
/**
* Update the current language
* @param {SyntheticEvent} e Event
*/
_setLanguage(e) {
Persist.setLangCode(e.target.value);
}
/**
* Toggle tooltips setting
*/
_toggleTooltips() {
Persist.showTooltips(!Persist.showTooltips());
}
/**
* Show delete all modal
* @param {SyntheticEvent} e Event
*/
_showDeleteAll(e) {
e.preventDefault();
this.context.showModal( );
};
/**
* Show export modal with backup data
* @param {SyntheticEvent} e Event
*/
_showBackup(e) {
let translate = this.context.language.translate;
e.preventDefault();
this.context.showModal( );
};
/**
* Show export modal with detailed export
* @param {SyntheticEvent} e Event
*/
_showDetailedExport(e) {
let translate = this.context.language.translate;
e.preventDefault();
this.context.showModal( );
}
/**
* Show help modal
* @param {SyntheticEvent} e Event
*/
_showHelp(e) {
let translate = this.context.language.translate;
e.preventDefault();
this.context.showModal( );
}
/**
* Show import modal
* @param {SyntheticEvent} e Event
*/
_showImport(e) {
e.preventDefault();
this.context.showModal( );
}
/**
* Update the app scale / size ratio
* @param {number} scale scale Size Ratio
*/
_setTextSize(scale) {
Persist.setSizeRatio((scale * SIZE_RANGE) + SIZE_MIN);
}
/**
* Reset the app scale / size ratio
*/
_resetTextSize() {
Persist.setSizeRatio(1);
}
/**
* Open a menu
* @param {string} menu Menu name
* @param {SyntheticEvent} event Event
*/
_openMenu(menu, event) {
event.stopPropagation();
if (this.props.currentMenu == menu) {
menu = null;
}
this.context.openMenu(menu);
}
/**
* Generate the ships menu
* @return {React.Component} Menu
*/
_getShipsMenu() {
const { translate } = this.context.language;
return (
e.stopPropagation() }>
{this.ships.map((s) =>
{translate(s)} )}
);
}
/**
* Generate the builds menu
* @return {React.Component} Menu
*/
_getBuildsMenu() {
const { translate } = this.context.language;
let builds = Persist.getBuilds();
let buildList = [];
for (let shipId of this.ships) {
if (builds[shipId]) {
let shipBuilds = [];
let buildNameOrder = Object.keys(builds[shipId]).sort();
for (let buildName of buildNameOrder) {
let href = outfitURL(shipId, builds[shipId][buildName], buildName);
shipBuilds.push({buildName} );
}
buildList.push({translate(shipId)}{shipBuilds} );
}
}
return (
e.stopPropagation() }>
{buildList}
);
}
/**
* Generate the comparison menu
* @return {React.Component} Menu
*/
_getComparisonsMenu() {
let comparisons;
let translate = this.context.language.translate;
if (Persist.hasComparisons()) {
comparisons = [];
let comps = Object.keys(Persist.getComparisons()).sort();
for (let name of comps) {
comparisons.push({name} );
}
} else {
comparisons = {translate('none created')} ;
}
return (
e.stopPropagation() } style={{ whiteSpace: 'nowrap' }}>
{comparisons}
{translate('compare all')}
{translate('create new')}
);
}
/**
* Generate the announcement menu
* @return {React.Component} Menu
*/
_getAnnouncementsMenu() {
let announcements;
let translate = this.context.language.translate;
if (this.props.announcements) {
announcements = [];
for (let announce of this.props.announcements) {
if (announce.expiry < Date.now()) {
continue;
}
announcements.push( );
announcements.push( );
}
}
return (
e.stopPropagation() } style={{ whiteSpace: 'nowrap' }}>
{announcements}
);
}
/**
* Generate the settings menu
* @return {React.Component} Menu
*/
_getSettingsMenu() {
let translate = this.context.language.translate;
let tips = Persist.showTooltips();
return (
e.stopPropagation() }>
{translate('builds')} & {translate('comparisons')}
{translate('backup')}
{translate('detailed export')}
{translate('import')}
{translate('delete all')}
{translate('about')}
);
}
/**
* Add listeners on mount
*/
componentWillMount() {
let update = () => this.forceUpdate();
Persist.addListener('language', update);
Persist.addListener('insurance', update);
// Persist.addListener('discounts', update);
Persist.addListener('deletedAll', update);
Persist.addListener('builds', update);
Persist.addListener('tooltips', update);
}
/**
* Update state based on property and context changes
* @param {Object} nextProps Incoming/Next properties
* @param {Object} nextContext Incoming/Next conext
*/
componentWillReceiveProps(nextProps, nextContext) {
if(this.context.language != nextContext.language) {
let translate = nextContext.language.translate;
this.insuranceOptions = [];
for (let name in Insurance) {
this.insuranceOptions.push({translate(name)} );
}
}
if (nextProps.currentMenu == 'settings') { // Settings menu is about to be opened
this.setState({
shipDiscount: normalizePercent(Persist.getShipDiscount() * 100),
moduleDiscount: normalizePercent(Persist.getModuleDiscount() * 100),
});
} else if (this.props.currentMenu == 'settings') { // Settings menu is about to be closed
if (this.state.shipDiscount != (Persist.getShipDiscount() * 100)) {
this._setShipDiscount();
}
if (this.state.moduleDiscount != (Persist.getModuleDiscount() * 100)) {
this._setModuleDiscount();
}
}
}
async update() {
const reg = await navigator.serviceWorker.getRegistration();
if (!reg || !reg.waiting) {
return window.location.reload();
}
reg.waiting.postMessage('skipWaiting');
window.location.reload();
}
/**
* Render the header
* @return {React.Component} Header
*/
render() {
let translate = this.context.language.translate;
let openedMenu = this.props.currentMenu;
let hasBuilds = Persist.hasBuilds();
return (
{this.props.appCacheUpdate && {translate('PHRASE_UPDATE_RDY')}
}
{this.props.appCacheUpdate ?
{'View Release Changes'}
: null}
{translate('ships')}
{openedMenu == 's' ? this._getShipsMenu() : null}
{translate('builds')}
{openedMenu == 'b' ? this._getBuildsMenu() : null}
{/* TODO: Enable */}
{/*
{translate('announcements')}
{openedMenu == 'announce' ? this._getAnnouncementsMenu() : null}
*/}
{window.location.origin.search('.edcd.io') >= 0 ?
: null
}
{translate('settings')}
{openedMenu == 'settings' ? this._getSettingsMenu() : null}
);
}
}