unitylock 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.babelrc +8 -0
  3. data/.gitignore +11 -0
  4. data/.node-version +1 -0
  5. data/.rspec +2 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +5 -0
  8. data/CODE_OF_CONDUCT.md +49 -0
  9. data/Gemfile +8 -0
  10. data/Gemfile.lock +53 -0
  11. data/LICENSE.txt +21 -0
  12. data/Procfile +1 -0
  13. data/README.md +32 -0
  14. data/Rakefile +10 -0
  15. data/app.json +7 -0
  16. data/app/action_creators/Lock.js +22 -0
  17. data/app/action_creators/Login.js +10 -0
  18. data/app/action_creators/LoginDialogAction.js +9 -0
  19. data/app/action_creators/Search.js +13 -0
  20. data/app/action_creators/SnackMessage.js +6 -0
  21. data/app/action_creators/Unlock.js +22 -0
  22. data/app/client.js +1 -0
  23. data/app/components/AppSnackBarComponent.js +24 -0
  24. data/app/components/FooterComponent.js +40 -0
  25. data/app/components/LockAppBarComponent.js +60 -0
  26. data/app/components/LockTableComponent.js +59 -0
  27. data/app/components/LoginDialogComponent.js +69 -0
  28. data/app/containers/App.js +18 -0
  29. data/app/containers/AppSnackBar.js +19 -0
  30. data/app/containers/LockAppBar.js +30 -0
  31. data/app/containers/LockTable.js +44 -0
  32. data/app/containers/LoginDialog.js +32 -0
  33. data/app/index.html +20 -0
  34. data/app/index.js +44 -0
  35. data/app/reducers/index.js +33 -0
  36. data/app/server.js +2 -0
  37. data/app/src/client.js +35 -0
  38. data/app/src/helper.js +85 -0
  39. data/app/src/row.js +60 -0
  40. data/app/src/socket.js +47 -0
  41. data/app/test/helper.spec.js +47 -0
  42. data/app/utils/FileUtils.js +11 -0
  43. data/art/unitylock-logo.png +0 -0
  44. data/bin/console +14 -0
  45. data/bin/setup +8 -0
  46. data/config.ru +4 -0
  47. data/config/puma.rb +9 -0
  48. data/exe/unitylock +44 -0
  49. data/lib/unitylock/client.rb +2 -0
  50. data/lib/unitylock/client/main.rb +90 -0
  51. data/lib/unitylock/server.rb +2 -0
  52. data/lib/unitylock/server/entity/unityfile.rb +38 -0
  53. data/lib/unitylock/server/model.rb +59 -0
  54. data/lib/unitylock/server/router.rb +71 -0
  55. data/lib/unitylock/server/service.rb +41 -0
  56. data/lib/unitylock/version.rb +3 -0
  57. data/package.json +52 -0
  58. data/public/favicon.ico +0 -0
  59. data/public/static/bundle.js +98 -0
  60. data/public/static/main.css +82 -0
  61. data/unitylock.gemspec +25 -0
  62. data/webpack.config.js +40 -0
  63. metadata +148 -0
