mirror of
https://github.com/Kenshin/simpread.git
synced 2026-02-01 15:41:29 +00:00
Merge branch 'feature/plugins' into develop
This commit is contained in:
commit
6a0602f86a
@ -3,7 +3,7 @@
|
||||
--text-color: #333;
|
||||
--secondary-color: color(#333 alpha(-30%));
|
||||
--background-color: #fff;
|
||||
--width: 740px;
|
||||
--width: 835px;
|
||||
}
|
||||
|
||||
* {
|
||||
@ -237,4 +237,120 @@ a {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAV1BMVEUAAAD////ExMS2tra/v7/Dw8O8vLzCwsK5ubnCwsK6urq+vr68vLy9vb29vb28vLy9vb29vb29vb29vb29vb29vb29vb29vb2+vr6+vr69vb29vb29vb2oiyseAAAAHHRSTlMAAQ0OEBETFRYZGkpMg8rLzM7R0tPW19je4uTlotOuxwAAAAFiS0dEAf8CLd4AAAB1SURBVCjPrdLHDoAgEATQUbH33vb/v1PEEnVXT85pM48QCAC/JhloC3AOJiO9wDms8UoZdN9KEHZUuAKEPeUuOGw9h0j36nqPHVi/g19RE9y3NoNenylwcGqqfQiANFMQwbIhAz/lB0z0yHg81Hzvp/jfj7AA4P8P+rUn4dEAAAAASUVORK5CYII=);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
cards {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
|
||||
card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
margin: 6px;
|
||||
|
||||
width: 259px;
|
||||
|
||||
color: rgba(51, 51, 51, .87);
|
||||
background-color: #fff;
|
||||
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
|
||||
|
||||
transition: all .25s ease-out;
|
||||
}
|
||||
|
||||
card:hover {
|
||||
box-shadow: 0 10px 20px 0 rgba(168,182,191,0.6);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
card-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 100%;
|
||||
height: 196px;
|
||||
|
||||
background-color: #40C4FF;
|
||||
}
|
||||
|
||||
card-header icon {
|
||||
display: block;
|
||||
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
|
||||
font-size: 80px;
|
||||
}
|
||||
|
||||
card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
width: 100%;
|
||||
height: 134px;
|
||||
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
card-content title {
|
||||
display: block;
|
||||
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
card-content desc {
|
||||
display: block;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
card-content note {
|
||||
display: block;
|
||||
|
||||
margin-top: 16px;
|
||||
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
card-footer {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: flex-end;
|
||||
|
||||
width: 100%;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
card-footer i {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
card-empty {
|
||||
width: 100%;
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
card-empty a {
|
||||
color: #9b9b9b;
|
||||
|
||||
font-size: 30px;
|
||||
font-weight: 500;
|
||||
}
|
||||
BIN
src/assets/images/plugins_icon.png
Normal file
BIN
src/assets/images/plugins_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 333 B |
BIN
src/assets/images/update_icon.png
Normal file
BIN
src/assets/images/update_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 192 B |
Binary file not shown.
|
Before Width: | Height: | Size: 393 B |
@ -156,6 +156,10 @@ browser.tabs.onUpdated.addListener( function( tabId, changeInfo, tab ) {
|
||||
browser.tabs.remove( tabId );
|
||||
}
|
||||
});
|
||||
} else if ( tab.url.startsWith( "http://simpread.ksria.cn/plugins/install/" )) {
|
||||
const url = tab.url.replace( "http://simpread.ksria.cn/plugins/install/", "" );
|
||||
browser.tabs.create({ url: browser.extension.getURL( "options/options.html#plugins?install=" + encodeURIComponent(url) ) });
|
||||
browser.tabs.remove( tabId );
|
||||
}
|
||||
|
||||
if ( !tab.url.startsWith( "chrome://" ) ) {
|
||||
|
||||
@ -133,6 +133,8 @@ export default class CommonOpt extends React.Component {
|
||||
menu.Refresh( json.option.menu );
|
||||
json.option.origins && json.option.origins.length > 0 &&
|
||||
new Notify().Render( "导入的配置文件包含了第三方源,请通过手动导入。" );
|
||||
json.option.plugins && json.option.plugins.length > 0 &&
|
||||
new Notify().Render( "导入的配置文件包含了插件,请通过手动导入。" );
|
||||
this.importsecret( json.option.secret, { ...json.secret }, () => {
|
||||
delete json.secret;
|
||||
storage.Write( ()=> {
|
||||
@ -240,7 +242,7 @@ export default class CommonOpt extends React.Component {
|
||||
</div>
|
||||
<div style={{ display: 'inline-flex', width: '100%' }}>
|
||||
<Button type="raised" text="手动同步适配列表" width="100%"
|
||||
icon={ ss.IconPath( "website_icon" ) }
|
||||
icon={ ss.IconPath( "update_icon" ) }
|
||||
color="#fff" backgroundColor="#2196F3"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.newsites() } />
|
||||
|
||||
@ -29,7 +29,7 @@ export default class FocusOpt extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const slider_width = location.protocol.includes( "extension" ) ? "565.8px" : undefined;
|
||||
const slider_width = location.protocol.includes( "extension" ) ? "660.09px" : undefined;
|
||||
return (
|
||||
<sr-opt-focus>
|
||||
<sr-opt-gp>
|
||||
|
||||
75
src/module/pluginbar.jsx
Normal file
75
src/module/pluginbar.jsx
Normal file
@ -0,0 +1,75 @@
|
||||
console.log( "=== simpread plugin bar load ===" )
|
||||
|
||||
import {storage} from 'storage';
|
||||
|
||||
import Button from 'button';
|
||||
|
||||
export default class Pluginbar extends React.Component {
|
||||
|
||||
state = {
|
||||
category: []
|
||||
};
|
||||
|
||||
category = {};
|
||||
|
||||
getCategory() {
|
||||
Object.values( storage.plugins ).forEach( ( item, idx ) => {
|
||||
if ( this.category[item.category] ) {
|
||||
this.category[item.category].push( item );
|
||||
} else {
|
||||
this.category[item.category] = [];
|
||||
this.category[item.category].push( item );
|
||||
}
|
||||
});
|
||||
this.setState({ category: Object.keys( this.category ) });
|
||||
}
|
||||
|
||||
enable( id ) {
|
||||
console.log( id, storage.plugins[id].enable )
|
||||
const plugin = storage.plugins[id];
|
||||
plugin.enable = !plugin.enable;
|
||||
storage.Plugins( () => {
|
||||
new Notify().Render( "当前插件已" + ( plugin.enable ? "启用" : "禁用" ) + ",请重新进入阅读模式以便生效。" );
|
||||
// hack code
|
||||
const bgColor = ( plugin.enable == undefined || plugin.enable == true ) ? plugin.icon.bgColor : "#c3c6c7";
|
||||
$( this.refs[id].refs.mask ).parent().css( "background-color", bgColor );
|
||||
}, storage.plugins );
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
storage.Plugins( () => {
|
||||
this.category = {};
|
||||
this.getCategory();
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const child = this.state.category.map( item => {
|
||||
|
||||
const actions = this.category[item].map( plugin => {
|
||||
const bgColor = ( plugin.enable == undefined || plugin.enable == true ) ? plugin.icon.bgColor : "#c3c6c7";
|
||||
plugin.enable == undefined && ( plugin.enable = true );
|
||||
return (
|
||||
<Button ref={plugin.id}
|
||||
shape="circle" type="flat"
|
||||
color={ plugin.icon.color } backgroundColor={ bgColor }
|
||||
tooltip={{ text: plugin.name }}
|
||||
fontIcon={ plugin.icon.type }
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.enable(plugin.id) } />
|
||||
)
|
||||
});
|
||||
|
||||
return (
|
||||
<sr-opt-gp>
|
||||
<sr-opt-label>{ item }</sr-opt-label>
|
||||
<actions style={{ display: "flex", margin: "10px 0" }}>{ actions }</actions>
|
||||
</sr-opt-gp>
|
||||
)
|
||||
});
|
||||
|
||||
return (
|
||||
<plugin-bar>{child}</plugin-bar>
|
||||
)
|
||||
}
|
||||
}
|
||||
292
src/module/plugins.jsx
Normal file
292
src/module/plugins.jsx
Normal file
@ -0,0 +1,292 @@
|
||||
console.log( "===== simpread option labs load =====" )
|
||||
|
||||
import {storage} from 'storage';
|
||||
import * as run from 'runtime';
|
||||
import * as ss from 'stylesheet';
|
||||
import {browser} from 'browser';
|
||||
import * as msg from 'message';
|
||||
import * as watch from 'watch';
|
||||
|
||||
import Button from 'button';
|
||||
|
||||
class Card extends React.Component {
|
||||
|
||||
static defaultProps = {
|
||||
plugin : {},
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
plugin : React.PropTypes.object,
|
||||
};
|
||||
|
||||
update() {
|
||||
run.Install( this.props.plugin.id, undefined, result => {
|
||||
if ( result ) {
|
||||
if ( this.props.plugin.version != result.version ) {
|
||||
storage.plugins[this.props.plugin.id] = result;
|
||||
this.props.onChange( "update" );
|
||||
} else {
|
||||
new Notify().Render( "当前插件为最新版,无需更新。" );
|
||||
}
|
||||
} else new Notify().Render( 2, "更新失败,请稍后再试。" );
|
||||
});
|
||||
}
|
||||
|
||||
delete() {
|
||||
new Notify().Render({ mode:"snackbar", content: "是否删除当前插件?", action: "确认", cancel: "取消", callback: type => {
|
||||
if ( type == "cancel" ) return;
|
||||
delete storage.plugins[ this.props.plugin.id ];
|
||||
storage.option.plugins = Object.keys( storage.plugins );
|
||||
this.props.onChange( "delete" );
|
||||
}});
|
||||
}
|
||||
|
||||
enable() {
|
||||
this.props.plugin.enable = this.props.plugin.enable == undefined || this.props.plugin.enable ? false : true;
|
||||
this.props.onChange( "enable" );
|
||||
}
|
||||
|
||||
addmore() {
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.new_tab, { url: "http://simpread.ksria.cn/plugins/details/" + this.props.plugin.id }));
|
||||
}
|
||||
|
||||
render() {
|
||||
const enable = this.props.plugin.enable == undefined || this.props.plugin.enable ? true : false;
|
||||
return (
|
||||
<card>
|
||||
<card-header style={{ backgroundColor: this.props.plugin.icon.bgColor }}>
|
||||
<icon style={{ color: this.props.plugin.icon.color }} dangerouslySetInnerHTML={{__html: this.props.plugin.icon.type }} ></icon>
|
||||
</card-header>
|
||||
<card-content>
|
||||
<desc>by { this.props.plugin.user.name }</desc>
|
||||
<note>{ this.props.plugin.name }</note>
|
||||
</card-content>
|
||||
<card-footer>
|
||||
<Button shape="circle" type="flat"
|
||||
color="#c3c6c7" hoverColor="rgba( 153, 153, 153, .1)"
|
||||
tooltip={{ text: enable == true ? "禁用当前插件" : "启用当前插件" }}
|
||||
fontIcon={`<i class="fas fa-eye${ enable ? "" : "-slash" }"></i>`}
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.enable() } />
|
||||
<Button shape="circle" type="flat"
|
||||
color="#c3c6c7" hoverColor="rgba( 153, 153, 153, .1)"
|
||||
tooltip={{ text: "删除当前插件" }}
|
||||
fontIcon='<i class="fas fa-trash-alt"></i>'
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.delete() } />
|
||||
<Button shape="circle" type="flat"
|
||||
color="#c3c6c7" hoverColor="rgba( 153, 153, 153, .1)"
|
||||
tooltip={{ text: "更新当前插件到最新版本" }}
|
||||
fontIcon='<i class="fas fa-cloud"></i>'
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.update() } />
|
||||
<Button shape="circle" type="flat"
|
||||
color="#c3c6c7" hoverColor="rgba( 153, 153, 153, .1)"
|
||||
tooltip={{ text: "查看当前插件的详细信息" }}
|
||||
fontIcon='<i class="fas fa-ellipsis-h"></i>'
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.addmore() } />
|
||||
</card-footer>
|
||||
</card>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Cards extends React.Component {
|
||||
|
||||
static defaultProps = {
|
||||
plugins : [],
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
plugins : React.PropTypes.array,
|
||||
onChange : React.PropTypes.func,
|
||||
};
|
||||
|
||||
render() {
|
||||
const card = this.props.plugins.length > 0 ? this.props.plugins.map( ( item, idx ) => {
|
||||
return (
|
||||
<Card plugin={ item } onChange={t=>this.props.onChange(t)} />
|
||||
)
|
||||
}) : <card-empty><a href="http://simpread.ksria.cn/plugins" target="_blank">没有任何扩展,点击打开扩展中心添加。</a></card-empty>;
|
||||
return (
|
||||
<cards>{ card }</cards>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default class PluginsOpt extends React.Component {
|
||||
|
||||
state = {
|
||||
plugins: []
|
||||
};
|
||||
|
||||
writecb() {
|
||||
watch.SendMessage( "option", true );
|
||||
}
|
||||
|
||||
install() {
|
||||
const id = decodeURIComponent( location.hash ).replace( "#plugins?install=", "" );
|
||||
run.Install( id, undefined, result => {
|
||||
if ( result ) {
|
||||
const add = () => {
|
||||
storage.plugins[result.id] = result;
|
||||
storage.Plugins( result => {
|
||||
new Notify().Render( "当前插件已安装成功,2 秒后自动刷新当前页面。" );
|
||||
setTimeout( ()=> {
|
||||
location.href = location.origin + location.pathname + "#plugins";
|
||||
location.reload();
|
||||
}, 2000 );
|
||||
}, storage.plugins );
|
||||
};
|
||||
if ( !storage.option.plugins.includes( result.id ) ) {
|
||||
add();
|
||||
storage.option.plugins.push( result.id );
|
||||
storage.Write( this.writecb );
|
||||
} else if ( storage.plugins[result.id].version != result.version ) {
|
||||
new Notify().Render({ content: "本地版本与安装版本不一致,是否安装新版本?", action: "安装", cancel: "取消", callback: type => {
|
||||
type == "action" && add();
|
||||
}});
|
||||
} else {
|
||||
new Notify().Render({ content: "本地版本与安装版本一致,是否重新安装?", action: "安装", cancel: "取消", callback: type => {
|
||||
type == "action" && add();
|
||||
}});
|
||||
}
|
||||
} else new Notify().Render( 2, id + " 获取失败,请稍后再试。" );
|
||||
});
|
||||
}
|
||||
|
||||
clear() {
|
||||
new Notify().Render({ mode: "snackbar", content: "是否清除本地全部插件?", action: "是的", cancel: "取消", callback: type => {
|
||||
if ( type == "action" ) {
|
||||
storage.option.plugins = [];
|
||||
storage.Write( this.writecb );
|
||||
storage.Plugins( () => {
|
||||
new Notify().Render( "snackbar", "清除成功,此页面需刷新后才能生效!", "刷新 ", ()=>{
|
||||
location.href = location.origin + location.pathname + "#plugins";
|
||||
location.reload();
|
||||
});
|
||||
}, {} );
|
||||
}
|
||||
}});
|
||||
}
|
||||
|
||||
addmore() {
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.new_tab, { url: "http://simpread.ksria.cn/plugins" }));
|
||||
}
|
||||
|
||||
update() {
|
||||
let is_update = false, count = 0;
|
||||
storage.option.plugins.forEach( id => {
|
||||
run.Install( id, undefined, result => {
|
||||
if ( !result ) {
|
||||
new Notify().Render( 2, id + "获取失败,请稍后再试。" );
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
if ( storage.plugins[id].version != result.version ) {
|
||||
storage.plugins[result.id] = result;
|
||||
is_update = true;
|
||||
}
|
||||
count == storage.option.plugins.length && complete();
|
||||
});
|
||||
});
|
||||
const complete = () => {
|
||||
if ( is_update ) {
|
||||
storage.Plugins( result => {
|
||||
new Notify().Render( "本地插件已全部更新完毕。" );
|
||||
this.setState({ plugins: Object.values( storage.plugins ) });
|
||||
}, storage.plugins );
|
||||
} else {
|
||||
new Notify().Render( "无任何可用更新。" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import() {
|
||||
new Notify().Render({ mode:"snackbar", content: "导入意味着从配置文件覆盖当前的插件!", action: "确认", cancel: "取消", callback: type => {
|
||||
if ( type == "cancel" ) return;
|
||||
let count = 0;
|
||||
storage.option.plugins.forEach( id => {
|
||||
run.Install( id, undefined, result => {
|
||||
if ( !result ) {
|
||||
new Notify().Render( 2, id + "获取失败,请稍后再试。" );
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
storage.plugins[result.id] = result;
|
||||
count == storage.option.plugins.length && complete();
|
||||
});
|
||||
});
|
||||
}});
|
||||
const complete = () => {
|
||||
storage.Plugins( result => {
|
||||
storage.option.plugins = Object.keys( storage.plugins );
|
||||
storage.Write( () => {
|
||||
new Notify().Render( "已从配置文件导入完毕。" );
|
||||
this.setState({ plugins: Object.values( storage.plugins ) });
|
||||
});
|
||||
}, storage.plugins );
|
||||
}
|
||||
}
|
||||
|
||||
onChange( type ) {
|
||||
storage.Write( this.writecb );
|
||||
storage.Plugins( () => {
|
||||
type == "update" && new Notify().Render( "当前插件已更新成功。" );
|
||||
type == "delete" && new Notify().Render( "当前插件已删除成功。" );
|
||||
type == "enable" && new Notify().Render( "当前插件已更改成功。" );
|
||||
this.setState({ plugins: Object.values( storage.plugins ) });
|
||||
}, storage.plugins );
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
$( "head" ).append( '<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/solid.css" integrity="sha384-TbilV5Lbhlwdyc4RuIV/JhD8NR+BfMrvz4BL5QFa2we1hQu6wvREr3v6XSRfCTRp" crossorigin="anonymous">' );
|
||||
$( "head" ).append( '<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/fontawesome.css" integrity="sha384-ozJwkrqb90Oa3ZNb+yKFW2lToAWYdTiF1vt8JiH5ptTGHTGcN7qdoR1F95e0kYyG" crossorigin="anonymous">' );
|
||||
storage.Plugins( () => {
|
||||
decodeURIComponent( location.href ).includes( "#plugins?install=" ) && this.install();
|
||||
this.setState({ plugins: Object.values( storage.plugins ) });
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id="labs" style={{ width: '100%' }}>
|
||||
<div className="label">全局</div>
|
||||
<div className="lab">
|
||||
<div style={{ display: 'inline-flex', width: '100%' }}>
|
||||
<Button type="raised" text="从配置文件导入插件" width="100%"
|
||||
tooltip={{ text: "注意:本操作并不能更新本地插件。" }}
|
||||
icon={ ss.IconPath( "import_icon" ) }
|
||||
color="#fff" backgroundColor="#00BCD4"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.import() } />
|
||||
<Button type="raised" text="查看并获取更多的插件" width="100%"
|
||||
fontIcon={`<i class="fas fa-external-link-square-alt"></i>`}
|
||||
color="#fff" backgroundColor="#00BCD4"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.addmore() } />
|
||||
</div>
|
||||
<div style={{ display: 'inline-flex', width: '100%' }}>
|
||||
<Button type="raised" text="更新本地全部插件" width="100%"
|
||||
icon={ ss.IconPath( "update_icon" ) }
|
||||
color="#fff" backgroundColor="#FF5252"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.update() } />
|
||||
<Button type="raised" text="清除本地全部插件" width="100%"
|
||||
icon={ ss.IconPath( "clear_icon" ) }
|
||||
color="#fff" backgroundColor="#757575"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.clear() } />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="label">管理</div>
|
||||
<div style={{ 'padding-top': '10px' }} className="lab">
|
||||
<Cards plugins={ this.state.plugins } onChange={ t=>this.onChange(t) } />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -76,7 +76,7 @@ export default class ReadOpt extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const slider_width = location.protocol.includes( "extension" ) ? "565.8px" : undefined;
|
||||
const slider_width = location.protocol.includes( "extension" ) ? "660.09px" : undefined;
|
||||
return (
|
||||
<sr-opt-read>
|
||||
<sr-opt-gp>
|
||||
|
||||
@ -26,10 +26,11 @@ import FocusOpt from 'focusopt';
|
||||
import ReadOpt from 'readopt';
|
||||
import CommonOpt from 'commonopt';
|
||||
import LabsOpt from 'labsopt';
|
||||
import PluginsOpt from 'pluginsopt';
|
||||
import About from 'about';
|
||||
import Unrdist from 'unrdist';
|
||||
import * as welc from 'welcome';
|
||||
import * as plug from 'plugin';
|
||||
import * as run from 'runtime';
|
||||
|
||||
import PureRead from 'puread';
|
||||
|
||||
@ -181,7 +182,7 @@ function welcomeRender( first, version ) {
|
||||
function mainRender( idx ) {
|
||||
$( ".top" ).css( "background-color", conf.topColors[idx] );
|
||||
$( ".header" ).css( "background-color", conf.topColors[idx] ).find( ".title" ).text( conf.tabsItem[idx].name );
|
||||
( idx == 3 || idx == 5 ) ? $( '.main' ).addClass( "main_labs" ) : $( '.main' ).removeClass( "main_labs" );
|
||||
( idx == 3 || idx == 4 || idx == 6 ) ? $( '.main' ).addClass( "main_labs" ) : $( '.main' ).removeClass( "main_labs" );
|
||||
tabsRender( conf.headerColors[ idx ] );
|
||||
}
|
||||
|
||||
@ -218,6 +219,9 @@ function tabsRender( color ) {
|
||||
<section style={{ 'padding': '0;' }}>
|
||||
<LabsOpt option={ storage.option } read={ storage.read } focus={ storage.focus } onChange={ (s)=>save(s) } />
|
||||
</section>
|
||||
<section style={{ 'padding': '0;' }}>
|
||||
<PluginsOpt />
|
||||
</section>
|
||||
<section><Unrdist list={ storage.unrdist.map( item => { return { ...item }} ) } /></section>
|
||||
<section style={{ 'padding': '0;' }}><About option={ storage.option } site={ storage.simpread.sites.length } statistics={ storage.simpread.statistics } onClick={t=>welcomeRender(true)}/></section>
|
||||
</Tabs>,
|
||||
@ -268,7 +272,7 @@ function sidebarRender() {
|
||||
* set user uid
|
||||
*/
|
||||
function setUserUID() {
|
||||
storage.user.uid = plug.ID( "user" );
|
||||
storage.user.uid = run.ID( "user" );
|
||||
storage.Write( () => {
|
||||
console.log( "current user info create!" )
|
||||
watch.SendMessage( "option", true );
|
||||
|
||||
@ -12,6 +12,7 @@ import { storage } from 'storage';
|
||||
|
||||
import ReadOpt from 'readopt';
|
||||
import Actionbar from 'actionbar';
|
||||
import Pluginbar from 'pluginbar';
|
||||
|
||||
import Fab from 'fab';
|
||||
import Fap from 'fap'
|
||||
@ -137,12 +138,13 @@ export default class ReadCtlbar extends React.Component {
|
||||
|
||||
render() {
|
||||
const Controlbar = storage.current.fap ?
|
||||
<Fap items={ [ "样式", "动作" ] } autoHide={ false }
|
||||
<Fap items={ [ "样式", "动作", "插件" ] } autoHide={ false }
|
||||
waves="md-waves-effect md-waves-circle md-waves-float"
|
||||
onOpen={ ()=> this.onPop( "open" ) } onClose={ ()=> this.onPop( "close" ) }
|
||||
onAction={ (event, type)=>this.onAction(event, type ) }>
|
||||
<ReadOpt option={ storage.current } onChange={ (t,c)=>this.onChange(t,c)}/>
|
||||
<Actionbar items={ conf.readItems } onAction={ (type)=>this.onAction(undefined, type ) }/>
|
||||
<Pluginbar />
|
||||
</Fap>
|
||||
:
|
||||
<Fab items={ conf.readItems } tooltip={ tooltip_options } waves="md-waves-effect md-waves-circle md-waves-float" onAction={ (event, type)=>this.onAction(event, type ) } />
|
||||
|
||||
@ -14,6 +14,7 @@ import * as ss from 'stylesheet';
|
||||
import {browser} from 'browser';
|
||||
import * as msg from 'message';
|
||||
import * as highlight from 'highlight';
|
||||
import * as run from 'runtime';
|
||||
|
||||
import * as tooltip from 'tooltip';
|
||||
import * as waves from 'waves';
|
||||
@ -40,8 +41,15 @@ const Footer = () => {
|
||||
class Read extends React.Component {
|
||||
|
||||
componentWillMount() {
|
||||
loadPlugins( "read_start" );
|
||||
$( "body" ).addClass( "simpread-hidden" );
|
||||
th.Change( this.props.read.theme );
|
||||
// hack code
|
||||
//storage.current.fap && $( "head" ).append( '<link rel="stylesheet" class="simpread-fs-style" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp" crossorigin="anonymous">' );
|
||||
if ( storage.current.fap ) {
|
||||
$( "head" ).append( '<link rel="stylesheet" class="simpread-fs-style" href="https://use.fontawesome.com/releases/v5.1.0/css/solid.css" integrity="sha384-TbilV5Lbhlwdyc4RuIV/JhD8NR+BfMrvz4BL5QFa2we1hQu6wvREr3v6XSRfCTRp" crossorigin="anonymous">' );
|
||||
$( "head" ).append( '<link rel="stylesheet" class="simpread-fs-style" href="https://use.fontawesome.com/releases/v5.1.0/css/fontawesome.css" integrity="sha384-ozJwkrqb90Oa3ZNb+yKFW2lToAWYdTiF1vt8JiH5ptTGHTGcN7qdoR1F95e0kYyG" crossorigin="anonymous">' );
|
||||
}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
@ -83,10 +91,13 @@ class Read extends React.Component {
|
||||
tooltip.Render( rdclsjq );
|
||||
waves.Render({ root: rdclsjq });
|
||||
storage.Statistics( "read" );
|
||||
|
||||
loadPlugins( "read_complete" );
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
loadPlugins( "read_end" );
|
||||
ss.FontSize( "" );
|
||||
$root.removeClass( theme )
|
||||
.removeClass( "simpread-font" );
|
||||
@ -248,4 +259,17 @@ function excludes( $target, exclude ) {
|
||||
$target.find( tags ).remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plugins from storage and exec
|
||||
*
|
||||
* @param {string} state include: plugin.run_at
|
||||
*/
|
||||
function loadPlugins( state ) {
|
||||
storage.Plugins( () => {
|
||||
storage.option.plugins.forEach( id => {
|
||||
storage.plugins[id] && run.Exec( state, storage.current.site.name, storage.plugins[id] );
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export { Render, Exist, Exit, Highlight };
|
||||
|
||||
@ -81,8 +81,8 @@ class TOC extends React.Component {
|
||||
function Render( root, $target, theme, hidden ) {
|
||||
|
||||
// hack code
|
||||
if ( location.host.includes( "blog.csdn.net" )) return;
|
||||
if ( location.host.includes( "post.smzdm.com" )) return;
|
||||
//if ( location.host.includes( "blog.csdn.net" )) return;
|
||||
//if ( location.host.includes( "post.smzdm.com" )) return;
|
||||
|
||||
const table = [],
|
||||
cls = hidden ? "toc-bg-hidden" : "";
|
||||
|
||||
@ -566,6 +566,10 @@ const tabsItem = [{
|
||||
name: "高级设定",
|
||||
value: "labs",
|
||||
route: "#labs",
|
||||
},{
|
||||
name: "插件管理",
|
||||
value: "plugins",
|
||||
route: "#plugins",
|
||||
},{
|
||||
name: "稍后读",
|
||||
value: "later",
|
||||
@ -579,8 +583,8 @@ const tabsItem = [{
|
||||
value: "help",
|
||||
route: "https://github.com/kenshin/simpread/wiki",
|
||||
}],
|
||||
headerColors = [ "#64B5F6", "#81C784", "#9575CD", "#7986CB", "#BA68C8", "#4DB6AC" ],
|
||||
topColors = [ "#2196F3", "#4CAF50", "#673AB7", "#3F51B5", "#9C27B0", "#009688" ],
|
||||
headerColors = [ "#64B5F6", "#81C784", "#9575CD", "#7986CB", "#4DD0E1", "#BA68C8", "#4DB6AC" ],
|
||||
topColors = [ "#2196F3", "#4CAF50", "#673AB7", "#3F51B5", "#00BCD4", "#9C27B0", "#009688" ],
|
||||
menuItem = tabsItem.map( ( item, idx ) => {
|
||||
const menu = { ...item };
|
||||
switch ( idx ) {
|
||||
@ -598,12 +602,15 @@ const tabsItem = [{
|
||||
menu.icon = ss.IconPath( "labs_icon" );
|
||||
break;
|
||||
case 4:
|
||||
menu.icon = ss.IconPath( "read_later_icon" );
|
||||
menu.icon = ss.IconPath( "plugins_icon" );
|
||||
break;
|
||||
case 5:
|
||||
menu.icon = ss.IconPath( "about_icon" );
|
||||
menu.icon = ss.IconPath( "read_later_icon" );
|
||||
break;
|
||||
case 6:
|
||||
menu.icon = ss.IconPath( "about_icon" );
|
||||
break;
|
||||
case 7:
|
||||
menu.icon = ss.IconPath( "help_icon" );
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
|
||||
import nanoid from 'nanoid';
|
||||
|
||||
/**
|
||||
* Generate ID
|
||||
*
|
||||
* @param {string} generate id, include: user id( uuid v4 ), plugin id( like t.co/cKPFh3Qsh4 )
|
||||
*/
|
||||
function generateID( type ) {
|
||||
if ( type == "user" ) {
|
||||
const random = "0123456789abcdefghijklmnopqrstuvwxyz",
|
||||
first = nanoid( random, 8 ),
|
||||
second = nanoid( random, 4 ),
|
||||
third = nanoid( random, 4 ),
|
||||
fourth = nanoid( random, 4 ),
|
||||
fifth = nanoid( random, 12 );
|
||||
return `${first}-${second}-${third}-${fourth}-${fifth}`;
|
||||
} else if ( type == "plugin" ) {
|
||||
return nanoid( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 10 );
|
||||
}
|
||||
}
|
||||
|
||||
function install( id ) {
|
||||
|
||||
}
|
||||
|
||||
function exec( str ) {
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
install as Install,
|
||||
exec as Exec,
|
||||
generateID as ID,
|
||||
}
|
||||
98
src/service/runtime.js
Normal file
98
src/service/runtime.js
Normal file
@ -0,0 +1,98 @@
|
||||
console.log( "=== simpread runtime load ===" )
|
||||
|
||||
import nanoid from 'nanoid';
|
||||
import {browser} from 'browser';
|
||||
import {storage, Clone} from 'storage';
|
||||
|
||||
/**
|
||||
* Generate ID
|
||||
*
|
||||
* @param {string} generate id, include: user id( uuid v4 ), plugin id( like t.co/cKPFh3Qsh4 )
|
||||
*/
|
||||
function generateID( type ) {
|
||||
if ( type == "user" ) {
|
||||
const random = "0123456789abcdefghijklmnopqrstuvwxyz",
|
||||
first = nanoid( random, 8 ),
|
||||
second = nanoid( random, 4 ),
|
||||
third = nanoid( random, 4 ),
|
||||
fourth = nanoid( random, 4 ),
|
||||
fifth = nanoid( random, 12 );
|
||||
return `${first}-${second}-${third}-${fourth}-${fifth}`;
|
||||
} else if ( type == "plugin" ) {
|
||||
return nanoid( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 10 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install plugin
|
||||
*
|
||||
* @param {string} id e.g. kw36BtjGu0
|
||||
* @param {string} url
|
||||
*/
|
||||
function install( id, url, callback ) {
|
||||
url = id ? `http://simpread.ksria.cn/plugins/srplug/${id}.srplug` : url;
|
||||
url = url + `?${+new Date()}`;
|
||||
console.log( "install url is", url )
|
||||
$.ajax({ url, dataType: "json" })
|
||||
.done( result => callback( result, undefined ))
|
||||
.fail( () => callback( undefined, "error" ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @param {string} state, include: read_start, read_loading, read_complete, read_end
|
||||
* @param {string} site name, inlcude: "", "xxx", "xxx, yyy"
|
||||
* @param {object} plugin object
|
||||
*/
|
||||
function exec( state, site, plugin ) {
|
||||
if ( plugin.enable == false ) return;
|
||||
if ( plugin.run_at != state ) return;
|
||||
if ( plugin.site != "" && !plugin.site.split(",").includes( site ) ) return;
|
||||
console.log( "current plugin is running", plugin )
|
||||
new Function( func( plugin.script ) )();
|
||||
plugin.style != "" && addStyle( plugin.style );
|
||||
}
|
||||
|
||||
/**
|
||||
* Contact (function(){})() string
|
||||
*
|
||||
* @param {string} source
|
||||
*/
|
||||
function func( source ) {
|
||||
window.Notify = Notify;
|
||||
window.browser = browser;
|
||||
window.current = Clone( storage.pr.current );
|
||||
window.read = Clone( storage.read );
|
||||
return `( function ( $$version, $title, $desc, $content, $footer, $process, $toc, Notify, browser, $$current, $$read ) {
|
||||
${ source }
|
||||
})( "0.0.1", $( "sr-rd-title" ), $( "sr-rd-desc" ), $( "sr-rd-content" ), $( "sr-rd-footer" ), $( "read-process" ), $( "toc" ), Notify, browser, current, read );`
|
||||
}
|
||||
|
||||
/**
|
||||
* Add style
|
||||
*
|
||||
* @param {string} add css to head
|
||||
*/
|
||||
function addStyle( str ) {
|
||||
$( "head" ).append(`<style type="text/css">${str}</style>`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Plugin
|
||||
*
|
||||
* @param {func} style func
|
||||
* @param {func} plugin func
|
||||
*/
|
||||
function testPlugin( style, plugin ) {
|
||||
style && addStyle( style() );
|
||||
plugin && plugin( "0.0.1", $( "sr-rd-title" ), $( "sr-rd-desc" ), $( "sr-rd-content" ), $( "sr-rd-footer" ), $( "read-process" ), $( "toc" ), Notify, browser, storage.pr.current, storage.read );
|
||||
}
|
||||
|
||||
window.simpread = { testPlugin };
|
||||
|
||||
export {
|
||||
install as Install,
|
||||
exec as Exec,
|
||||
generateID as ID,
|
||||
}
|
||||
@ -119,7 +119,8 @@ const name = "simpread",
|
||||
origins : [],
|
||||
blacklist : [
|
||||
"google.com",
|
||||
]
|
||||
],
|
||||
plugins : [], // plugin id, e.g. kw36BtjGu0
|
||||
},
|
||||
statistics = {
|
||||
"focus" : 0,
|
||||
@ -176,7 +177,8 @@ let current = {},
|
||||
statistics,
|
||||
user,
|
||||
},
|
||||
secret = {
|
||||
plugins = {},
|
||||
secret = {
|
||||
version : "2017-11-22",
|
||||
"dropbox" : {
|
||||
"access_token": ""
|
||||
@ -312,6 +314,15 @@ class Storage {
|
||||
return secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugins data structure
|
||||
*
|
||||
* @return {object} plugins object
|
||||
*/
|
||||
get plugins() {
|
||||
return plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all sites structure
|
||||
*
|
||||
@ -653,6 +664,28 @@ class Storage {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugins set/get, plugins not import/export
|
||||
*
|
||||
* @param {object} plugins
|
||||
* @param {function} callback
|
||||
*/
|
||||
Plugins( callback, data ) {
|
||||
if ( data ) {
|
||||
plugins = { ...data };
|
||||
browser.storage.local.set( { ["plugins"] : plugins }, () => {
|
||||
console.log( "chrome storage plugins set success!", plugins );
|
||||
callback && callback();
|
||||
});
|
||||
} else {
|
||||
browser.storage.local.get( ["plugins"], result => {
|
||||
console.log( "chrome storage plugins get success!", result );
|
||||
result && !$.isEmptyObject( result ) && ( plugins = result["plugins"] );
|
||||
callback && callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export, only include: version, option, focus, read, unrdist
|
||||
*
|
||||
|
||||
@ -148,6 +148,7 @@ function Verify( curver, data ) {
|
||||
|
||||
if ( curver == "1.1.1" ) {
|
||||
data.user = { "uid": "","name": "","email": "","avatar": "","permission": "" };
|
||||
data.option.plugins = [];
|
||||
curver = "1.1.2";
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
/*!
|
||||
* React Material Design: Button
|
||||
*
|
||||
* @version : 0.0.3
|
||||
* @update : 2018/04/26
|
||||
* @version : 0.0.4
|
||||
* @update : 2018/06/21
|
||||
* @homepage: https://github.com/kenshin/mduikit-ui
|
||||
* @license : MIT https://github.com/kenshin/mduikit/blob/master/LICENSE
|
||||
* @author : Kenshin Wang <kenshin@ksria.com>
|
||||
@ -122,6 +122,23 @@ const cssinjs = () => {
|
||||
backgroundRepeat: 'no-repeat',
|
||||
},
|
||||
|
||||
font_icon: {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
|
||||
fontSize: '18px',
|
||||
color: '#fff',
|
||||
|
||||
order: -1,
|
||||
display: 'block',
|
||||
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
|
||||
border: 'none',
|
||||
},
|
||||
|
||||
circle: {
|
||||
borderRadius: '50%',
|
||||
},
|
||||
@ -151,6 +168,7 @@ export default class Button extends React.Component {
|
||||
text : "",
|
||||
disable : false,
|
||||
icon : "",
|
||||
fontIcon: "",
|
||||
order : "before",
|
||||
type : "flat",
|
||||
mode : "primary",
|
||||
@ -170,6 +188,7 @@ export default class Button extends React.Component {
|
||||
text : React.PropTypes.string,
|
||||
disable : React.PropTypes.bool,
|
||||
icon : React.PropTypes.string,
|
||||
fontIcon: React.PropTypes.string,
|
||||
order : React.PropTypes.oneOf([ "before", "after" ]),
|
||||
type : React.PropTypes.oneOf([ "flat", "raised" ]),
|
||||
mode : React.PropTypes.oneOf([ "primary", "secondary" ]),
|
||||
@ -218,7 +237,7 @@ export default class Button extends React.Component {
|
||||
current.color = this.state.color;
|
||||
current.backgroundColor = this.state.backgroundColor;
|
||||
|
||||
if ( this.props.text == "" && this.props.icon != "" ) {
|
||||
if ( this.props.text == "" && ( this.props.icon != "" || this.props.fontIcon != "" )) {
|
||||
delete style.normal_root.minWidth;
|
||||
delete style.normal_root.borderRadius;
|
||||
style.normal_root.width = style.normal_root.height;
|
||||
@ -242,7 +261,13 @@ export default class Button extends React.Component {
|
||||
( style.root = { ...style.root, ...this.props.style } );
|
||||
|
||||
this.props.text == "" && ( style.text.display = "none" );
|
||||
this.props.icon != "" ? ( style.icon.backgroundImage = `url(${this.props.icon})` ) : ( style.icon.display = "none" );
|
||||
|
||||
if ( this.props.fontIcon != "" ) {
|
||||
style.icon = { ...style.font_icon };
|
||||
style.icon.display = "flex";
|
||||
style.icon.color = style.color;
|
||||
} else this.props.icon != "" ? ( style.icon.backgroundImage = `url(${this.props.icon})` ) : ( style.icon.display = "none" );
|
||||
|
||||
this.props.order == "after" && ( style.icon.order = 1 );
|
||||
this.props.width && ( style.root.width = this.props.width );
|
||||
|
||||
@ -261,7 +286,7 @@ export default class Button extends React.Component {
|
||||
{ ...events }>
|
||||
<button-mask ref="mask" style={ style.mask }>
|
||||
<button-span style={ style.span }>
|
||||
<button-icon style={ style.icon }></button-icon>
|
||||
<button-icon style={ style.icon } dangerouslySetInnerHTML={{__html: this.props.fontIcon }} ></button-icon>
|
||||
<button-text style={ style.text }>{ this.props.text }</button-text>
|
||||
</button-span>
|
||||
</button-mask>
|
||||
|
||||
@ -249,7 +249,7 @@ const webpack = require( 'webpack' ),
|
||||
export : __dirname + '/src/service/export.js',
|
||||
highlight : __dirname + '/src/service/highlight.js',
|
||||
output : __dirname + '/src/service/output.js',
|
||||
plugin : __dirname + '/src/service/plugin.js',
|
||||
runtime : __dirname + '/src/service/runtime.js',
|
||||
|
||||
focus : __dirname + '/src/focus/focus.js',
|
||||
controlbar : __dirname + '/src/focus/controlbar.jsx',
|
||||
@ -265,6 +265,7 @@ const webpack = require( 'webpack' ),
|
||||
focusopt : __dirname + '/src/module/focus.jsx',
|
||||
readopt : __dirname + '/src/module/read.jsx',
|
||||
commonopt : __dirname + '/src/module/common.jsx',
|
||||
pluginsopt : __dirname + '/src/module/plugins.jsx',
|
||||
labsopt : __dirname + '/src/module/labs.jsx',
|
||||
about : __dirname + '/src/module/about.jsx',
|
||||
unrdist : __dirname + '/src/module/unrdist.jsx',
|
||||
@ -272,6 +273,7 @@ const webpack = require( 'webpack' ),
|
||||
authorize : __dirname + '/src/module/authorize.jsx',
|
||||
siteeditor : __dirname + '/src/module/siteeditor.jsx',
|
||||
actionbar : __dirname + '/src/module/actionbar.jsx',
|
||||
pluginbar : __dirname + '/src/module/pluginbar.jsx',
|
||||
editor : __dirname + '/src/module/common/editor.jsx',
|
||||
themesel : __dirname + '/src/module/common/theme.jsx',
|
||||
shortcuts : __dirname + '/src/module/common/shortcuts.jsx',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user