Skip to content

Commit

Permalink
feat(mobx): complete store rewrite and better socket handling
Browse files Browse the repository at this point in the history
  • Loading branch information
ph1p committed May 1, 2020
1 parent c956805 commit 997e4c9
Show file tree
Hide file tree
Showing 17 changed files with 543 additions and 397 deletions.
31 changes: 18 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@
},
"dependencies": {
"@types/react-transition-group": "^4.2.4",
"mobx": "^5.15.4",
"mobx-react": "^6.2.2",
"prop-types": "^15.7.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-easy-state": "^6.1.3",
"react-router-dom": "^5.1.2",
"react-timeago": "^4.4.0",
"react-transition-group": "^4.3.0",
Expand Down
171 changes: 104 additions & 67 deletions src/Ui.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React from 'react';
import 'mobx-react-lite/batchingForReactDom';
import React, { useEffect, useState } from 'react';
import { observer } from 'mobx-react';
import * as ReactDOM from 'react-dom';
import styled, { createGlobalStyle } from 'styled-components';
import {
Expand All @@ -17,13 +19,15 @@ import Notifications from './components/Notifications';
import { DEFAULT_SERVER_URL } from './shared/constants';
import { ConnectionEnum } from './shared/interfaces';
import { SocketProvider } from './shared/SocketProvider';
import { state, view } from './shared/state';
import { sendMainMessage } from './shared/utils';
// views
import ChatView from './views/Chat';
import MinimizedView from './views/Minimized';
import UserListView from './views/UserList';

import { StoreProvider, useStore } from './store';
import { reaction } from 'mobx';

onmessage = (message) => {
if (message.data.pluginMessage) {
const { type, payload } = message.data.pluginMessage;
Expand Down Expand Up @@ -52,85 +56,113 @@ const AppWrapper = styled.div`
overflow: hidden;
`;

let socket: SocketIOClient.Socket;

const initSocketConnection = function (url) {
state.status = ConnectionEnum.NONE;
state.settings.url = url;

if (socket) {
socket.removeAllListeners();
socket.disconnect();
}

socket = io(url, {
reconnectionAttempts: 3,
forceNew: true,
transports: ['websocket'],
});

socket.on('connected', () => {
state.status = ConnectionEnum.CONNECTED;

socket.emit('set user', state.settings);
socket.emit('join room', {
room: state.roomName,
settings: state.settings,
});

sendMainMessage('ask-for-relaunch-message');
});

socket.on('connect_error', () => {
state.status = ConnectionEnum.ERROR;
});

socket.on('reconnect_error', () => {
state.status = ConnectionEnum.ERROR;
});

socket.on('chat message', (data) => {
state.addMessage(data);
});
const init = (serverUrl) => {
const App = observer(() => {
const store = useStore();
let [socket, setSocket] = useState<SocketIOClient.Socket>(undefined);

socket.on('join leave message', (data) => {
const username = data.user.name || 'Anon';
let message = 'joins the conversation';
function onFocus() {
sendMainMessage('focus', false);

if (data.type === 'LEAVE') {
message = 'leaves the conversation';
store.isFocused = false;
}
function onFocusOut() {
sendMainMessage('focus', false);
store.isFocused = false;
}
state.addNotification(`${username} ${message}`);
});

socket.on('online', (data) => (state.online = data));
function initSocketConnection() {
const url = store.settings.url || serverUrl;
store.status = ConnectionEnum.NONE;

sendMainMessage('get-root-data');
};
if (socket) {
socket.removeAllListeners();
socket.disconnect();
}

const init = (serverUrl) => {
initSocketConnection(serverUrl);
setSocket(
io(url, {
reconnectionAttempts: 3,
forceNew: true,
transports: ['websocket'],
})
);

// check focus
window.addEventListener('focus', () => {
sendMainMessage('focus', true);
state.isFocused = true;
});
sendMainMessage('get-root-data');
}

window.addEventListener('blur', () => {
sendMainMessage('focus', false);
state.isFocused = false;
});
useEffect(() => {
if (socket && store.status === ConnectionEnum.NONE) {
socket.on('connected', () => {
store.status = ConnectionEnum.CONNECTED;

socket.emit('set user', store.settings);
socket.emit('join room', {
room: store.roomName,
settings: store.settings,
});

sendMainMessage('ask-for-relaunch-message');
});

socket.on('connect_error', () => {
store.status = ConnectionEnum.ERROR;
});

socket.on('reconnect_error', () => {
store.status = ConnectionEnum.ERROR;
});

socket.on('chat message', (data) => {
store.addMessage(data);
});

socket.on('join leave message', (data) => {
const username = data.user.name || 'Anon';
let message = 'joins the conversation';

if (data.type === 'LEAVE') {
message = 'leaves the conversation';
}
store.addNotification(`${username} ${message}`);
});

socket.on('online', (data) => (store.online = data));
}

return () => {
if (socket) {
socket.removeAllListeners();
socket.disconnect();
}
};
}, [socket]);

useEffect(() => {
const serverUrlDisposer = reaction(
() => store.settings.url,
initSocketConnection
);

// check focus
window.addEventListener('focus', onFocus);
window.addEventListener('blur', onFocusOut);

return () => {
window.removeEventListener('focus', onFocus);
window.removeEventListener('blur', onFocusOut);
serverUrlDisposer();
};
}, []);

const App = view(() => {
return (
<AppWrapper>
<GlobalStyle />
<Notifications />

<SocketProvider socket={socket}>
<Router>
{state.isMinimized && <Redirect to="/minimized" />}
{store.isMinimized && <Redirect to="/minimized" />}
<Switch>
<Route exact path="/minimized">
<MinimizedView />
Expand All @@ -139,7 +171,7 @@ const init = (serverUrl) => {
<UserListView />
</Route>
<Route path="/">
<ChatView init={initSocketConnection} />
<ChatView />
</Route>
</Switch>
</Router>
Expand All @@ -148,5 +180,10 @@ const init = (serverUrl) => {
);
});

ReactDOM.render(<App />, document.getElementById('app'));
ReactDOM.render(
<StoreProvider>
<App />
</StoreProvider>,
document.getElementById('app')
);
};
12 changes: 0 additions & 12 deletions src/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,18 +100,6 @@ const isValidShape = (node) =>
node.type === 'INSTANCE' ||
node.type === 'POLYGON';

function iterateOverFile(node, cb) {
if ('children' in node) {
if (node.type !== 'INSTANCE') {
cb(node);
for (const child of node.children) {
cb(child);
iterateOverFile(child, cb);
}
}
}
}

let previousSelection = figma.currentPage.selection || [];

main().then(({ roomName, secret, history, instanceId }) => {
Expand Down
27 changes: 13 additions & 14 deletions src/components/Chatbar.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
import React, { FunctionComponent, useState, useEffect, useRef } from 'react';
import { useRouteMatch } from 'react-router-dom';
import styled from 'styled-components';
import { state, view } from '../shared/state';
import { sendMainMessage } from '../shared/utils';
import ColorPicker from './ColorPicker';
import { ConnectionEnum } from '../shared/interfaces';
import { ConnectionEnum } from '../shared/interfaces'; // store
import { observer } from 'mobx-react';
import { useStore } from '../store';

interface ChatProps {
sendMessage: (event: any) => void;
setTextMessage: (text: string) => void;
textMessage: string;
setSelectionIsChecked: (event: any) => void;
selectionIsChecked: boolean;
init?: (url: string) => void;
}

const ChatBar: FunctionComponent<ChatProps> = (props) => {
const store = useStore();
const isSettings = useRouteMatch('/settings');
const selection = state.selection.length;
const selection = store.selection.length;
const hasSelection = Boolean(selection);
const [show, setShow] = useState(hasSelection);
const chatTextInput = useRef(null);

const isFailed = state.status === ConnectionEnum.ERROR;
const isConnected = state.status === ConnectionEnum.CONNECTED;
const isFailed = store.status === ConnectionEnum.ERROR;
const isConnected = store.status === ConnectionEnum.CONNECTED;

useEffect(() => {
if (hasSelection) {
Expand All @@ -48,8 +48,7 @@ const ChatBar: FunctionComponent<ChatProps> = (props) => {
<ConnectionInfo isConnected={isConnected}>
{isFailed ? (
<>
connection failed{' '}
<span onClick={() => props.init(state.url)}>retry</span>
connection failed 🙈
</>
) : (
'connecting...'
Expand All @@ -60,7 +59,7 @@ const ChatBar: FunctionComponent<ChatProps> = (props) => {
<ChatInput hasSelection={hasSelection}>
<BellIcon
onClick={() =>
(state.settings.enableNotificationSound = !state.settings
(store.settings.enableNotificationSound = !store.settings
.enableNotificationSound)
}
>
Expand All @@ -69,16 +68,16 @@ const ChatBar: FunctionComponent<ChatProps> = (props) => {
fill="none"
viewBox="0 0 15 16"
>
{state.settings.enableNotificationSound ? (
{store.settings.enableNotificationSound ? (
<path
fill={state.settings.color}
fill={store.settings.color}
fillRule="evenodd"
d="M11 5v4l1 1H2l1-1V5a4 4 0 018 0zm1 4a2 2 0 002 2H0a2 2 0 002-2V5a5 5 0 0110 0v4zm-5 5l-2-2H4a3 3 0 106 0H9l-2 2z"
clipRule="evenodd"
/>
) : (
<path
fill={state.settings.color}
fill={store.settings.color}
fillRule="evenodd"
d="M4.998 11.472h9.475v-.882a1.76 1.76 0 01-1.764-1.765v-3.53a5.31 5.31 0 00-.134-1.188l-.88.854c.01.11.014.222.014.334v3.53c0 .617.202 1.187.544 1.647H6.027l-1.029 1zm5.718-8.924a4.295 4.295 0 00-7.597 2.747v3.53c0 .604-.194 1.162-.522 1.617l-1.06 1.03H.354v-.882a1.76 1.76 0 001.765-1.765v-3.53a5.295 5.295 0 019.315-3.445l-.718.698zm-5.009 9.807a1.706 1.706 0 103.413 0h1a2.706 2.706 0 11-5.413 0h1zM0 14.146l14-14 .707.708-14 14L0 14.146z"
clipRule="evenodd"
Expand Down Expand Up @@ -252,4 +251,4 @@ const SelectionCheckbox = styled.div`
}
`;

export default view(ChatBar);
export default observer(ChatBar);
Loading

0 comments on commit 997e4c9

Please sign in to comment.