Skip to content

Commit

Permalink
Merge pull request #168 from Coding/fs-module-refactor
Browse files Browse the repository at this point in the history
M 0.14.0 finish
  • Loading branch information
hackape committed Aug 18, 2017
2 parents 2799609 + 214e49a commit d74604f
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 53 deletions.
25 changes: 13 additions & 12 deletions app/commons/File/subscribeToFileChange.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ function handleGitFiles (node) {
return false
}

// fixme: maybe we should make this a standard method of File model
function fileIsOpened (filePath) {
const openedFilePaths = mobxStore.EditorState.entities.values().map(editor => editor.filePath)
return openedFilePaths.includes(filePath)
}

export default function subscribeToFileChange () {
autorun(() => {
if (!config.fsSocketConnected) return
Expand All @@ -51,27 +57,22 @@ export default function subscribeToFileChange () {
client.subscribe(`/topic/ws/${config.spaceKey}/change`, (frame) => {
const data = JSON.parse(frame.body)
const node = data.fileInfo

switch (data.changeType) {
case 'create':
if (handleGitFiles(node)) {
break
}
FileActions.loadNodeData([node])
break
case 'modify':
if (handleGitFiles(node)) {
break
}
FileActions.loadNodeData([node])
const tabsToUpdate = mobxStore.EditorTabState.tabs.values().filter(tab => tab.path === node.path)
if (tabsToUpdate.length) {
if (!node.isDir && fileIsOpened(node.path)) {
api.readFile(node.path).then(({ content }) => {
TabActions.updateTabByPath({
path: node.path,
content,
})
node.content = content
FileActions.loadNodeData([node])
})
} else {
FileActions.loadNodeData([node])
}

break
case 'delete':
FileActions.removeNode(node)
Expand Down
11 changes: 4 additions & 7 deletions app/commons/Tree/state.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import _ from 'lodash'
import { observable, computed, action, autorun } from 'mobx'
import { protectedObservable } from 'utils/decorators'

function TreeNodeScope () {
const SHADOW_ROOT_NODE = 'SHADOW_ROOT_NODE'
Expand Down Expand Up @@ -28,17 +29,13 @@ function TreeNodeScope () {
state.entities.set(this.id, this)
}

@observable _isDir = false
@observable _name = ''
@computed get name () { return this._name }
set name (v) { return this._name = v }
@computed get isDir () { return this._isDir }
set isDir (v) { return this._isDir = v }
@protectedObservable _name = ''
@protectedObservable _isDir = false
@protectedObservable _parentId = undefined
@observable isFolded = true
@observable isFocused = false
@observable isHighlighted = false
@observable parentId = undefined
@observable index = 0
@computed get isShadowRoot () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ class BaseCodeEditor extends Component {
<div ref={r => this.dom = r} style={{ width: '100%', height: '100%' }} />
)
}

componentWillUnmount () {
this.editor.destroy()
}
}

BaseCodeEditor.propTypes = {
Expand Down
6 changes: 3 additions & 3 deletions app/components/FileTree/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ export const toggleNodeFold = registerAction('filetree:toggle_node_fold',
}
)

export const removeNode = registerAction('filetree:remove_node',
node => state.entities.delete(node.id)
)
export const removeNode = registerAction('filetree:remove_node', (node) => {
state.entities.delete(node.id || node.path)
})

export const openContextMenu = contextMenuStore.openContextMenuFactory(FileTreeContextMenuItems)
export const closeContextMenu = contextMenuStore.closeContextMenu
Expand Down
31 changes: 16 additions & 15 deletions app/components/FileTree/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,29 +42,29 @@ class FileTreeNode extends TreeNode {
if (this.path === ROOT_PATH) this.isFolded = false
}

@observable path = null

// override default name / isDir behavior
/* override base class */
@computed get name () {
return this.file.name
return this.file ? this.file.name : ''
}

@computed get isDir () {
return this.file.isDir
}

@computed get file () {
return FileState.entities.get(this.path)
return this.file ? this.file.isDir : false
}

@computed get parent () {
@computed get parentId () {
// prioritize corresponding file's tree node
if (this.file) {
const parentFile = this.file && this.file.parent
if (parentFile === null) return state.shadowRoot
return state.entities.get(parentFile.path)
if (this.file && this.file.parent) {
return this.file.parent.path
}
return null
return this._parentId
}
/* end override */

/* extend base class */
@observable path = null

@computed get file () {
return FileState.entities.get(this.path)
}

@computed get children () {
Expand All @@ -84,6 +84,7 @@ class FileTreeNode extends TreeNode {
@computed get size () {
if (this.file) return this.file.size
}
/* end extend */
}

export default state
Expand Down
1 change: 1 addition & 0 deletions app/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const config = observable({
fsSocketConnected: false,
ttySocketConnected: false,
fileExcludePatterns: ['/.git', '/.coding-ide'],
preventAccidentalClose: false,
})

window.config = config
Expand Down
12 changes: 12 additions & 0 deletions app/initialize/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ const stepCache = observable.map({
func: () =>
api.connectWebsocketClient()
},
preventAccidentalClose: {
desc: 'Prevent accidental close',
func: () => {
window.onbeforeunload = function () {
if (config.preventAccidentalClose) {
return 'Do you really want to leave this site? Changes you made may not be saved.'
}
return void 0
}
return true
}
}
})

stepCache.insert = function (key, value, referKey, before = false) {
Expand Down
30 changes: 15 additions & 15 deletions app/settings.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import isObject from 'lodash/isObject'
import { observable, reaction, extendObservable, computed, action } from 'mobx'
import config from 'config'
import emitter, { THEME_CHANGED } from 'utils/emitter'
import is from 'utils/is'
import { observable, reaction, extendObservable, computed, action, autorunAsync } from 'mobx'
import dynamicStyle from 'utils/dynamicStyle'

let EditorState
Expand All @@ -14,7 +15,7 @@ export const UIThemeOptions = [
]
export const SyntaxThemeOptions = ['default', 'neo', 'eclipse', 'monokai', 'material']

const changeTheme = (nextThemeId) => {
const changeUITheme = (nextThemeId) => {
if (!window.themes) window.themes = {}
if (UIThemeOptions.map(option => option.value).includes(nextThemeId)) {
import(`!!style-loader/useable!css-loader!stylus-loader!./styles/${nextThemeId}/index.styl`)
Expand All @@ -26,9 +27,10 @@ const changeTheme = (nextThemeId) => {
})
}

if (nextThemeId === 'dark' && EditorState.options.theme === 'default') {
const editorTheme = EditorState.options.theme
if (nextThemeId === 'dark' && (editorTheme === 'default' || editorTheme === 'neo' || editorTheme === 'eclipse')) {
settings.theme.syntax_theme.value = 'material'
} else if (nextThemeId === 'base-theme' && (EditorState.options.theme === 'monokai' || EditorState.options.theme === 'material')) {
} else if (nextThemeId === 'base-theme' && (editorTheme === 'monokai' || editorTheme === 'material')) {
settings.theme.syntax_theme.value = 'default'
}
emitter.emit(THEME_CHANGED, nextThemeId)
Expand Down Expand Up @@ -144,22 +146,21 @@ const settings = observable({
ui_theme: {
name: 'settings.theme.uiTheme',
value: 'base-theme',
options: UIThemeOptions
options: UIThemeOptions,
reaction: changeUITheme,
},
syntax_theme: {
name: 'settings.theme.syntaxTheme',
value: 'default',
options: SyntaxThemeOptions,
reaction (value) {
changeSyntaxTheme(value)
}
reaction: changeSyntaxTheme,
}
}),

extensions: new DomainSetting({}),

general: new DomainSetting({
_keys: ['language', 'hide_files'],
_keys: ['language', 'exclude_files'],
requireConfirm: true,
language: {
name: 'settings.general.language',
Expand All @@ -169,9 +170,12 @@ const settings = observable({
{ name: 'settings.general.languageOption.chinese', value: 'Chinese' },
]
},
hide_files: {
exclude_files: {
name: 'settings.general.hideFiles',
value: '/.git,/.coding-ide'
value: config.fileExcludePatterns.join(','),
reaction (value) {
config.fileExcludePatterns = value.split(',')
}
}
}),

Expand Down Expand Up @@ -275,7 +279,3 @@ const settings = observable({
})

export default settings

autorunAsync('changeTheme', () => {
changeTheme(settings.theme.ui_theme.value)
})
3 changes: 2 additions & 1 deletion app/utils/decorators/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import mapEntityFactory from './mapEntity'
import defaultProps from './defaultProps'
import protectedObservable from './protectedObservable'

export { mapEntityFactory, defaultProps }
export { mapEntityFactory, defaultProps, protectedObservable }
40 changes: 40 additions & 0 deletions app/utils/decorators/protectedObservable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { observable, computed } from 'mobx'

/*
* This decorator enforce a pattern that's widely used in this project,
*
* @protectedObservable _foo = 'bar'
*
* is a short hand for:
*
* @observable _foo = 'bar'
* @computed
* get foo () { return this._foo }
* set foo (value) { return this._foo = value }
*
* you can specify publicKey explicitly by calling:
* @protectedObservable('publicFoo') _foo = 'bar'
*/
function _protectedObservableDecorator (target, privateKey, descriptor, publicKey) {
if (!publicKey) publicKey = privateKey.replace(/^_/, '')

const computedDescriptor = computed(target, publicKey, {
get () { return this[privateKey] },
set (v) { return this[privateKey] = v },
})

Object.defineProperty(target, publicKey, computedDescriptor)

return observable(target, privateKey, descriptor)
}

function protectedObservable (optionalPublicKey) {
if (typeof optionalPublicKey === 'string') {
return function protectedObservableDecorator (target, key, descriptor) {
return _protectedObservableDecorator(target, key, descriptor, optionalPublicKey)
}
} else {
return _protectedObservableDecorator.apply(null, arguments)
}
}
export default protectedObservable

0 comments on commit d74604f

Please sign in to comment.