Skip to content

增加插件系统的开发支持 #56

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"asyncArrow": "ignore"
}],
"jsx-quotes": ["error", "prefer-single"],
"react/jsx-first-prop-new-line": 0,
"react/jsx-first-prop-new-line": 0
},
"settings": {
"import/resolver": {
Expand All @@ -32,6 +32,8 @@
},
"globals": {
"i18n": false,
"extension": false
"extension": false,
"__DEV__": false,
"__PACKAGE_SERVER__": false
}
}
9 changes: 7 additions & 2 deletions app/backendAPI/packageAPI.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
/* @flow weak */
import { request } from '../utils'
import config from '../config'
const packageServer = config.packageServer

const { packageServer } = config

export const fetchPackageList = () => request.get(`${packageServer}/packages`)
export const fetchPackageInfo = (pkgName) => request.get(`${packageServer}/packages/${pkgName}`)
export const fetchPackageScript = (pkgName) => request.get(`${packageServer}/packages/${pkgName}/download`)
export const fetchPackageScript = (pkgName, debugServer) => {
if (debugServer) {
return request.get(`${debugServer}/${pkgName}.js`)
}
}
41 changes: 25 additions & 16 deletions app/components/Package/PackageControlView.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,33 @@ class PackageControlView extends Component {
render () {
const { remotePackages, localPackages, dispatch } = this.props
return (
<div name="container">
<div name="remote_extensions">
<div name='container'>
<div name='remote_extensions'>
<h3>Available Packages:</h3>
{ _.map(remotePackages, pkg =>
{_.map(remotePackages, pkg =>
<div key={pkg.name} style={{ display: 'flex' }}>
<div>name: {pkg.name}</div>
{ false && localPackages[pkg.name]
? <button disabled={true} >Installed</button>
: <button onClick={e=>dispatch(fetchPackage(pkg.name))}>
<div>插件名字: {pkg.displayName}</div>
{false && localPackages[pkg.name]
? <button disabled >Installed</button>
: <button onClick={e => dispatch(fetchPackage(pkg.name))}>
Install
</button>
}
</div>
)}
</div>
<div name="local_packages">
<div name="enabledπ">
<div name='local_packages'>
<div name='enabled'>
<h3>Installed Packages:</h3>
{ _.map(localPackages, pkg =>
{_.map(localPackages, pkg =>
<div key={pkg.name} style={{ display: 'flex' }}>
<div>name: {pkg.name}</div>
<label><input type='checkbox' onChange={e=>dispatch(togglePackage(pkg.name))} checked={pkg.enabled}/>
{ pkg.enabled ? 'Enabled' : 'Disabled' }
<label><input
type='checkbox'
onChange={e => dispatch(togglePackage(pkg.name, e.target.checked))}
checked={pkg.enabled}
/>
enabled
</label>
</div>
)}
Expand All @@ -51,8 +55,13 @@ class PackageControlView extends Component {
}


export default connect(state => ({
remotePackages: state.PackageState.remotePackages,
localPackages: state.PackageState.localPackages
})
export default connect(state => {
const {
PackageState: { remotePackages, localPackages }
} = state
return ({
remotePackages: Object.keys(remotePackages).map(key => remotePackages[key]),
localPackages
})
}
)(PackageControlView)
78 changes: 58 additions & 20 deletions app/components/Package/actions.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,73 @@
import { createAction } from 'redux-actions'
import { request } from '../../utils'
import api from '../../backendAPI'

export const PACKAGE_UPDATE_LIST = 'PACKAGE_UPDATE_LIST'
export const updatePackageList = createAction(PACKAGE_UPDATE_LIST)
export const updatePackageList = createAction(PACKAGE_UPDATE_LIST, list => list)


export const fetchPackageList = () => dispatch => {
api.fetchPackageList().then(list => dispatch(updatePackageList(list)))
api.fetchPackageList()
.then(list => dispatch(updatePackageList(list)))
}

export const PACKAGE_UPDATE_LOCAL = 'PACKAGE_UPDATE_LOCAL'
export const updateLocalPackage = createAction(PACKAGE_UPDATE_LOCAL)

export const PACKAGE_TOGGLE = 'PACKAGE_TOGGLE'
export const togglePackage = createAction(PACKAGE_TOGGLE, (pkgId, shouldEnable) => ({
id: pkgId,
shouldEnable: shouldEnable
}))

export const fetchPackage = (pkgId) => (dispatch, getState) => {
const pkgInfo = api.fetchPackageInfo(pkgId)
const pkgScript = api.fetchPackageScript(pkgId)

// 往本地localstoage写一个插件的信息

export const updateLocalPackage = createAction(PACKAGE_UPDATE_LOCAL, p => p)
export const togglePackage = createAction(PACKAGE_TOGGLE, (pkgId, shouldEnable) => {
const script = localStorage.getItem(pkgId)
if (!shouldEnable) {
delete window.extensions[pkgId]
} else {
try {
const component = eval(script)
window.extensions[pkgId] = component
} catch (err) {
throw err
}
}
return ({
id: pkgId,
shouldEnable,
})
})

export const fetchPackage = (pkgId) => (dispatch) => {
const pkgInfo = api.fetchPackageInfo(pkgId, __PACKAGE_SERVER__)
const pkgScript = api.fetchPackageScript(pkgId, __PACKAGE_SERVER__)
.then(script => {
let storageKey = `CodingPackage___${pkgId}`
localStorage.setItem(storageKey, script)
return storageKey
localStorage.setItem(pkgId, script)
return pkgId
})

Promise.all([pkgInfo, pkgScript]).then(([pkg, storageKey]) => {
dispatch(updateLocalPackage({...pkg, storageKey, enabled: true}))
Promise.all([pkgInfo, pkgScript]).then(([pkg, id]) => {
dispatch(updateLocalPackage({
...pkg,
enabled: Boolean(window.extensions[pkgId]),
id
}))
dispatch(togglePackage(pkgId, true))
})

// return api.fetchPackageScript(pkgId, __PACKAGE_SERVER__)
// .then(script => {
// const storageKey = `CodingPackage___${pkgId}`
// localStorage.setItem(storageKey, script)
// dispatch(updateLocalPackage, {

// })
// return storageKey
// })
// const pkgInfo = api.fetchPackageInfo(pkgId)
// const pkgScript = api.fetchPackageScript(pkgId)
// .then(script => {
// const storageKey = `CodingPackage___${pkgId}`
// localStorage.setItem(storageKey, script)
// return storageKey
// })
// Promise.all([pkgInfo, pkgScript]).then(([pkg, storageKey]) => {
// dispatch(updateLocalPackage({ ...pkg, storageKey, enabled: true }))
// })
}

export const PACKAGE_ACTIVATE_EXTENSION = 'PACKAGE_ACTIVATE_EXTENSION'
Expand Down
38 changes: 18 additions & 20 deletions app/components/Package/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,29 @@ const defaultState = {

const toggleExtensionAvailability = (state, pkgId) => {
// handle availability state of extension packages in side panels
let pkg = state.localPackages[pkgId]
const pkg = state.localPackages[pkgId]
if (!pkg.type === 'extension' || !pkg.ui || !pkg.ui.position) return state

let extensionIds = state.extensionsUIState.panels[pkg.ui.position].extensionIds
const extensionIds = state.extensionsUIState.panels[pkg.ui.position].extensionIds
if (extensionIds.includes(pkgId) === pkg.enabled) return state

return update(state, {
const nextState = update(state, {
extensionsUIState: { panels: { [pkg.ui.position]: {
extensionIds: pkg.enabled
? {'$push' : [pkgId] }
: {'$without': pkgId }
}}}
? { $push: [pkgId] }
: { $without: pkgId }
} }
}
})

return nextState
}

export default handleActions({
[updatePackageList]: (state, action) => {
return { ...state, remotePackages: action.payload }
},
[updatePackageList]: (state, action) => ({ ...state, remotePackages: action.payload }),

[updateLocalPackage]: (state, action) => {
const packageObj = action.payload
let nextState = update(state, {
const nextState = update(state, {
localPackages: {
[packageObj.id]: { $set: packageObj }
}
Expand All @@ -62,12 +61,13 @@ export default handleActions({
},

[togglePackage]: (state, action) => {
let { id, shouldEnable } = action.payload
let targetPackage = state.localPackages[id]
const { id } = action.payload
let { shouldEnable } = action.payload
const targetPackage = state.localPackages[id]
if (shouldEnable === targetPackage.enabled) return state
if (typeof shouldEnable !== 'boolean') shouldEnable = !targetPackage.enabled

let nextState = update(state, {
const nextState = update(state, {
localPackages: {
[id]: { enabled: { $set: shouldEnable } }
}
Expand All @@ -79,9 +79,7 @@ export default handleActions({
}, defaultState)


const getPanelByRef = (PanelState, ref) => {
return PanelState.panels[PanelState.panelRefs[ref]]
}
const getPanelByRef = (PanelState, ref) => PanelState.panels[PanelState.panelRefs[ref]]
export const PackageCrossReducer = handleActions({
[activateExtenstion]: (allState, action) => {
let nextState = allState
Expand All @@ -106,7 +104,7 @@ export const PackageCrossReducer = handleActions({

// special case: redistribute size between center and right panel
if (targetPanel.ref === 'PANEL_RIGHT') {
let centerPanel = getPanelByRef(nextState.PanelState, 'PANEL_CENTER')
const centerPanel = getPanelByRef(nextState.PanelState, 'PANEL_CENTER')
let centerPanelSize = shouldHidePanel
? centerPanel.size + targetPanel.size
: centerPanel.size - targetPanel.size
Expand All @@ -125,9 +123,9 @@ export const PackageCrossReducer = handleActions({

return update(nextState, {
PackageState: {
extensionsUIState: { panels: {[panelSide]: {
extensionsUIState: { panels: { [panelSide]: {
activeExtenstionId: { $set: packageId }
}}}
} } }
}
})
}
Expand Down
8 changes: 5 additions & 3 deletions app/components/Panel/ExtensionPanelContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import _ from 'lodash'


const _ExtensionContainer = ({ extension, isActive }) => {
const script = localStorage.getItem(extension.storageKey)
let reactElement = eval(script) // <- this should be store in IDE Environment object
return <div style={{display: isActive ? 'block' : 'none'}}>{reactElement}</div>
const reactElement = window.extensions[extension.name].app
// <- this should be store in window
return (<div style={{ display: isActive ? 'block' : 'none' }}>
{React.createElement(reactElement)}
</div>)
}

const ExtensionContainer = connect((state, { extension }) => {
Expand Down
1 change: 0 additions & 1 deletion app/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ export default {
projectName: '',
baseURL: '' || window.location.origin,
spaceKey: '',
extensionServer: 'http://localhost:8083' || process.env.EXTENSION_SERVER,
packageServer: 'http://localhost:8081' || process.env.CODING_PACKAGE_SERVER,
requiredExtensions: []
}
2 changes: 1 addition & 1 deletion app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { createI18n, getExtensions } from './utils'

window.i18n = createI18n
window.extensions = {}
window.extension = f=>null
window.extension = f => getExtensions

const app = React.createElement(Root)
render(app, document.getElementById('root'))
2 changes: 1 addition & 1 deletion app/localStoreCache.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ localStoreCache.beforeReducer = (state, action) => {
localStoreCache.afterReducer = (state, action) => {
let nextCachedState = JSON.stringify(stateFilter(state))
if (nextCachedState !== cachedState) {
window.localStorage.setItem('snapshot', nextCachedState)
localStorage.setItem('snapshot', nextCachedState)
cachedState = nextCachedState
}
return state
Expand Down
14 changes: 8 additions & 6 deletions app/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@ const finalReducer = composeReducers(
localStoreCache.beforeReducer
)

let enhancer = compose(
const enhancer = compose(
applyMiddleware(thunkMiddleware),
window.devToolsExtension ? window.devToolsExtension({
serializeState: (key, value) => {
if (key === 'editor') return {}
if (key === 'DOMNode') return {}
return value
serialize: {
replacer: (key, value) => {
if (key === 'editor') return {}
if (key === 'DOMNode') return {}
return value
}
}
}) : f => f
)
Expand All @@ -62,7 +64,7 @@ window.getState = store.getState


window.addEventListener('storage', (e) => {
if (e.key.includes('extension')) {
if (e.key && e.key.includes('CodingPackage')) {
store.dispatch({ type: 'UPDATE_EXTENSION_CACHE' })
}
})
Expand Down
2 changes: 1 addition & 1 deletion app/utils/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const defaultRequestOptions = {
const defaultFetchOptions = {
method: 'GET',
mode: 'cors',
credentials: 'include',
credentials: __DEV__ ? '' : 'include',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个是为什么?

redirect: 'manual'
}

Expand Down
2 changes: 1 addition & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const CommonConfig = {
}),
new CopyWebpackPlugin([{
from: 'static/favicon.ico'
}])
}]),
],
module: {
loaders: [
Expand Down
6 changes: 5 additions & 1 deletion webpack_configs/devServer.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ module.exports = function (options) {
plugins: [
new webpack.HotModuleReplacementPlugin({
multiStep: true
})
}),
new webpack.DefinePlugin({
__PACKAGE_SERVER__: JSON.stringify(process.env.PACKAGE_SERVER || ''),
__DEV__: true,
}),
],
module: {
loaders: [
Expand Down