Skip to content
This repository has been archived by the owner on May 12, 2022. It is now read-only.

Commit

Permalink
Channel connection
Browse files Browse the repository at this point in the history
* Create a socket
* Connect to a channel on note selection
* Leave old channel and connect to new one when selection new note
  • Loading branch information
arkanoryn committed Aug 6, 2017
1 parent 8291d93 commit ff84e1a
Show file tree
Hide file tree
Showing 11 changed files with 199 additions and 68 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"antd": "^2.12.3",
"dotenv": "^4.0.0",
"lodash": "^4.17.4",
"phoenix": "^1.3.0",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-redux": "^5.0.5",
Expand Down
22 changes: 22 additions & 0 deletions src/Note/Actions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
import { UPDATE_BODY, UPDATE_STATUS, UPDATE_TITLE } from './Types';

export const connectToChannel = function connectToChannel(socket, noteId) {
return (dispatch) => {
if (!socket) { return false; }
const channel = socket.channel(`notes:${noteId}`);

channel.join().receive('ok', (response) => {
dispatch({ type: 'NOTE_CONNECTED_TO_CHANNEL', response, channel });
});

return false;
};
}

export const leaveChannel = function leaveChannel(channel) {
return (dispatch) => {
if (channel) {
channel.leave();
}
dispatch({ type: 'USER_CHANGED_NOTE' });
};
}

