├── .gitignore ├── LICENSE ├── README.md ├── examples ├── fluent-chat │ ├── .gitignore │ ├── README.md │ ├── css │ │ └── chatapp.css │ ├── index.html │ ├── js │ │ ├── ChatExampleData.js │ │ ├── actions │ │ │ ├── ChatMessageActionCreators.js │ │ │ ├── ChatServerActionCreators.js │ │ │ └── ChatThreadActionCreators.js │ │ ├── app.js │ │ ├── components │ │ │ ├── ChatApp.react.js │ │ │ ├── MessageComposer.react.js │ │ │ ├── MessageListItem.react.js │ │ │ ├── MessageSection.react.js │ │ │ ├── ThreadListItem.react.js │ │ │ └── ThreadSection.react.js │ │ ├── dispatcher │ │ │ └── ChatAppDispatcher.js │ │ ├── stores │ │ │ ├── MessageStore.js │ │ │ ├── ThreadStore.js │ │ │ └── UnreadThreadStore.js │ │ └── utils │ │ │ ├── ChatMessageUtils.js │ │ │ └── ChatWebAPIUtils.js │ └── package.json └── flux-chat │ ├── .gitignore │ ├── README.md │ ├── css │ └── chatapp.css │ ├── index.html │ ├── js │ ├── ChatExampleData.js │ ├── actions │ │ ├── ChatMessageActionCreators.js │ │ ├── ChatServerActionCreators.js │ │ └── ChatThreadActionCreators.js │ ├── app.js │ ├── components │ │ ├── ChatApp.react.js │ │ ├── MessageComposer.react.js │ │ ├── MessageListItem.react.js │ │ ├── MessageSection.react.js │ │ ├── ThreadListItem.react.js │ │ └── ThreadSection.react.js │ ├── constants │ │ └── ChatConstants.js │ ├── dispatcher │ │ └── ChatAppDispatcher.js │ ├── stores │ │ ├── MessageStore.js │ │ ├── ThreadStore.js │ │ ├── UnreadThreadStore.js │ │ └── __tests__ │ │ │ └── UnreadThreadStore-test.js │ └── utils │ │ ├── ChatMessageUtils.js │ │ └── ChatWebAPIUtils.js │ └── package.json ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Twincl Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## FluentJS 2 | 3 | FluentJS is a tiny Flux implementation we’re using in our site [twincl.com](https://twincl.com). It has some good features: 4 | 5 | - **Very tiny** - less than 100 lines of code. 6 | - **ES2015 class-based**. 7 | - **Can be used at server-side rendering**. (For example, all [twincl.com](https://twincl.com) pages can be rendered at server side by appending a `?ssr=1` query parameter.) 8 | - **Makes your code very terse**. No more repetitive `emitChange`, `add/removeChangeListener`, `ActionTypes` constants, etc. 9 | - As close to Facebook Flux pattern and as few magic as possible. 10 | - Can be used with Facebook [**Flow**](http://flowtype.org/) **type checker** effectively. (For example, if you call an action creator or store method with wrong argument types, the Flow type checker will complain.) 11 | 12 | If you already know Flux, a quick way to glimpse FluentJS is by looking at our [`fluent-chat`](https://github.com/twincl/fluent/tree/master/examples) example modified from the Facebook `flux-chat` code. This [commit](https://github.com/twincl/fluent/commit/32aab0b6ca32ddba01151f17fbf4ac029c0c06cb) shows the code changes. As you can see, lots of repetitive code were removed. 13 | 14 | ### Installation 15 | 16 | To install FluentJS, type: 17 | 18 | npm install --save fluent-js 19 | 20 | ### Usage 21 | 22 | See [this page](https://twincl.com/@arthurtw/*613/fluent-js-a-tiny-facebook-flux-library) (English) or [this page](https://tw.twincl.com/programming/*631e) (中文). 23 | 24 | -------------------------------------------------------------------------------- /examples/fluent-chat/.gitignore: -------------------------------------------------------------------------------- 1 | /js/bundle.js 2 | /js/bundle.min.js 3 | -------------------------------------------------------------------------------- /examples/fluent-chat/README.md: -------------------------------------------------------------------------------- 1 | ## Flux Chat Example 2 | 3 | This is an example application we've created to show an example of how a Flux 4 | app is structured, and how you might use waitFor to make sure the Stores' 5 | registered callbacks are called in the correct order. 6 | 7 | ## Running 8 | 9 | You must have [npm](https://www.npmjs.org/) installed on your computer. 10 | From the root project directory run these commands from the command line: 11 | 12 | `npm install` 13 | 14 | This will install all dependencies. 15 | 16 | To build the project, first run this command: 17 | 18 | `npm start` 19 | 20 | This will perform an initial build and start a watcher process that will 21 | update bundle.js with any changes you wish to make. This watcher is 22 | based on [Browserify](http://browserify.org/) and 23 | [Watchify](https://github.com/substack/watchify), and it transforms 24 | React's JSX syntax into standard JavaScript with 25 | [Reactify](https://github.com/andreypopp/reactify). 26 | 27 | After starting the watcher, you can open `index.html` in your browser to 28 | open the app. 29 | 30 | -------------------------------------------------------------------------------- /examples/fluent-chat/css/chatapp.css: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is provided by Facebook for testing and evaluation purposes 3 | * only. Facebook reserves all rights not expressly granted. 4 | * 5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 9 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 10 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | */ 12 | 13 | .chatapp { 14 | font-family: 'Muli', 'Helvetica Neue', helvetica, arial; 15 | max-width: 760px; 16 | margin: 20px auto; 17 | overflow: hidden; 18 | } 19 | 20 | .message-list, .thread-list { 21 | border: 1px solid #ccf; 22 | font-size: 16px; 23 | height: 400px; 24 | margin: 0; 25 | overflow-y: auto; 26 | padding: 0; 27 | } 28 | 29 | .message-section { 30 | float: right; 31 | width: 65%; 32 | } 33 | 34 | .thread-section { 35 | float: left; 36 | width: 32.5%; 37 | } 38 | 39 | .message-thread-heading, 40 | .thread-count { 41 | height: 40px; 42 | margin: 0; 43 | } 44 | 45 | .message-list-item, .thread-list-item { 46 | list-style: none; 47 | padding: 12px 14px 14px; 48 | } 49 | 50 | .thread-list-item { 51 | border-bottom: 1px solid #ccc; 52 | cursor: pointer; 53 | } 54 | 55 | .thread-list:hover .thread-list-item:hover { 56 | background-color: #f8f8ff; 57 | } 58 | 59 | .thread-list:hover .thread-list-item { 60 | background-color: #fff; 61 | } 62 | 63 | .thread-list-item.active, 64 | .thread-list:hover .thread-list-item.active, 65 | .thread-list:hover .thread-list-item.active:hover { 66 | background-color: #efefff; 67 | cursor: default; 68 | } 69 | 70 | .message-author-name, 71 | .thread-name { 72 | color: #66c; 73 | float: left; 74 | font-size: 13px; 75 | margin: 0; 76 | } 77 | 78 | .message-time, .thread-time { 79 | color: #aad; 80 | float: right; 81 | font-size: 12px; 82 | } 83 | 84 | .message-text, .thread-last-message { 85 | clear: both; 86 | font-size: 14px; 87 | padding-top: 10px; 88 | } 89 | 90 | .message-composer { 91 | box-sizing: border-box; 92 | font-family: inherit; 93 | font-size: 14px; 94 | height: 5em; 95 | width: 100%; 96 | margin: 20px 0 0; 97 | padding: 10px; 98 | } 99 | -------------------------------------------------------------------------------- /examples/fluent-chat/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flux • Chat 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/fluent-chat/js/ChatExampleData.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is provided by Facebook for testing and evaluation purposes 3 | * only. Facebook reserves all rights not expressly granted. 4 | * 5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 9 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 10 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | */ 12 | 13 | module.exports = { 14 | 15 | init: function() { 16 | localStorage.clear(); 17 | localStorage.setItem('messages', JSON.stringify([ 18 | { 19 | id: 'm_1', 20 | threadID: 't_1', 21 | threadName: 'Jing and Bill', 22 | authorName: 'Bill', 23 | text: 'Hey Jing, want to give a Flux talk at ForwardJS?', 24 | timestamp: Date.now() - 99999 25 | }, 26 | { 27 | id: 'm_2', 28 | threadID: 't_1', 29 | threadName: 'Jing and Bill', 30 | authorName: 'Bill', 31 | text: 'Seems like a pretty cool conference.', 32 | timestamp: Date.now() - 89999 33 | }, 34 | { 35 | id: 'm_3', 36 | threadID: 't_1', 37 | threadName: 'Jing and Bill', 38 | authorName: 'Jing', 39 | text: 'Sounds good. Will they be serving dessert?', 40 | timestamp: Date.now() - 79999 41 | }, 42 | { 43 | id: 'm_4', 44 | threadID: 't_2', 45 | threadName: 'Dave and Bill', 46 | authorName: 'Bill', 47 | text: 'Hey Dave, want to get a beer after the conference?', 48 | timestamp: Date.now() - 69999 49 | }, 50 | { 51 | id: 'm_5', 52 | threadID: 't_2', 53 | threadName: 'Dave and Bill', 54 | authorName: 'Dave', 55 | text: 'Totally! Meet you at the hotel bar.', 56 | timestamp: Date.now() - 59999 57 | }, 58 | { 59 | id: 'm_6', 60 | threadID: 't_3', 61 | threadName: 'Functional Heads', 62 | authorName: 'Bill', 63 | text: 'Hey Brian, are you going to be talking about functional stuff?', 64 | timestamp: Date.now() - 49999 65 | }, 66 | { 67 | id: 'm_7', 68 | threadID: 't_3', 69 | threadName: 'Bill and Brian', 70 | authorName: 'Brian', 71 | text: 'At ForwardJS? Yeah, of course. See you there!', 72 | timestamp: Date.now() - 39999 73 | } 74 | ])); 75 | } 76 | 77 | }; 78 | -------------------------------------------------------------------------------- /examples/fluent-chat/js/actions/ChatMessageActionCreators.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is provided by Facebook for testing and evaluation purposes 3 | * only. Facebook reserves all rights not expressly granted. 4 | * 5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 9 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 10 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | */ 12 | 13 | var Fluent = require('fluent-js'); 14 | var ChatAppDispatcher = require('../dispatcher/ChatAppDispatcher'); 15 | var ChatWebAPIUtils = require('../utils/ChatWebAPIUtils'); 16 | var ChatMessageUtils = require('../utils/ChatMessageUtils'); 17 | 18 | class ChatMessageActions extends Fluent.Actions { 19 | 20 | createMessage(text, currentThreadID) { 21 | this.dispatch(text, currentThreadID); 22 | var message = ChatMessageUtils.getCreatedMessageData(text, currentThreadID); 23 | ChatWebAPIUtils.createMessage(message); 24 | } 25 | 26 | } 27 | 28 | module.exports = new ChatMessageActions(ChatAppDispatcher); 29 | -------------------------------------------------------------------------------- /examples/fluent-chat/js/actions/ChatServerActionCreators.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is provided by Facebook for testing and evaluation purposes 3 | * only. Facebook reserves all rights not expressly granted. 4 | * 5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 9 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 10 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | */ 12 | 13 | var Fluent = require('fluent-js'); 14 | var ChatAppDispatcher = require('../dispatcher/ChatAppDispatcher'); 15 | 16 | class ChatServerActions extends Fluent.Actions { 17 | 18 | receiveAll(rawMessages) { 19 | this.dispatch(rawMessages); 20 | } 21 | 22 | receiveCreatedMessage(createdMessage) { 23 | this.dispatch(createdMessage); 24 | } 25 | 26 | } 27 | 28 | module.exports = new ChatServerActions(ChatAppDispatcher, true); 29 | -------------------------------------------------------------------------------- /examples/fluent-chat/js/actions/ChatThreadActionCreators.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is provided by Facebook for testing and evaluation purposes 3 | * only. Facebook reserves all rights not expressly granted. 4 | * 5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 9 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 10 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | */ 12 | 13 | var Fluent = require('fluent-js'); 14 | var ChatAppDispatcher = require('../dispatcher/ChatAppDispatcher'); 15 | 16 | class ChatThreadActions extends Fluent.Actions { 17 | 18 | clickThread(threadID) { 19 | this.dispatch(threadID); 20 | } 21 | 22 | } 23 | 24 | module.exports = new ChatThreadActions(ChatAppDispatcher); 25 | -------------------------------------------------------------------------------- /examples/fluent-chat/js/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is provided by Facebook for testing and evaluation purposes 3 | * only. Facebook reserves all rights not expressly granted. 4 | * 5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 9 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 10 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | */ 12 | 13 | // This file bootstraps the entire application. 14 | 15 | var ChatApp = require('./components/ChatApp.react'); 16 | var ChatExampleData = require('./ChatExampleData'); 17 | var ChatWebAPIUtils = require('./utils/ChatWebAPIUtils'); 18 | var React = require('react'); 19 | window.React = React; // export for http://fb.me/react-devtools 20 | 21 | ChatExampleData.init(); // load example data into localstorage 22 | 23 | ChatWebAPIUtils.getAllMessages(); 24 | 25 | React.render( 26 | , 27 | document.getElementById('react') 28 | ); 29 | -------------------------------------------------------------------------------- /examples/fluent-chat/js/components/ChatApp.react.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is provided by Facebook for testing and evaluation purposes 3 | * only. Facebook reserves all rights not expressly granted. 4 | * 5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 9 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 10 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | */ 12 | 13 | var MessageSection = require('./MessageSection.react'); 14 | var React = require('react'); 15 | var ThreadSection = require('./ThreadSection.react'); 16 | 17 | var ChatApp = React.createClass({ 18 | 19 | render: function() { 20 | return ( 21 |
22 | 23 | 24 |
25 | ); 26 | } 27 | 28 | }); 29 | 30 | module.exports = ChatApp; 31 | -------------------------------------------------------------------------------- /examples/fluent-chat/js/components/MessageComposer.react.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is provided by Facebook for testing and evaluation purposes 3 | * only. Facebook reserves all rights not expressly granted. 4 | * 5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 9 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 10 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | */ 12 | 13 | var ChatMessageActionCreators = require('../actions/ChatMessageActionCreators'); 14 | var React = require('react'); 15 | 16 | var ENTER_KEY_CODE = 13; 17 | 18 | var MessageComposer = React.createClass({ 19 | 20 | propTypes: { 21 | threadID: React.PropTypes.string.isRequired 22 | }, 23 | 24 | getInitialState: function() { 25 | return {text: ''}; 26 | }, 27 | 28 | render: function() { 29 | return ( 30 |