@@ -0,0 +1,59 @@
1
+ import { basename } from '../utils/FileUtils'
2
+ import React, { Component, PropTypes } from 'react'
3
+ import { Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn } from 'material-ui/Table'
4
+
5
+ class LockTableComponent extends Component {
6
+ constructor(props) {
7
+ super(props)
8
+ this.handleOnCellClick = this.handleOnCellClick.bind(this);
9
+ }
10
+
11
+ handleOnCellClick(index) {
12
+ let selectedData = this.props.list[index];
13
+ this.props.onDataClick(this.props.user, selectedData);
14
+ }
15
+
16
+ render() {
17
+ const _data = this.props.list;
18
+ const _user = this.props.user;
19
+ this.data = _data;
20
+
21
+ return (
22
+ <Table
23
+ multiSelectable={false}
24
+ onCellClick={this.handleOnCellClick}
25
+ >
26
+ <TableHeader enableSelectAll={false}>
27
+ <TableRow>
28
+ <TableHeaderColumn>User</TableHeaderColumn>
29
+ <TableHeaderColumn>File</TableHeaderColumn>
30
+ <TableHeaderColumn>Path</TableHeaderColumn>
31
+ <TableHeaderColumn>Time</TableHeaderColumn>
32
+ </TableRow>
33
+ </TableHeader>
34
+ <TableBody>
35
+ {_data.map( (item, index) => (
36
+ <TableRow key={index} selectable={item.user == null || item.user == _user}>
37
+ <TableRowColumn>{item.user}</TableRowColumn>
38
+ <TableRowColumn>{basename(item.file)}</TableRowColumn>
39
+ <TableRowColumn>{item.file}</TableRowColumn>
40
+ <TableRowColumn>{item.updated_at}</TableRowColumn>
41
+ </TableRow>
42
+ ))}
43
+ </TableBody>
44
+ </Table>
45
+ )
46
+ }
47
+ }
48
+
49
+ LockTableComponent.propTypes = {
50
+ user: PropTypes.string.isRequired,
51
+ list: PropTypes.arrayOf(PropTypes.shape({
52
+ user: PropTypes.string.isDefined,
53
+ file: PropTypes.string.isRequired,
54
+ updated_at: PropTypes.string.isRequired
55
+ }).isRequired).isRequired,
56
+ onDataClick: PropTypes.func.isRequired,
57
+ }
58
+
59
+ export default LockTableComponent
@@ -0,0 +1,69 @@
1
+ import React, { Component, PropTypes } from 'react'
2
+ import Dialog from 'material-ui/Dialog'
3
+ import FlatButton from 'material-ui/FlatButton'
4
+ import RaisedButton from 'material-ui/RaisedButton'
5
+ import TextField from 'material-ui/TextField'
6
+
7
+ class LoginDialogComponent extends Component {
8
+ constructor(props) {
9
+ super(props)
10
+ this.state = {
11
+ user: this.props.user,
12
+ }
13
+ this.handleOnLoginClick = this.handleOnLoginClick.bind(this)
14
+ this.handleOnTextChange = this.handleOnTextChange.bind(this)
15
+ }
16
+
17
+ handleOnLoginClick() {
18
+ this.props.onLoginRequest(this.state.user)
19
+ }
20
+
21
+ handleOnTextChange(event) {
22
+ this.setState({
23
+ user: event.target.value,
24
+ })
25
+ }
26
+
27
+ render () {
28
+ const actions = [
29
+ <FlatButton
30
+ label="Cancel"
31
+ onTouchTap={this.props.onCancelRequest}
32
+ />,
33
+ <FlatButton
34
+ label="Login"
35
+ primary={true}
36
+ onTouchTap={this.handleOnLoginClick}
37
+ />,
38
+ ]
39
+
40
+ return (
41
+ <div>
42
+ <Dialog
43
+ title='Login'
44
+ actions={actions}
45
+ modal={false}
46
+ open={this.props.open}
47
+ >
48
+ Input your name:
49
+ <br/>
50
+ <TextField
51
+ id='text-field-controlled'
52
+ hintText='User Name'
53
+ value={this.state.user}
54
+ onChange={this.handleOnTextChange}
55
+ />
56
+ </Dialog>
57
+ </div>
58
+ )
59
+ }
60
+ }
61
+
62
+ LoginDialogComponent.propTypes = {
63
+ open: PropTypes.bool.isRequired,
64
+ user: PropTypes.string.isRequired,
65
+ onLoginRequest: PropTypes.func.isRequired,
66
+ onCancelRequest: PropTypes.func.isRequired,
67
+ }
68
+
69
+ export default LoginDialogComponent
@@ -0,0 +1,18 @@
1
+ import React, { Component, PropTypes } from 'react'
2
+ import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
3
+ import LockTable from './LockTable'
4
+ import LockAppBar from './LockAppBar'
5
+ import AppSnackBar from './AppSnackBar'
6
+ import LoginDialog from './LoginDialog'
7
+ import FooterComponent from '../components/FooterComponent'
8
+
9
+ const App = () => (
10
+ <div>
11
+ <LockAppBar />
12
+ <LockTable />
13
+ <AppSnackBar />
14
+ <LoginDialog />
15
+ <FooterComponent />
16
+ </div>
17
+ )
18
+ export default App
@@ -0,0 +1,19 @@
1
+ import AppSnackBarComponent from '../components/AppSnackBarComponent'
2
+ import { connect } from 'react-redux'
3
+
4
+ const mapStateToProps = (state) => {
5
+ return {
6
+ message: state.snack_message || '',
7
+ }
8
+ };
9
+
10
+ const mapDispatchToProps = (dispatch, ownProps) => {
11
+ return {}
12
+ };
13
+
14
+ const AppSnackBar = connect(
15
+ mapStateToProps,
16
+ mapDispatchToProps
17
+ )(AppSnackBarComponent)
18
+
19
+ export default AppSnackBar
@@ -0,0 +1,30 @@
1
+ import { connect } from 'react-redux'
2
+ import LockAppBarComponent from '../components/LockAppBarComponent'
3
+ import search from '../action_creators/Search'
4
+ import snack_message from '../action_creators/SnackMessage'
5
+ import login_dialog_action from '../action_creators/LoginDialogAction'
6
+
7
+ const mapStateToProps = (state) => {
8
+ return {}
9
+ };
10
+
11
+ const mapDispatchToProps = (dispatch, ownProps) => {
12
+ return {
13
+ onLoginClick: () => {
14
+ login_dialog_action(dispatch, true)
15
+ },
16
+ onSyncClick: () => {
17
+ search(dispatch)
18
+ .then((_) => {
19
+ snack_message(dispatch, 'synchronized');
20
+ })
21
+ },
22
+ }
23
+ };
24
+
25
+ const LockAppBar = connect(
26
+ mapStateToProps,
27
+ mapDispatchToProps
28
+ )(LockAppBarComponent)
29
+
30
+ export default LockAppBar
@@ -0,0 +1,44 @@
1
+ import { connect } from 'react-redux'
2
+ import LockTableComponent from '../components/LockTableComponent'
3
+ import search from '../action_creators/Search'
4
+ import lock from '../action_creators/Lock'
5
+ import unlock from '../action_creators/Unlock'
6
+
7
+ const mapStateToProps = (state) => {
8
+ return {
9
+ list: state['data'],
10
+ user: state['user'] || 'sample_user',
11
+ }
12
+ };
13
+
14
+ const mapDispatchToProps = (dispatch, ownProps) => {
15
+ return {
16
+ onDataClick: (user, selectedData) => {
17
+ if (user == null || user == '') {
18
+ console.warn("user not defined");
19
+ return;
20
+ }
21
+
22
+ let dataFile = selectedData['file']
23
+ let dataUser = selectedData['user']
24
+
25
+ // unlock
26
+ if (dataUser) {
27
+ unlock(dispatch, user, dataFile)
28
+ .then((it) => { search(dispatch) })
29
+ }
30
+ // lock
31
+ else {
32
+ lock(dispatch, user, dataFile)
33
+ .then((it) => { search(dispatch) })
34
+ }
35
+ }
36
+ }
37
+ };
38
+
39
+ const LockTable = connect(
40
+ mapStateToProps,
41
+ mapDispatchToProps
42
+ )(LockTableComponent)
43
+
44
+ export default LockTable
@@ -0,0 +1,32 @@
1
+ import { connect } from 'react-redux'
2
+ import LoginDialogComponent from '../components/LoginDialogComponent'
3
+ import login_dialog_action from '../action_creators/LoginDialogAction'
4
+ import login from '../action_creators/Login'
5
+ import snack_message from '../action_creators/SnackMessage'
6
+
7
+ const mapStateToProps = (state) => {
8
+ return {
9
+ open: state.login_dialog_open,
10
+ user: state.user,
11
+ }
12
+ };
13
+
14
+ const mapDispatchToProps = (dispatch, ownProps) => {
15
+ return {
16
+ onLoginRequest: (user) => {
17
+ login(dispatch, user)
18
+ .then(_ => login_dialog_action(dispatch, false))
19
+ .then(_ => snack_message(dispatch, 'Logined as ' + user))
20
+ },
21
+ onCancelRequest: () => {
22
+ login_dialog_action(dispatch, false)
23
+ },
24
+ }
25
+ };
26
+
27
+ const LoginDialog = connect(
28
+ mapStateToProps,
29
+ mapDispatchToProps
30
+ )(LoginDialogComponent)
31
+
32
+ export default LoginDialog
@@ -0,0 +1,20 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>unitylock</title>
6
+ <link rel="stylesheet" type="text/css" href="/static/main.css">
7
+ <!--
8
+ <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
9
+ <link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
10
+ <link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
11
+ <link rel="manifest" href="/manifest.json">
12
+ <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
13
+ <meta name="theme-color" content="#ffffff">
14
+ -->
15
+ </head>
16
+ <body>
17
+ <div id="root"></div>
18
+ <script src="/static/bundle.js"></script>
19
+ </body>
20
+ </html>
@@ -0,0 +1,44 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom'
3
+ import { Provider } from 'react-redux'
4
+ import { compose, createStore } from 'redux'
5
+ import persistState from 'redux-localstorage'
6
+ import injectTapEventPlugin from 'react-tap-event-plugin'
7
+ import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
8
+
9
+ import reducers from './reducers'
10
+ import App from './containers/App'
11
+ import search from './action_creators/Search'
12
+ import login from './action_creators/Login'
13
+ import snack_message from './action_creators/SnackMessage'
14
+
15
+ injectTapEventPlugin()
16
+
17
+ const enhancer = compose(persistState('user'))
18
+ const store = createStore(reducers, enhancer)
19
+ const rootElement = document.getElementById('root')
20
+
21
+ function render()
22
+ {
23
+ ReactDOM.render(
24
+ <MuiThemeProvider>
25
+ <Provider store={store}>
26
+ <App />
27
+ </Provider>
28
+ </MuiThemeProvider>,
29
+ rootElement
30
+ )
31
+ }
32
+
33
+ if (store.state) {
34
+ console.log("logined user: " + store.state.user);
35
+ }
36
+
37
+ render()
38
+ store.subscribe(render)
39
+
40
+ // initial request
41
+ search(store.dispatch).then((_) =>
42
+ snack_message(store.dispatch, 'Welcome! ' + store.getState().user)
43
+ )
44
+
@@ -0,0 +1,33 @@
1
+ export default function list(
2
+ state = {},
3
+ action
4
+ )
5
+ {
6
+ var newState = Object.assign({
7
+ data: [],
8
+ user: 'guest',
9
+ snack_message: '',
10
+ login_dialog_open: false,
11
+ }, state)
12
+
13
+ switch (action.type) {
14
+ case 'search':
15
+ newState['data'] = action['data']
16
+ break;
17
+ case 'login':
18
+ newState['user'] = action['user']
19
+ break;
20
+ case 'snack_message':
21
+ newState['snack_message'] = action['snack_message']
22
+ break;
23
+ case 'login_dialog_open':
24
+ newState['login_dialog_open'] = action.login_dialog_open
25
+ break;
26
+ default:
27
+ break;
28
+ }
29
+
30
+ console.log("reduced: " + action.type);
31
+
32
+ return newState;
33
+ }
@@ -0,0 +1,2 @@
1
+ require('./src/server.js')
2
+ require('./src/socket.js')
@@ -0,0 +1,35 @@
1
+ var WebSocketClient = require('websocket').client;
2
+ var port = 8080
3
+ var client = new WebSocketClient();
4
+
5
+ client.on('connectFailed', function(error) {
6
+ console.log('Connect Error: ' + error.toString());
7
+ });
8
+
9
+ client.on('connect', function(connection) {
10
+ console.log('WebSocket Client Connected');
11
+ connection.on('error', function(error) {
12
+ console.log("Connection Error: " + error.toString());
13
+ });
14
+ connection.on('close', function() {
15
+ console.log('echo-protocol Connection Closed');
16
+ });
17
+
18
+
19
+ connection.on('message', function(message) {
20
+ if (message.type === 'utf8') {
21
+ console.log("Received: '" + message.utf8Data + "'");
22
+ }
23
+ });
24
+
25
+ function sendNumber() {
26
+ if (connection.connected) {
27
+ var number = Math.round(Math.random() * 0xFFFFFF);
28
+ connection.sendUTF(number.toString());
29
+ setTimeout(sendNumber, 1000);
30
+ }
31
+ }
32
+ sendNumber();
33
+ });
34
+
35
+ client.connect('ws://localhost:' + port, 'echo-protocol');
@@ -0,0 +1,85 @@
1
+ var fs = require('fs')
2
+
3
+ // { path: { path:"", owner:"", updated_at: 0 } }
4
+ // TODO: synchronize
5
+ // TODO: mysql support
6
+ class Helper {
7
+ constructor(file) {
8
+ this.file = file
9
+ }
10
+
11
+ raw() {
12
+ return new Promise((resolve, reject) => {
13
+ fs.readFile(this.file, (err, text) => {
14
+ if (err) {
15
+ // reject(err)
16
+ resolve({})
17
+ return
18
+ }
19
+ resolve(JSON.parse(text))
20
+ })
21
+ })
22
+ }
23
+
24
+ search() {
25
+ return this.raw()
26
+ .then(hash => {
27
+ return hash.values || []
28
+ })
29
+ }
30
+
31
+ fetch_by_path(path) {
32
+ return this.raw()
33
+ .then(hash => {
34
+ return hash[path]
35
+ })
36
+ }
37
+
38
+ update_user_by_row(row) {
39
+ this.row()
40
+ .then(hash => {
41
+ let hashkeys = hash.keys || []
42
+ hash[row.path]
43
+
44
+ })
45
+ }
46
+
47
+ create_or_update_by_pathes(pathes) {
48
+ return new Promise((resolve, reject) => {
49
+ this.raw()
50
+ .then(hash => {
51
+ let hashkeys = hash.keys || []
52
+ pathes.forEach(path => {
53
+ if (!hashkeys.includes(path)) {
54
+ hash[path] = { path: path, updated_at: Date.now() }
55
+ } else {
56
+ hash[path]["updated_at"] = Date.now()
57
+ }
58
+ })
59
+ return hash
60
+ })
61
+ .then(hash => {
62
+ var writeData = JSON.stringify(hash)
63
+ fs.writeFile(this.file, writeData, (err) => {
64
+ if (err != null) { reject(err) }
65
+ let hashvalues = (Object.keys(hash) || []).map(it => hash[it])
66
+ let result = hashvalues.filter(it => pathes.includes(it.path)) || []
67
+ resolve(result)
68
+ })
69
+ })
70
+ })
71
+ }
72
+
73
+ delete_by_path(path) {
74
+ return raw()
75
+ .then(hash => {
76
+ delete hash[path]
77
+ return hash
78
+ })
79
+ .then(hash => {
80
+ fs.writeFile(this.file, JSON.stringify(hash))
81
+ })
82
+ }
83
+ }
84
+
85
+ export default Helper