Show error and status in PUP

This commit is contained in:
Ilya Ig. Petrov 2017-05-17 22:11:33 +05:00
parent 94bbb92764
commit 82a2994dcc
6 changed files with 163 additions and 44 deletions

View File

@ -3,28 +3,152 @@ import Component from 'inferno-component';
import createElement from 'inferno-create-element'; import createElement from 'inferno-create-element';
import getNotControlledWarning from './NotControlledWarning'; import getNotControlledWarning from './NotControlledWarning';
import getTabPannel from './TabPannel'; import getTabPanel from './TabPanel';
import getPacChooser from './PacChooser'; import getPacChooser from './PacChooser';
import getFooter from './Footer'; import getFooter from './Footer';
export default function getApp(theState) { export default function getApp(theState) {
const NotControlledWarning = getNotControlledWarning(theState);
const TabPanel = getTabPanel(theState);
const PacChooser = getPacChooser(theState);
const Footer = getFooter(theState);
return class App extends Component { return class App extends Component {
constructor(props) {
super(props);
this.state = {
status: 'Загрузка...',
areInputsDisabled: false,
};
}
componentDidMount() {
this.showErrors({ message: 'PANIC!' });
}
setStatusTo(msg) {
this.setState(
{
status: msg,
}
);
}
showErrors(err, ...warns) {
const warningHtml = warns
.map(
(w) => w && w.messageHtml || ''
)
.filter( (m) => m )
.map( (m) => '✘ ' + m )
.join('<br/>');
let messageHtml = '';
if (err) {
let wrapped = err.wrapped;
messageHtml = err.message || '';
while( wrapped ) {
const deeperMsg = wrapped && wrapped.message;
if (deeperMsg) {
messageHtml = messageHtml + ' &gt; ' + deeperMsg;
}
wrapped = wrapped.wrapped;
}
}
messageHtml = messageHtml.trim();
if (warningHtml) {
messageHtml = messageHtml ? messageHtml + '<br/>' + warningHtml : warningHtml;
}
this.setStatusTo(
(<span>
<span style="color:red">
{err ? <span><span class="emoji">🔥</span> Ошибка!</span> : 'Некритичная oшибка.'}
</span>
<br/>
<span style="font-size: 0.9em; color: darkred" dangerouslySetInnerHTML={{__html: messageHtml}}></span>
{err && <a href="" onClick={(evt) => {
this.props.apis.errorHandlers.viewError('pup-ext-err', err);
evt.preventDefault();
}}> [Техн.детали]</a>}
</span>)
);
}
switchInputs(val) {
this.setState({
areInputsDisabled: val === 'off' ? true : false,
});
/*
const inputs = document.querySelectorAll('input');
for ( let i = 0; i < inputs.length; i++ ) {
const input = inputs[i];
if (val === 'off') {
input.dataset.previousDisabledValue = input.disabled;
input.disabled = true;
} else {
input.disabled = input.dataset.previousDisabledValue === 'true';
}
}
*/
}
conduct(
beforeStatus, operation, afterStatus,
onSuccess = () => {}, onError = () => {}
) {
this.setStatusTo(beforeStatus);
this.switchInputs('off');
operation((err, res, ...warns) => {
warns = warns.filter( (w) => w );
if (err || warns.length) {
showErrors(err, ...warns);
} else {
this.setStatusTo(afterStatus);
}
this.switchInputs('on');
if (!err) {
onSuccess(res);
} else {
onError(err);
}
});
}
render(props) { render(props) {
return createElement('div', { props = Object.assign({}, props, {
onClick: () => console.log('DDDDDCLICK'), funs: {
onDick: () => console.log('DICK!'), setStatusTo: this.setStatusTo,
ondick: () => console.log('dddDICK!'), conduct: this.conduct,
}, [ },
createElement(getNotControlledWarning(theState), props), });
createElement(getTabPannel(theState), {
return createElement('div', null, [
createElement(NotControlledWarning, props),
createElement(TabPanel, {
tabs:[ tabs:[
{ {
label: 'PAC-скрипт', label: 'PAC-скрипт',
content: createElement(getPacChooser(theState), props), content: createElement(PacChooser, props),
}, },
{ {
label: 'Исключения', label: 'Исключения',
@ -44,7 +168,7 @@ export default function getApp(theState) {
} }
] ]
}), }),
createElement(getFooter(theState), props), createElement(Footer, Object.assign({ status: this.state.status }, props)),
]); ]);
} }

View File

@ -6,24 +6,24 @@ export default function getFooter() {
const scopedCss = css` const scopedCss = css`
#statusRow { .statusRow {
padding: 0 0.3em 1em; padding: 0 0.3em 1em;
} }
#status { .status {
display: inline-block; display: inline-block;
} }
.controlRow { .controlRow {
margin: 1em 0 1em 0; margin: 1em 0 1em 0;
} }
`; `;
return function (props) { return function (props) {
return ( return (
<div class="horPadded"> <div class="horPadded">
<section id="statusRow"> <section class={scopedCss.statusRow}>
<div id="status" style="will-change: contents">{props.status}</div> <div clss={scopedCss.status} style="will-change: contents">{props.status}</div>
</section> </section>
<footer class={scopedCss.controlRow + ' horFlex nowrap'}> <footer class={scopedCss.controlRow + ' horFlex nowrap'}>

View File

@ -98,12 +98,11 @@ export default function getInfoRow() {
render(props) { render(props) {
// Object.assign is left-associative, let's make it right. props = Object.assign({}, {
Object.assign(props, Object.assign({
idPrefix: '', idPrefix: '',
ifDashify: false, ifDashify: false,
htmlAfterLabel: '', htmlAfterLabel: '',
}, props)); }, props);
const iddy = props.idPrefix + ( props.ifDashify ? camelToDash(props.conf.key) : props.conf.key ); const iddy = props.idPrefix + ( props.ifDashify ? camelToDash(props.conf.key) : props.conf.key );
return ( return (

View File

@ -1,5 +1,6 @@
import Inferno from 'inferno'; import Inferno from 'inferno';
import Component from 'inferno-component'; import Component from 'inferno-component';
import createElement from 'inferno-create-element';
import css from 'csjs-inject'; import css from 'csjs-inject';
import getInfoLi from './InfoLi'; import getInfoLi from './InfoLi';
@ -103,55 +104,48 @@ export default function getPacChooser(...args) {
render(props) { render(props) {
const date = this.getDate(props.antiCensorRu); const date = this.getDate(props.apis.antiCensorRu);
return (<div>Обновлялись: <span class="updateDate" title={date.title}>{ date.text }</span></div>); return (<div>Обновлялись: <span class="updateDate" title={date.title}>{ date.text }</span></div>);
} }
} }
const updatePac = function updatePac() {
conduct(
'Обновляем...', (cb) => antiCensorRu.syncWithPacProviderAsync(cb),
'Обновлено.'
);
return false;
};
const InfoLi = getInfoLi(...args); const InfoLi = getInfoLi(...args);
function emit(e) {
const event = new Event('dick');
e.target.dispatchEvent(event);
}
return class PacChooser extends Component { return class PacChooser extends Component {
render(props) { render(props) {
const checkedIddy = props.antiCensorRu.getCurrentPacProviderKey() || 'none'; const updatePac = function updatePac() {
props.funs.conduct(
'Обновляем...',
(cb) => props.apis.antiCensorRu.syncWithPacProviderAsync(cb),
'Обновлено.'
);
};
const checkedIddy = props.apis.antiCensorRu.getCurrentPacProviderKey() || 'none';
return ( return (
<div> <div>
{props.flags.ifInsideOptionsPage && (<header>PAC-скрипт:</header>)} {props.flags.ifInsideOptionsPage && (<header>PAC-скрипт:</header>)}
<ul> <ul>
{ {
props.antiCensorRu.getSortedEntriesForProviders().map((provConf) => props.apis.antiCensorRu.getSortedEntriesForProviders().map((provConf) =>
(<InfoLi (<InfoLi
conf={provConf} conf={provConf}
type="radio" type="radio"
name="pacProvider" name="pacProvider"
checked={checkedIddy === provConf.key} checked={checkedIddy === provConf.key}
> >
&nbsp;<a href="" class={scopedCss.updateButton} onClick={(evt) => { evt.preventDefault(); emit(evt) }}>[обновить]</a> &nbsp;<a href="" class={scopedCss.updateButton} onClick={(evt) => { evt.preventDefault(); updatePac(); }}>[обновить]</a>
</InfoLi>) </InfoLi>)
) )
} }
<InfoLi type="radio" name="pacProvider" conf={{key: 'none', label: 'Отключить'}} checked={checkedIddy === 'none'}/> <InfoLi type="radio" name="pacProvider" conf={{key: 'none', label: 'Отключить'}} checked={checkedIddy === 'none'}/>
</ul> </ul>
<div id="updateMessage" class="horFlex" style="align-items: center"> <div id="updateMessage" class="horFlex" style="align-items: center">
<LastUpdateDate antiCensorRu={props.antiCensorRu}/> { createElement(LastUpdateDate, props) }
<div class={scopedCss.fullLineHeight}> <div class={scopedCss.fullLineHeight}>
{ {
props.flags.ifMini props.flags.ifMini

View File

@ -25,6 +25,9 @@ export default function getTabsPannel({ flags }) {
.underlined { .underlined {
border-bottom: 1px solid var(--cr-options-headline); border-bottom: 1px solid var(--cr-options-headline);
} }
:root.ifInsideOptionsPage .navLabels {
display: none;
}
/* HIDE starts. */ /* HIDE starts. */
@ -113,7 +116,6 @@ export default function getTabsPannel({ flags }) {
constructor(props) { constructor(props) {
super(props); super(props);
console.log('CONSTRUCTOR');
this.state = { this.state = {
chosenTabIndex: 0, chosenTabIndex: 0,
}; };
@ -126,7 +128,6 @@ export default function getTabsPannel({ flags }) {
if (/^#tab(\d+)$/.test(window.location.hash)) { if (/^#tab(\d+)$/.test(window.location.hash)) {
const inputIndex = RegExp.$1; const inputIndex = RegExp.$1;
if (inputIndex < this.props.tabs.length) { if (inputIndex < this.props.tabs.length) {
console.log('SET STATE');
this.setState({chosenTabIndex: inputIndex}); this.setState({chosenTabIndex: inputIndex});
} }
} }
@ -137,6 +138,9 @@ export default function getTabsPannel({ flags }) {
return (<div ref={(dom) => { return (<div ref={(dom) => {
if (!dom) {
return /* Unmounting. */;
}
const target = dom.querySelector(`.${scopedCss.mainNav} *:target`); const target = dom.querySelector(`.${scopedCss.mainNav} *:target`);
if (target) { if (target) {
const tabIndex = parseInt(target.id.replace('tab', '')); const tabIndex = parseInt(target.id.replace('tab', ''));

View File

@ -26,14 +26,12 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
theState = { theState = {
utils: backgroundPage.utils, utils: backgroundPage.utils,
antiCensorRu: backgroundPage.apis.antiCensorRu, apis: backgroundPage.apis,
errorHandlers: apis.errorHandlers,
flags: { flags: {
/* Shortcuts to boolean values. */ /* Shortcuts to boolean values. */
ifNotControlled: !apis.errorHandlers.ifControllable, ifNotControlled: !apis.errorHandlers.ifControllable,
ifMini: apis.version.ifMini, ifMini: apis.version.ifMini,
}, },
status: 'Хорошего настроения Вам!',
}; };
} }