├── .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 |
37 | );
38 | },
39 |
40 | _onChange: function(event, value) {
41 | this.setState({text: event.target.value});
42 | },
43 |
44 | _onKeyDown: function(event) {
45 | if (event.keyCode === ENTER_KEY_CODE) {
46 | event.preventDefault();
47 | var text = this.state.text.trim();
48 | if (text) {
49 | ChatMessageActionCreators.createMessage(text, this.props.threadID);
50 | }
51 | this.setState({text: ''});
52 | }
53 | }
54 |
55 | });
56 |
57 | module.exports = MessageComposer;
58 |
--------------------------------------------------------------------------------
/examples/fluent-chat/js/components/MessageListItem.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 React = require('react');
14 |
15 | var ReactPropTypes = React.PropTypes;
16 |
17 | var MessageListItem = React.createClass({
18 |
19 | propTypes: {
20 | message: ReactPropTypes.object
21 | },
22 |
23 | render: function() {
24 | var message = this.props.message;
25 | return (
26 |
27 |
{message.authorName}
28 |
29 | {message.date.toLocaleTimeString()}
30 |
31 |
{message.text}
32 |
33 | );
34 | }
35 |
36 | });
37 |
38 | module.exports = MessageListItem;
39 |
--------------------------------------------------------------------------------
/examples/fluent-chat/js/components/MessageSection.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 Fluent = require('fluent-js');
14 | var MessageComposer = require('./MessageComposer.react');
15 | var MessageListItem = require('./MessageListItem.react');
16 | var MessageStore = require('../stores/MessageStore');
17 | var React = require('react');
18 | var ThreadStore = require('../stores/ThreadStore');
19 |
20 | function getStateFromStores() {
21 | return {
22 | messages: MessageStore.getAllForCurrentThread(),
23 | thread: ThreadStore.getCurrent()
24 | };
25 | }
26 |
27 | function getMessageListItem(message) {
28 | return (
29 |
33 | );
34 | }
35 |
36 | class MessageSection extends React.Component {
37 |
38 | constructor(props) {
39 | super(props);
40 | this.state = getStateFromStores();
41 | this.onChange = this._onChange;
42 | }
43 |
44 | componentDidMount() {
45 | this._scrollToBottom();
46 | }
47 |
48 | render() {
49 | var messageListItems = this.state.messages.map(getMessageListItem);
50 | return (
51 |
52 |
{this.state.thread.name}
53 |
54 | {messageListItems}
55 |
56 |
57 |
58 | );
59 | }
60 |
61 | componentDidUpdate() {
62 | this._scrollToBottom();
63 | }
64 |
65 | _scrollToBottom() {
66 | var ul = this.refs.messageList.getDOMNode();
67 | ul.scrollTop = ul.scrollHeight;
68 | }
69 |
70 | /**
71 | * Event handler for 'change' events coming from the MessageStore
72 | */
73 | _onChange() {
74 | this.setState(getStateFromStores());
75 | }
76 |
77 | }
78 |
79 | module.exports = Fluent.connectToStores(React, MessageSection, [MessageStore, ThreadStore], 'onChange');
80 |
--------------------------------------------------------------------------------
/examples/fluent-chat/js/components/ThreadListItem.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 ChatThreadActionCreators = require('../actions/ChatThreadActionCreators');
14 | var React = require('react');
15 | var classNames = require('classnames');
16 |
17 | var ReactPropTypes = React.PropTypes;
18 |
19 | var ThreadListItem = React.createClass({
20 |
21 | propTypes: {
22 | thread: ReactPropTypes.object,
23 | currentThreadID: ReactPropTypes.string
24 | },
25 |
26 | render: function() {
27 | var thread = this.props.thread;
28 | var lastMessage = thread.lastMessage;
29 | return (
30 |
36 |
{thread.name}
37 |
38 | {lastMessage.date.toLocaleTimeString()}
39 |
40 |
41 | {lastMessage.text}
42 |
43 |
44 | );
45 | },
46 |
47 | _onClick: function() {
48 | ChatThreadActionCreators.clickThread(this.props.thread.id);
49 | }
50 |
51 | });
52 |
53 | module.exports = ThreadListItem;
54 |
--------------------------------------------------------------------------------
/examples/fluent-chat/js/components/ThreadSection.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 React = require('react');
14 | var Fluent = require('fluent-js');
15 | var ThreadListItem = require('../components/ThreadListItem.react');
16 | var ThreadStore = require('../stores/ThreadStore');
17 | var UnreadThreadStore = require('../stores/UnreadThreadStore');
18 |
19 | function getStateFromStores() {
20 | return {
21 | threads: ThreadStore.getAllChrono(),
22 | currentThreadID: ThreadStore.getCurrentID(),
23 | unreadCount: UnreadThreadStore.getCount()
24 | };
25 | }
26 |
27 | class ThreadSection extends React.Component {
28 |
29 | constructor(props) {
30 | super(props);
31 | this.state = getStateFromStores();
32 | this.onChange = this._onChange;
33 | }
34 |
35 | render() {
36 | var threadListItems = this.state.threads.map(function(thread) {
37 | return (
38 |
43 | );
44 | }, this);
45 | var unread =
46 | this.state.unreadCount === 0 ?
47 | null :
48 | Unread threads: {this.state.unreadCount};
49 | return (
50 |
51 |
52 | {unread}
53 |
54 |
55 | {threadListItems}
56 |
57 |
58 | );
59 | }
60 |
61 | /**
62 | * Event handler for 'change' events coming from the stores
63 | */
64 | _onChange() {
65 | this.setState(getStateFromStores());
66 | }
67 |
68 | }
69 |
70 | module.exports = Fluent.connectToStores(React, ThreadSection, [ThreadStore, UnreadThreadStore], 'onChange');
71 |
--------------------------------------------------------------------------------
/examples/fluent-chat/js/dispatcher/ChatAppDispatcher.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 Dispatcher = require('fluent-js').Dispatcher;
14 |
15 | module.exports = new Dispatcher();
16 |
--------------------------------------------------------------------------------
/examples/fluent-chat/js/stores/MessageStore.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 ChatMessageUtils = require('../utils/ChatMessageUtils');
16 | var ThreadStore = require('../stores/ThreadStore');
17 |
18 | var _messages = {};
19 |
20 | function _addMessages(rawMessages) {
21 | rawMessages.forEach(function(message) {
22 | if (!_messages[message.id]) {
23 | _messages[message.id] = ChatMessageUtils.convertRawMessage(
24 | message,
25 | ThreadStore.getCurrentID()
26 | );
27 | }
28 | });
29 | }
30 |
31 | function _markAllInThreadRead(threadID) {
32 | for (var id in _messages) {
33 | if (_messages[id].threadID === threadID) {
34 | _messages[id].isRead = true;
35 | }
36 | }
37 | }
38 |
39 | class MessageStore extends Fluent.Store {
40 |
41 | get(id) {
42 | return _messages[id];
43 | }
44 |
45 | getAll() {
46 | return _messages;
47 | }
48 |
49 | /**
50 | * @param {string} threadID
51 | */
52 | getAllForThread(threadID) {
53 | var threadMessages = [];
54 | for (var id in _messages) {
55 | if (_messages[id].threadID === threadID) {
56 | threadMessages.push(_messages[id]);
57 | }
58 | }
59 | threadMessages.sort(function(a, b) {
60 | if (a.date < b.date) {
61 | return -1;
62 | } else if (a.date > b.date) {
63 | return 1;
64 | }
65 | return 0;
66 | });
67 | return threadMessages;
68 | }
69 |
70 | getAllForCurrentThread() {
71 | return this.getAllForThread(ThreadStore.getCurrentID());
72 | }
73 |
74 | }
75 |
76 | var actionHandlers = {
77 |
78 | viewActionHandlers: {
79 |
80 | clickThread(threadID) {
81 | this.waitFor([ThreadStore]);
82 | _markAllInThreadRead(ThreadStore.getCurrentID());
83 | },
84 |
85 | createMessage(text, currentThreadID) {
86 | var message = ChatMessageUtils.getCreatedMessageData(
87 | text,
88 | currentThreadID
89 | );
90 | _messages[message.id] = message;
91 | }
92 |
93 | },
94 |
95 | serverActionHandlers: {
96 |
97 | receiveAll(rawMessages) {
98 | _addMessages(rawMessages);
99 | this.waitFor([ThreadStore]);
100 | _markAllInThreadRead(ThreadStore.getCurrentID());
101 | }
102 |
103 | }
104 |
105 | };
106 |
107 | module.exports = new MessageStore(ChatAppDispatcher, actionHandlers);
108 |
--------------------------------------------------------------------------------
/examples/fluent-chat/js/stores/ThreadStore.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 ChatMessageUtils = require('../utils/ChatMessageUtils');
16 |
17 | var _currentID = null;
18 | var _threads = {};
19 |
20 | class ThreadStore extends Fluent.Store {
21 |
22 | init(rawMessages) {
23 | rawMessages.forEach(function(message) {
24 | var threadID = message.threadID;
25 | var thread = _threads[threadID];
26 | if (thread && thread.lastMessage.timestamp > message.timestamp) {
27 | return;
28 | }
29 | _threads[threadID] = {
30 | id: threadID,
31 | name: message.threadName,
32 | lastMessage: ChatMessageUtils.convertRawMessage(message, _currentID)
33 | };
34 | }, this);
35 |
36 | if (!_currentID) {
37 | var allChrono = this.getAllChrono();
38 | _currentID = allChrono[allChrono.length - 1].id;
39 | }
40 |
41 | _threads[_currentID].lastMessage.isRead = true;
42 | }
43 |
44 | /**
45 | * @param {string} id
46 | */
47 | get(id) {
48 | return _threads[id];
49 | }
50 |
51 | getAll() {
52 | return _threads;
53 | }
54 |
55 | getAllChrono() {
56 | var orderedThreads = [];
57 | for (var id in _threads) {
58 | var thread = _threads[id];
59 | orderedThreads.push(thread);
60 | }
61 | orderedThreads.sort(function(a, b) {
62 | if (a.lastMessage.date < b.lastMessage.date) {
63 | return -1;
64 | } else if (a.lastMessage.date > b.lastMessage.date) {
65 | return 1;
66 | }
67 | return 0;
68 | });
69 | return orderedThreads;
70 | }
71 |
72 | getCurrentID() {
73 | return _currentID;
74 | }
75 |
76 | getCurrent() {
77 | return this.get(this.getCurrentID());
78 | }
79 |
80 | }
81 |
82 | var actionHandlers = {
83 |
84 | viewActionHandlers: {
85 |
86 | clickThread(threadID) {
87 | _currentID = threadID;
88 | _threads[_currentID].lastMessage.isRead = true;
89 | }
90 |
91 | },
92 |
93 | serverActionHandlers: {
94 |
95 | receiveAll(rawMessages) {
96 | threadStore.init(rawMessages);
97 | }
98 |
99 | }
100 |
101 | };
102 |
103 | var threadStore = new ThreadStore(ChatAppDispatcher, actionHandlers);
104 |
105 | module.exports = threadStore;
106 |
--------------------------------------------------------------------------------
/examples/fluent-chat/js/stores/UnreadThreadStore.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 MessageStore = require('../stores/MessageStore');
16 | var ThreadStore = require('../stores/ThreadStore');
17 |
18 | class UnreadThreadStore extends Fluent.Store {
19 |
20 | getCount() {
21 | var threads = ThreadStore.getAll();
22 | var unreadCount = 0;
23 | for (var id in threads) {
24 | if (!threads[id].lastMessage.isRead) {
25 | unreadCount++;
26 | }
27 | }
28 | return unreadCount;
29 | }
30 |
31 | }
32 |
33 | var actionHandlers = {
34 |
35 | viewActionHandlers: {
36 |
37 | clickThread(threadID) {
38 | this.waitFor([ThreadStore, MessageStore]);
39 | }
40 |
41 | },
42 |
43 | serverActionHandlers: {
44 |
45 | receiveAll(rawMessages) {
46 | this.waitFor([ThreadStore, MessageStore]);
47 | }
48 |
49 | }
50 |
51 | };
52 |
53 | module.exports = new UnreadThreadStore(ChatAppDispatcher, actionHandlers);
54 |
--------------------------------------------------------------------------------
/examples/fluent-chat/js/utils/ChatMessageUtils.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 | convertRawMessage: function(rawMessage, currentThreadID) {
16 | return {
17 | id: rawMessage.id,
18 | threadID: rawMessage.threadID,
19 | authorName: rawMessage.authorName,
20 | date: new Date(rawMessage.timestamp),
21 | text: rawMessage.text,
22 | isRead: rawMessage.threadID === currentThreadID
23 | };
24 | },
25 |
26 | getCreatedMessageData: function(text, currentThreadID) {
27 | var timestamp = Date.now();
28 | return {
29 | id: 'm_' + timestamp,
30 | threadID: currentThreadID,
31 | authorName: 'Bill', // hard coded for the example
32 | date: new Date(timestamp),
33 | text: text,
34 | isRead: true
35 | };
36 | }
37 |
38 | };
39 |
--------------------------------------------------------------------------------
/examples/fluent-chat/js/utils/ChatWebAPIUtils.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 ChatServerActionCreators = require('../actions/ChatServerActionCreators');
14 |
15 | // !!! Please Note !!!
16 | // We are using localStorage as an example, but in a real-world scenario, this
17 | // would involve XMLHttpRequest, or perhaps a newer client-server protocol.
18 | // The function signatures below might be similar to what you would build, but
19 | // the contents of the functions are just trying to simulate client-server
20 | // communication and server-side processing.
21 |
22 | module.exports = {
23 |
24 | getAllMessages: function() {
25 | // simulate retrieving data from a database
26 | var rawMessages = JSON.parse(localStorage.getItem('messages'));
27 |
28 | // simulate success callback
29 | ChatServerActionCreators.receiveAll(rawMessages);
30 | },
31 |
32 | createMessage: function(message, threadName) {
33 | // simulate writing to a database
34 | var rawMessages = JSON.parse(localStorage.getItem('messages'));
35 | var timestamp = Date.now();
36 | var id = 'm_' + timestamp;
37 | var threadID = message.threadID || ('t_' + Date.now());
38 | var createdMessage = {
39 | id: id,
40 | threadID: threadID,
41 | threadName: threadName,
42 | authorName: message.authorName,
43 | text: message.text,
44 | timestamp: timestamp
45 | };
46 | rawMessages.push(createdMessage);
47 | localStorage.setItem('messages', JSON.stringify(rawMessages));
48 |
49 | // simulate success callback
50 | setTimeout(function() {
51 | ChatServerActionCreators.receiveCreatedMessage(createdMessage);
52 | }, 0);
53 | }
54 |
55 | };
56 |
--------------------------------------------------------------------------------
/examples/fluent-chat/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fluent-chat",
3 | "version": "0.0.2",
4 | "description": "Example Fluent chat application based on Facebook Flux chat example.",
5 | "repository": "https://github.com/twincl/fluent",
6 | "main": "js/app.js",
7 | "dependencies": {
8 | "classnames": "^2.1.3",
9 | "fluent-js": "^0.9.1",
10 | "keymirror": "~0.1.0",
11 | "object-assign": "^1.0.0",
12 | "react": "^0.13.0"
13 | },
14 | "devDependencies": {
15 | "browserify": "^6.2.0",
16 | "envify": "^3.0.0",
17 | "jest-cli": "^0.4.3",
18 | "reactify": "^0.15.2",
19 | "uglify-js": "~2.4.15",
20 | "watchify": "^2.1.1"
21 | },
22 | "scripts": {
23 | "start": "watchify -o js/bundle.js -v -d js/app.js",
24 | "build": "browserify . -t [envify --NODE_ENV production] | uglifyjs -cm > js/bundle.min.js",
25 | "test": "jest"
26 | },
27 | "author": "Bill Fisher",
28 | "browserify": {
29 | "transform": [
30 | ["reactify", {"es6": true}],
31 | "envify"
32 | ]
33 | },
34 | "jest": {
35 | "rootDir": "./js"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/examples/flux-chat/.gitignore:
--------------------------------------------------------------------------------
1 | /js/bundle.js
2 | /js/bundle.min.js
3 |
--------------------------------------------------------------------------------
/examples/flux-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/flux-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/flux-chat/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Flux • Chat
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/flux-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/flux-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 ChatAppDispatcher = require('../dispatcher/ChatAppDispatcher');
14 | var ChatConstants = require('../constants/ChatConstants');
15 | var ChatWebAPIUtils = require('../utils/ChatWebAPIUtils');
16 | var ChatMessageUtils = require('../utils/ChatMessageUtils');
17 |
18 | var ActionTypes = ChatConstants.ActionTypes;
19 |
20 | module.exports = {
21 |
22 | createMessage: function(text, currentThreadID) {
23 | ChatAppDispatcher.dispatch({
24 | type: ActionTypes.CREATE_MESSAGE,
25 | text: text,
26 | currentThreadID: currentThreadID
27 | });
28 | var message = ChatMessageUtils.getCreatedMessageData(text, currentThreadID);
29 | ChatWebAPIUtils.createMessage(message);
30 | }
31 |
32 | };
33 |
--------------------------------------------------------------------------------
/examples/flux-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 ChatAppDispatcher = require('../dispatcher/ChatAppDispatcher');
14 | var ChatConstants = require('../constants/ChatConstants');
15 |
16 | var ActionTypes = ChatConstants.ActionTypes;
17 |
18 | module.exports = {
19 |
20 | receiveAll: function(rawMessages) {
21 | ChatAppDispatcher.dispatch({
22 | type: ActionTypes.RECEIVE_RAW_MESSAGES,
23 | rawMessages: rawMessages
24 | });
25 | },
26 |
27 | receiveCreatedMessage: function(createdMessage) {
28 | ChatAppDispatcher.dispatch({
29 | type: ActionTypes.RECEIVE_RAW_CREATED_MESSAGE,
30 | rawMessage: createdMessage
31 | });
32 | }
33 |
34 | };
35 |
--------------------------------------------------------------------------------
/examples/flux-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 ChatAppDispatcher = require('../dispatcher/ChatAppDispatcher');
14 | var ChatConstants = require('../constants/ChatConstants');
15 |
16 | var ActionTypes = ChatConstants.ActionTypes;
17 |
18 | module.exports = {
19 |
20 | clickThread: function(threadID) {
21 | ChatAppDispatcher.dispatch({
22 | type: ActionTypes.CLICK_THREAD,
23 | threadID: threadID
24 | });
25 | }
26 |
27 | };
28 |
--------------------------------------------------------------------------------
/examples/flux-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/flux-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/flux-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 |
37 | );
38 | },
39 |
40 | _onChange: function(event, value) {
41 | this.setState({text: event.target.value});
42 | },
43 |
44 | _onKeyDown: function(event) {
45 | if (event.keyCode === ENTER_KEY_CODE) {
46 | event.preventDefault();
47 | var text = this.state.text.trim();
48 | if (text) {
49 | ChatMessageActionCreators.createMessage(text, this.props.threadID);
50 | }
51 | this.setState({text: ''});
52 | }
53 | }
54 |
55 | });
56 |
57 | module.exports = MessageComposer;
58 |
--------------------------------------------------------------------------------
/examples/flux-chat/js/components/MessageListItem.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 React = require('react');
14 |
15 | var ReactPropTypes = React.PropTypes;
16 |
17 | var MessageListItem = React.createClass({
18 |
19 | propTypes: {
20 | message: ReactPropTypes.object
21 | },
22 |
23 | render: function() {
24 | var message = this.props.message;
25 | return (
26 |
27 |
{message.authorName}
28 |
29 | {message.date.toLocaleTimeString()}
30 |
31 |
{message.text}
32 |
33 | );
34 | }
35 |
36 | });
37 |
38 | module.exports = MessageListItem;
39 |
--------------------------------------------------------------------------------
/examples/flux-chat/js/components/MessageSection.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 MessageComposer = require('./MessageComposer.react');
14 | var MessageListItem = require('./MessageListItem.react');
15 | var MessageStore = require('../stores/MessageStore');
16 | var React = require('react');
17 | var ThreadStore = require('../stores/ThreadStore');
18 |
19 | function getStateFromStores() {
20 | return {
21 | messages: MessageStore.getAllForCurrentThread(),
22 | thread: ThreadStore.getCurrent()
23 | };
24 | }
25 |
26 | function getMessageListItem(message) {
27 | return (
28 |
32 | );
33 | }
34 |
35 | var MessageSection = React.createClass({
36 |
37 | getInitialState: function() {
38 | return getStateFromStores();
39 | },
40 |
41 | componentDidMount: function() {
42 | this._scrollToBottom();
43 | MessageStore.addChangeListener(this._onChange);
44 | ThreadStore.addChangeListener(this._onChange);
45 | },
46 |
47 | componentWillUnmount: function() {
48 | MessageStore.removeChangeListener(this._onChange);
49 | ThreadStore.removeChangeListener(this._onChange);
50 | },
51 |
52 | render: function() {
53 | var messageListItems = this.state.messages.map(getMessageListItem);
54 | return (
55 |
56 |
{this.state.thread.name}
57 |
58 | {messageListItems}
59 |
60 |
61 |
62 | );
63 | },
64 |
65 | componentDidUpdate: function() {
66 | this._scrollToBottom();
67 | },
68 |
69 | _scrollToBottom: function() {
70 | var ul = this.refs.messageList.getDOMNode();
71 | ul.scrollTop = ul.scrollHeight;
72 | },
73 |
74 | /**
75 | * Event handler for 'change' events coming from the MessageStore
76 | */
77 | _onChange: function() {
78 | this.setState(getStateFromStores());
79 | }
80 |
81 | });
82 |
83 | module.exports = MessageSection;
84 |
--------------------------------------------------------------------------------
/examples/flux-chat/js/components/ThreadListItem.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 ChatThreadActionCreators = require('../actions/ChatThreadActionCreators');
14 | var React = require('react');
15 | var classNames = require('classnames');
16 |
17 | var ReactPropTypes = React.PropTypes;
18 |
19 | var ThreadListItem = React.createClass({
20 |
21 | propTypes: {
22 | thread: ReactPropTypes.object,
23 | currentThreadID: ReactPropTypes.string
24 | },
25 |
26 | render: function() {
27 | var thread = this.props.thread;
28 | var lastMessage = thread.lastMessage;
29 | return (
30 |
36 |
{thread.name}
37 |
38 | {lastMessage.date.toLocaleTimeString()}
39 |
40 |
41 | {lastMessage.text}
42 |
43 |
44 | );
45 | },
46 |
47 | _onClick: function() {
48 | ChatThreadActionCreators.clickThread(this.props.thread.id);
49 | }
50 |
51 | });
52 |
53 | module.exports = ThreadListItem;
54 |
--------------------------------------------------------------------------------
/examples/flux-chat/js/components/ThreadSection.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 React = require('react');
14 | var ThreadListItem = require('../components/ThreadListItem.react');
15 | var ThreadStore = require('../stores/ThreadStore');
16 | var UnreadThreadStore = require('../stores/UnreadThreadStore');
17 |
18 | function getStateFromStores() {
19 | return {
20 | threads: ThreadStore.getAllChrono(),
21 | currentThreadID: ThreadStore.getCurrentID(),
22 | unreadCount: UnreadThreadStore.getCount()
23 | };
24 | }
25 |
26 | var ThreadSection = React.createClass({
27 |
28 | getInitialState: function() {
29 | return getStateFromStores();
30 | },
31 |
32 | componentDidMount: function() {
33 | ThreadStore.addChangeListener(this._onChange);
34 | UnreadThreadStore.addChangeListener(this._onChange);
35 | },
36 |
37 | componentWillUnmount: function() {
38 | ThreadStore.removeChangeListener(this._onChange);
39 | UnreadThreadStore.removeChangeListener(this._onChange);
40 | },
41 |
42 | render: function() {
43 | var threadListItems = this.state.threads.map(function(thread) {
44 | return (
45 |
50 | );
51 | }, this);
52 | var unread =
53 | this.state.unreadCount === 0 ?
54 | null :
55 | Unread threads: {this.state.unreadCount};
56 | return (
57 |
58 |
59 | {unread}
60 |
61 |
62 | {threadListItems}
63 |
64 |
65 | );
66 | },
67 |
68 | /**
69 | * Event handler for 'change' events coming from the stores
70 | */
71 | _onChange: function() {
72 | this.setState(getStateFromStores());
73 | }
74 |
75 | });
76 |
77 | module.exports = ThreadSection;
78 |
--------------------------------------------------------------------------------
/examples/flux-chat/js/constants/ChatConstants.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 keyMirror = require('keymirror');
14 |
15 | module.exports = {
16 |
17 | ActionTypes: keyMirror({
18 | CLICK_THREAD: null,
19 | CREATE_MESSAGE: null,
20 | RECEIVE_RAW_CREATED_MESSAGE: null,
21 | RECEIVE_RAW_MESSAGES: null
22 | })
23 |
24 | };
25 |
--------------------------------------------------------------------------------
/examples/flux-chat/js/dispatcher/ChatAppDispatcher.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 Dispatcher = require('flux').Dispatcher;
14 |
15 | module.exports = new Dispatcher();
16 |
--------------------------------------------------------------------------------
/examples/flux-chat/js/stores/MessageStore.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 ChatAppDispatcher = require('../dispatcher/ChatAppDispatcher');
14 | var ChatConstants = require('../constants/ChatConstants');
15 | var ChatMessageUtils = require('../utils/ChatMessageUtils');
16 | var EventEmitter = require('events').EventEmitter;
17 | var ThreadStore = require('../stores/ThreadStore');
18 | var assign = require('object-assign');
19 |
20 | var ActionTypes = ChatConstants.ActionTypes;
21 | var CHANGE_EVENT = 'change';
22 |
23 | var _messages = {};
24 |
25 | function _addMessages(rawMessages) {
26 | rawMessages.forEach(function(message) {
27 | if (!_messages[message.id]) {
28 | _messages[message.id] = ChatMessageUtils.convertRawMessage(
29 | message,
30 | ThreadStore.getCurrentID()
31 | );
32 | }
33 | });
34 | }
35 |
36 | function _markAllInThreadRead(threadID) {
37 | for (var id in _messages) {
38 | if (_messages[id].threadID === threadID) {
39 | _messages[id].isRead = true;
40 | }
41 | }
42 | }
43 |
44 | var MessageStore = assign({}, EventEmitter.prototype, {
45 |
46 | emitChange: function() {
47 | this.emit(CHANGE_EVENT);
48 | },
49 |
50 | /**
51 | * @param {function} callback
52 | */
53 | addChangeListener: function(callback) {
54 | this.on(CHANGE_EVENT, callback);
55 | },
56 |
57 | removeChangeListener: function(callback) {
58 | this.removeListener(CHANGE_EVENT, callback);
59 | },
60 |
61 | get: function(id) {
62 | return _messages[id];
63 | },
64 |
65 | getAll: function() {
66 | return _messages;
67 | },
68 |
69 | /**
70 | * @param {string} threadID
71 | */
72 | getAllForThread: function(threadID) {
73 | var threadMessages = [];
74 | for (var id in _messages) {
75 | if (_messages[id].threadID === threadID) {
76 | threadMessages.push(_messages[id]);
77 | }
78 | }
79 | threadMessages.sort(function(a, b) {
80 | if (a.date < b.date) {
81 | return -1;
82 | } else if (a.date > b.date) {
83 | return 1;
84 | }
85 | return 0;
86 | });
87 | return threadMessages;
88 | },
89 |
90 | getAllForCurrentThread: function() {
91 | return this.getAllForThread(ThreadStore.getCurrentID());
92 | }
93 |
94 | });
95 |
96 | MessageStore.dispatchToken = ChatAppDispatcher.register(function(action) {
97 |
98 | switch(action.type) {
99 |
100 | case ActionTypes.CLICK_THREAD:
101 | ChatAppDispatcher.waitFor([ThreadStore.dispatchToken]);
102 | _markAllInThreadRead(ThreadStore.getCurrentID());
103 | MessageStore.emitChange();
104 | break;
105 |
106 | case ActionTypes.CREATE_MESSAGE:
107 | var message = ChatMessageUtils.getCreatedMessageData(
108 | action.text,
109 | action.currentThreadID
110 | );
111 | _messages[message.id] = message;
112 | MessageStore.emitChange();
113 | break;
114 |
115 | case ActionTypes.RECEIVE_RAW_MESSAGES:
116 | _addMessages(action.rawMessages);
117 | ChatAppDispatcher.waitFor([ThreadStore.dispatchToken]);
118 | _markAllInThreadRead(ThreadStore.getCurrentID());
119 | MessageStore.emitChange();
120 | break;
121 |
122 | default:
123 | // do nothing
124 | }
125 |
126 | });
127 |
128 | module.exports = MessageStore;
129 |
--------------------------------------------------------------------------------
/examples/flux-chat/js/stores/ThreadStore.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 ChatAppDispatcher = require('../dispatcher/ChatAppDispatcher');
14 | var ChatConstants = require('../constants/ChatConstants');
15 | var ChatMessageUtils = require('../utils/ChatMessageUtils');
16 | var EventEmitter = require('events').EventEmitter;
17 | var assign = require('object-assign');
18 |
19 | var ActionTypes = ChatConstants.ActionTypes;
20 | var CHANGE_EVENT = 'change';
21 |
22 | var _currentID = null;
23 | var _threads = {};
24 |
25 | var ThreadStore = assign({}, EventEmitter.prototype, {
26 |
27 | init: function(rawMessages) {
28 | rawMessages.forEach(function(message) {
29 | var threadID = message.threadID;
30 | var thread = _threads[threadID];
31 | if (thread && thread.lastMessage.timestamp > message.timestamp) {
32 | return;
33 | }
34 | _threads[threadID] = {
35 | id: threadID,
36 | name: message.threadName,
37 | lastMessage: ChatMessageUtils.convertRawMessage(message, _currentID)
38 | };
39 | }, this);
40 |
41 | if (!_currentID) {
42 | var allChrono = this.getAllChrono();
43 | _currentID = allChrono[allChrono.length - 1].id;
44 | }
45 |
46 | _threads[_currentID].lastMessage.isRead = true;
47 | },
48 |
49 | emitChange: function() {
50 | this.emit(CHANGE_EVENT);
51 | },
52 |
53 | /**
54 | * @param {function} callback
55 | */
56 | addChangeListener: function(callback) {
57 | this.on(CHANGE_EVENT, callback);
58 | },
59 |
60 | /**
61 | * @param {function} callback
62 | */
63 | removeChangeListener: function(callback) {
64 | this.removeListener(CHANGE_EVENT, callback);
65 | },
66 |
67 | /**
68 | * @param {string} id
69 | */
70 | get: function(id) {
71 | return _threads[id];
72 | },
73 |
74 | getAll: function() {
75 | return _threads;
76 | },
77 |
78 | getAllChrono: function() {
79 | var orderedThreads = [];
80 | for (var id in _threads) {
81 | var thread = _threads[id];
82 | orderedThreads.push(thread);
83 | }
84 | orderedThreads.sort(function(a, b) {
85 | if (a.lastMessage.date < b.lastMessage.date) {
86 | return -1;
87 | } else if (a.lastMessage.date > b.lastMessage.date) {
88 | return 1;
89 | }
90 | return 0;
91 | });
92 | return orderedThreads;
93 | },
94 |
95 | getCurrentID: function() {
96 | return _currentID;
97 | },
98 |
99 | getCurrent: function() {
100 | return this.get(this.getCurrentID());
101 | }
102 |
103 | });
104 |
105 | ThreadStore.dispatchToken = ChatAppDispatcher.register(function(action) {
106 |
107 | switch(action.type) {
108 |
109 | case ActionTypes.CLICK_THREAD:
110 | _currentID = action.threadID;
111 | _threads[_currentID].lastMessage.isRead = true;
112 | ThreadStore.emitChange();
113 | break;
114 |
115 | case ActionTypes.RECEIVE_RAW_MESSAGES:
116 | ThreadStore.init(action.rawMessages);
117 | ThreadStore.emitChange();
118 | break;
119 |
120 | default:
121 | // do nothing
122 | }
123 |
124 | });
125 |
126 | module.exports = ThreadStore;
127 |
--------------------------------------------------------------------------------
/examples/flux-chat/js/stores/UnreadThreadStore.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 ChatAppDispatcher = require('../dispatcher/ChatAppDispatcher');
14 | var ChatConstants = require('../constants/ChatConstants');
15 | var EventEmitter = require('events').EventEmitter;
16 | var MessageStore = require('../stores/MessageStore');
17 | var ThreadStore = require('../stores/ThreadStore');
18 | var assign = require('object-assign');
19 |
20 | var ActionTypes = ChatConstants.ActionTypes;
21 | var CHANGE_EVENT = 'change';
22 |
23 | var UnreadThreadStore = assign({}, EventEmitter.prototype, {
24 |
25 | emitChange: function() {
26 | this.emit(CHANGE_EVENT);
27 | },
28 |
29 | /**
30 | * @param {function} callback
31 | */
32 | addChangeListener: function(callback) {
33 | this.on(CHANGE_EVENT, callback);
34 | },
35 |
36 | /**
37 | * @param {function} callback
38 | */
39 | removeChangeListener: function(callback) {
40 | this.removeListener(CHANGE_EVENT, callback);
41 | },
42 |
43 | getCount: function() {
44 | var threads = ThreadStore.getAll();
45 | var unreadCount = 0;
46 | for (var id in threads) {
47 | if (!threads[id].lastMessage.isRead) {
48 | unreadCount++;
49 | }
50 | }
51 | return unreadCount;
52 | }
53 |
54 | });
55 |
56 | UnreadThreadStore.dispatchToken = ChatAppDispatcher.register(function(action) {
57 | ChatAppDispatcher.waitFor([
58 | ThreadStore.dispatchToken,
59 | MessageStore.dispatchToken
60 | ]);
61 |
62 | switch (action.type) {
63 |
64 | case ActionTypes.CLICK_THREAD:
65 | UnreadThreadStore.emitChange();
66 | break;
67 |
68 | case ActionTypes.RECEIVE_RAW_MESSAGES:
69 | UnreadThreadStore.emitChange();
70 | break;
71 |
72 | default:
73 | // do nothing
74 | }
75 | });
76 |
77 | module.exports = UnreadThreadStore;
78 |
--------------------------------------------------------------------------------
/examples/flux-chat/js/stores/__tests__/UnreadThreadStore-test.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 | jest.dontMock('../UnreadThreadStore');
14 | jest.dontMock('object-assign');
15 |
16 | describe('UnreadThreadStore', function() {
17 |
18 | var ChatAppDispatcher;
19 | var UnreadThreadStore;
20 | var callback;
21 |
22 | beforeEach(function() {
23 | ChatAppDispatcher = require('../../dispatcher/ChatAppDispatcher');
24 | UnreadThreadStore = require('../UnreadThreadStore');
25 | callback = ChatAppDispatcher.register.mock.calls[0][0];
26 | });
27 |
28 | it('registers a callback with the dispatcher', function() {
29 | expect(ChatAppDispatcher.register.mock.calls.length).toBe(1);
30 | });
31 |
32 | it('provides the unread thread count', function() {
33 | var ThreadStore = require('../ThreadStore');
34 | ThreadStore.getAll.mockReturnValueOnce(
35 | {
36 | foo: {lastMessage: {isRead: false}},
37 | bar: {lastMessage: {isRead: false}},
38 | baz: {lastMessage: {isRead: true}}
39 | }
40 | );
41 | expect(UnreadThreadStore.getCount()).toBe(2);
42 | });
43 |
44 | });
45 |
--------------------------------------------------------------------------------
/examples/flux-chat/js/utils/ChatMessageUtils.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 | convertRawMessage: function(rawMessage, currentThreadID) {
16 | return {
17 | id: rawMessage.id,
18 | threadID: rawMessage.threadID,
19 | authorName: rawMessage.authorName,
20 | date: new Date(rawMessage.timestamp),
21 | text: rawMessage.text,
22 | isRead: rawMessage.threadID === currentThreadID
23 | };
24 | },
25 |
26 | getCreatedMessageData: function(text, currentThreadID) {
27 | var timestamp = Date.now();
28 | return {
29 | id: 'm_' + timestamp,
30 | threadID: currentThreadID,
31 | authorName: 'Bill', // hard coded for the example
32 | date: new Date(timestamp),
33 | text: text,
34 | isRead: true
35 | };
36 | }
37 |
38 | };
39 |
--------------------------------------------------------------------------------
/examples/flux-chat/js/utils/ChatWebAPIUtils.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 ChatServerActionCreators = require('../actions/ChatServerActionCreators');
14 |
15 | // !!! Please Note !!!
16 | // We are using localStorage as an example, but in a real-world scenario, this
17 | // would involve XMLHttpRequest, or perhaps a newer client-server protocol.
18 | // The function signatures below might be similar to what you would build, but
19 | // the contents of the functions are just trying to simulate client-server
20 | // communication and server-side processing.
21 |
22 | module.exports = {
23 |
24 | getAllMessages: function() {
25 | // simulate retrieving data from a database
26 | var rawMessages = JSON.parse(localStorage.getItem('messages'));
27 |
28 | // simulate success callback
29 | ChatServerActionCreators.receiveAll(rawMessages);
30 | },
31 |
32 | createMessage: function(message, threadName) {
33 | // simulate writing to a database
34 | var rawMessages = JSON.parse(localStorage.getItem('messages'));
35 | var timestamp = Date.now();
36 | var id = 'm_' + timestamp;
37 | var threadID = message.threadID || ('t_' + Date.now());
38 | var createdMessage = {
39 | id: id,
40 | threadID: threadID,
41 | threadName: threadName,
42 | authorName: message.authorName,
43 | text: message.text,
44 | timestamp: timestamp
45 | };
46 | rawMessages.push(createdMessage);
47 | localStorage.setItem('messages', JSON.stringify(rawMessages));
48 |
49 | // simulate success callback
50 | setTimeout(function() {
51 | ChatServerActionCreators.receiveCreatedMessage(createdMessage);
52 | }, 0);
53 | }
54 |
55 | };
56 |
--------------------------------------------------------------------------------
/examples/flux-chat/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flux-chat",
3 | "version": "0.0.2",
4 | "description": "Example Flux chat application primarily intended to explain the use of Dispatcher.waitFor().",
5 | "repository": "https://github.com/facebook/flux",
6 | "main": "js/app.js",
7 | "dependencies": {
8 | "classnames": "^2.1.3",
9 | "flux": "^2.0.0",
10 | "keymirror": "~0.1.0",
11 | "object-assign": "^1.0.0",
12 | "react": "^0.12.0"
13 | },
14 | "devDependencies": {
15 | "browserify": "^6.2.0",
16 | "envify": "^3.0.0",
17 | "jest-cli": "^0.4.3",
18 | "reactify": "^0.15.2",
19 | "uglify-js": "~2.4.15",
20 | "watchify": "^2.1.1"
21 | },
22 | "scripts": {
23 | "start": "watchify -o js/bundle.js -v -d js/app.js",
24 | "build": "browserify . -t [envify --NODE_ENV production] | uglifyjs -cm > js/bundle.min.js",
25 | "test": "jest"
26 | },
27 | "author": "Bill Fisher",
28 | "browserify": {
29 | "transform": [
30 | "reactify",
31 | "envify"
32 | ]
33 | },
34 | "jest": {
35 | "rootDir": "./js"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var Dispatcher = require('flux').Dispatcher;
2 | var EventEmitter = require('events').EventEmitter;
3 | var objectAssign = require('object-assign');
4 |
5 | var CHANGE_EVENT = 'change';
6 | var VIEW_ACTION = 'VIEW_ACTION.';
7 | var SERVER_ACTION = 'SERVER_ACTION.';
8 |
9 | var Actions = function Actions(dispatcher, isServerAction) {
10 | var actionSource = isServerAction ? SERVER_ACTION : VIEW_ACTION;
11 | var propertyNames = Object.getOwnPropertyNames(Object.getPrototypeOf(this));
12 | var _this = this; // to make the code below clearer
13 |
14 | propertyNames.forEach(function (name) {
15 | if (typeof _this[name] === 'function' && name !== 'constructor') {
16 | _this[name] = _this[name].bind({
17 | _actionType: actionSource + name, // save for later dispatch below
18 | dispatch: function dispatch() {
19 | dispatcher.dispatch({
20 | type: this._actionType, // use the _actionType saved above
21 | args: arguments
22 | });
23 | }
24 | });
25 | }
26 | });
27 | };
28 |
29 | var Store = function (dispatcher, options) {
30 | this._actionHandlers = {};
31 | this._dispatcher = dispatcher,
32 | this.eventEmitter = new EventEmitter(),
33 |
34 | // Bind store to the callback, and register the callback with the dispatcher
35 | this.dispatchToken = dispatcher.register(function (action) {
36 | if (action.type in this._actionHandlers) {
37 | var defaultAction = action.type.replace(/\..*/, '.*'); // e.g. VIEW_ACTION.*
38 | if (defaultAction in this._actionHandlers) {
39 | this._actionHandlers[defaultAction].call(this);
40 | }
41 | var retval = this._actionHandlers[action.type].apply(this, action.args);
42 | if (retval !== false) { // no change event if handler returns false
43 | this.eventEmitter.emit(CHANGE_EVENT);
44 | }
45 | }
46 | }.bind(this));
47 |
48 | // Assign action handlers
49 | var assignHandlers = function (actionSource, handlers) {
50 | for (var actionType in handlers) {
51 | this._actionHandlers[actionSource + actionType] = handlers[actionType];
52 | }
53 | }.bind(this);
54 | assignHandlers(VIEW_ACTION, options.viewActionHandlers || {});
55 | assignHandlers(SERVER_ACTION, options.serverActionHandlers || {});
56 | };
57 |
58 | Store.prototype.waitFor = function (stores) {
59 | var tokens = stores.map(function (store) {
60 | return store.dispatchToken;
61 | });
62 | this._dispatcher.waitFor(tokens);
63 | };
64 |
65 | function connectToStores(React, Component, stores, onChangeName) {
66 | return React.createClass({
67 | componentDidMount: function () {
68 | this._isMounted = true;
69 | stores.forEach(function (store) {
70 | return store.eventEmitter.on(CHANGE_EVENT, this._onChange);
71 | }.bind(this));
72 | },
73 |
74 | componentWillUnmount: function () {
75 | this._isMounted = false;
76 | stores.forEach(function (store) {
77 | return store.eventEmitter.removeListener(CHANGE_EVENT, this._onChange);
78 | }.bind(this));
79 | },
80 |
81 | _onChange: function () {
82 | if (this._isMounted) {
83 | if (onChangeName) {
84 | // Call the onChange event handler
85 | this.refs.wrapped[onChangeName].apply(this.refs.wrapped);
86 | } else {
87 | this.forceUpdate();
88 | }
89 | }
90 | },
91 |
92 | render: function () {
93 | var props = this.props;
94 | if (onChangeName) {
95 | // Reference to the wrapped element for _onChange handler
96 | props = objectAssign({ref: 'wrapped'}, props);
97 | }
98 | return React.createElement(Component, props);
99 | }
100 | });
101 | }
102 |
103 | module.exports = {
104 | Dispatcher: Dispatcher,
105 | Actions: Actions,
106 | Store: Store,
107 | connectToStores: connectToStores
108 | };
109 |
110 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fluent-js",
3 | "version": "0.9.1",
4 | "license": "MIT",
5 | "description": "A lightweight Facebook Flux library.",
6 | "repository": "https://github.com/twincl/fluent",
7 | "author": "Arthur Liao",
8 | "main": "index.js",
9 | "dependencies": {
10 | "flux": "^2.1.1",
11 | "object-assign": "^4.0.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------