export const updateBody = function updateBody(id, body) {
return ({
type: UPDATE_BODY,
Expand Down
27 changes: 20 additions & 7 deletions src/Note/Reducer.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { GENERAL, UPDATE_BODY, UPDATE_TITLE, UPDATE_STATUS } from './Types';

const initialState = {
id: -1,
title: '',
body: '',
status: GENERAL,
currentNote: {
id: -1,
title: '',
body: '',
status: GENERAL,
},
channel: null,
}

let NoteReducer = function NoteReducer(state = initialState, action) {
Expand All @@ -13,23 +16,33 @@ let NoteReducer = function NoteReducer(state = initialState, action) {
return (Object.assign(
{},
state,
{body: action.body}
{...state.currentNote, body: action.body}
));

case UPDATE_STATUS:
return (Object.assign(
{},
state,
{status: action.status}
{...state.currentNote, status: action.status}
));

case UPDATE_TITLE:
return (Object.assign(
{},
state,
{title: action.title}
{...state.currentNote, title: action.title}
));

case 'NOTE_CONNECTED_TO_CHANNEL':
return {
...state,
channel: action.channel,
currentNote: action.response.note,
};

case 'USER_CHANGED_NOTE':
return initialState;

default:
return state;
}
Expand Down
155 changes: 98 additions & 57 deletions src/Note/index.js
Original file line number Diff line number Diff line change
@@ -1,86 +1,127 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import _ from 'lodash';
import { Col, Input, Layout, Row } from 'antd';
import { updateBody, updateTitle } from './Actions';
import {
connectToChannel,
leaveChannel,
updateBody,
updateTitle
} from './Actions';

const { Content } = Layout;
const { Content } = Layout;
const { TextArea } = Input;
const Props = {
const Props = {
notes: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
body: PropTypes.string.isRequired
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
body: PropTypes.string.isRequired
}).isRequired
).isRequired,
noteId: PropTypes.number.isRequired,
onUpdateBody: PropTypes.func.isRequired,
noteId: PropTypes.number.isRequired,
onUpdateBody: PropTypes.func.isRequired,
onUpdateTitle: PropTypes.func.isRequired,
onConnectToChannel: PropTypes.func.isRequired,
};

let RenderNote = function Note({notes, noteId, onUpdateBody, onUpdateTitle}) {
let currentNote;
class Note extends Component {
componentDidMount() {
const {socket, noteId, onConnectToChannel} = this.props;

if (noteId !== -1) {
let noteIndex = _.findIndex(notes, (note) => {return (note.id === noteId)});
if (noteId !== -1) {
onConnectToChannel(socket, noteId);
}
}

componentWillReceiveProps(nextProps) {
const {socket, noteId, channel, onConnectToChannel, onLeaveChannel} = this.props;

if (nextProps.noteId !== noteId) {
onLeaveChannel(channel);
onConnectToChannel(nextProps.socket, nextProps.noteId);
}
if (!socket && nextProps.socket) {
onConnectToChannel(nextProps.socket, nextProps.noteId);
}
}

componentWillUnmount() {
const {channel, onLeaveChannel} = this.props;

currentNote = notes[noteIndex]
} else {
currentNote = {title: 'No Note selected.', body: ''}
if (channel) {
onLeaveChannel(channel);
}
}

return (
<Content style={{ margin: '24px', overflow: 'initial' }}>
<div style={{ padding: 24, background: '#fff', textAlign: 'center' }}>
<Row>
<Col span={20} offset={2}>
<h1>
<TextArea
autosize={{ minRows: 1 }}
placeholder="Title"
value={currentNote.title}
onChange={({ target: { value: newTitle } }) =>
onUpdateTitle(currentNote.id, newTitle)}
/>
</h1>
</Col>
</Row>

<Row style={{ marginTop: 35, textAlign: "justify" }}>
<Col span={20} offset={2}>
<p>
<TextArea
autosize={{ minRows: 6 }}
placeholder="Note content"
value={currentNote.body || ''}
onChange={({ target: { value: newBody } }) =>
onUpdateBody(currentNote.id, newBody)}
/>
</p>
</Col>
</Row>
</div>
</Content>
);
render () {
const {notes, noteId, onUpdateBody, onUpdateTitle} = this.props;
let currentNote;

if (noteId !== -1) {
let noteIndex = _.findIndex(notes, (note) => {return (note.id === noteId)});

currentNote = notes[noteIndex]
} else {
currentNote = {title: 'No Note selected.', body: ''}
}

return (
<Content style={{ margin: '24px', overflow: 'initial' }}>
<div style={{ padding: 24, background: '#fff', textAlign: 'center' }}>
<Row>
<Col span={20} offset={2}>
<h1>
<TextArea
autosize={{ minRows: 1 }}
placeholder="Title"
value={currentNote.title}
onChange={({ target: { value: newTitle } }) =>
onUpdateTitle(currentNote.id, newTitle)}
/>
</h1>
</Col>
</Row>

<Row style={{ marginTop: 35, textAlign: "justify" }}>
<Col span={20} offset={2}>
<p>
<TextArea
autosize={{ minRows: 6 }}
placeholder="Note content"
value={currentNote.body || ''}
onChange={({ target: { value: newBody } }) =>
onUpdateBody(currentNote.id, newBody)}
/>
</p>
</Col>
</Row>
</div>
</Content>
);
}
}

RenderNote.PropTypes = Props;
Note.PropTypes = Props;

const mapStateToProps = function mapStateToProps(state) {
return {
socket: state.SessionReducer.socket,
notes: state.NotesListReducer.notes,
noteId: state.NotesListReducer.noteId
noteId: state.NotesListReducer.noteId,
channel: state.NoteReducer.channel,
};
};

const mapDispatchToProps = function mapDispatchToProps(dispatch) {
return ({
onUpdateBody: (id, body) => {dispatch(updateBody(id, body))},
onUpdateTitle: (id, title) => {dispatch(updateTitle(id, title))},
onUpdateBody: (id, body) => {dispatch(updateBody(id, body))},
onUpdateTitle: (id, title) => {dispatch(updateTitle(id, title))},
onConnectToChannel: (socket, id) => {dispatch(connectToChannel(socket, id))},
onLeaveChannel: (channel) => {dispatch(leaveChannel(channel))},
});
}

const Note = connect(mapStateToProps, mapDispatchToProps)(RenderNote);
Note = connect(mapStateToProps, mapDispatchToProps)(Note);
export default Note;
8 changes: 7 additions & 1 deletion src/Reducers/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { combineReducers } from 'redux';
import NoteReducer from '../Note/Reducer';
import NotesListReducer from '../NotesList/Reducer';
import SessionReducer from '../Session/Reducer';

let NoteApp = combineReducers({NotesListReducer});
let NoteApp = combineReducers({
SessionReducer,
NoteReducer,
NotesListReducer,
});

export default NoteApp;
17 changes: 17 additions & 0 deletions src/Session/Action.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Socket } from 'phoenix';
import { SOCKET_CONNECTED } from './Types';

const API_URL = process.env.REACT_APP_API_HOST_URL;
const WEBSOCKET_URL = API_URL.replace(/(https|http)/, 'ws').replace('/api', '');

export const connectToSocket = () => {
return function(dispatch) {
const socket = new Socket(`${WEBSOCKET_URL}/socket`, {});
socket.connect();

return (dispatch({
type: SOCKET_CONNECTED,
socket: socket,
}));
}
}
24 changes: 24 additions & 0 deletions src/Session/Reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { SOCKET_CONNECTED } from './Types';

const initialState = {
socket: null,
};

let SessionReducer = function SessionReducer(state = initialState, action) {
switch (action.type) {
case SOCKET_CONNECTED:
return (Object.assign(
{},
state,
{
...state,
socket: action.socket,
}
));

default:
return state;
}
}

export default SessionReducer;
1 change: 1 addition & 0 deletions src/Session/Types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const SOCKET_CONNECTED = 'SOCKET_CONNECTED';
4 changes: 2 additions & 2 deletions src/SubMenu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ class SubMenu extends Component {
});
}

if (isFetching !== prevProps) {
if (isFetching !== prevProps.isFetching) {
if (isFetching) {
message.loading('Fetching notes...', 0);
} else {
message.destroy();
if (errors.length === 0) {
if (errors.length === 0 && prevProps.isFetching) {
message.success('Notes successfully loaded! ;)');
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ import App from './App';
import registerServiceWorker from './registerServiceWorker';
import NoteApp from './Reducers';
import { fetchNotes } from './NotesList/Actions';
import { connectToSocket } from './Session/Action';

const loggerMiddleware = createLogger();

let store = createStore(NoteApp,
applyMiddleware(thunkMiddleware, loggerMiddleware)
);

store.dispatch(fetchNotes()).then(() => console.log(store.getState()));
store.dispatch(fetchNotes());
store.dispatch(connectToSocket());

ReactDOM.render(
<Provider store={store}>
Expand Down
4 changes: 4 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4798,6 +4798,10 @@ performance-now@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"

phoenix@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/phoenix/-/phoenix-1.3.0.tgz#1df2c27f986ee295e37c9983ec28ebac1d7f4a3e"

pify@^2.0.0, pify@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
Expand Down

0 comments on commit ff84e1a

Please sign in to comment.