Improve routing

This commit is contained in:
Ken-Håvard Lieng 2018-12-06 13:27:53 +01:00
parent aca380629f
commit 35c2d682e3
7 changed files with 223 additions and 179 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,7 @@
import documentTitle from './documentTitle'; import documentTitle from './documentTitle';
import fonts from './fonts'; import fonts from './fonts';
import initialState from './initialState'; import initialState from './initialState';
import route from './route';
import socket from './socket'; import socket from './socket';
import storage from './storage'; import storage from './storage';
import widthUpdates from './widthUpdates'; import widthUpdates from './widthUpdates';
@ -8,6 +9,7 @@ import widthUpdates from './widthUpdates';
export default function runModules(ctx) { export default function runModules(ctx) {
fonts(ctx); fonts(ctx);
initialState(ctx); initialState(ctx);
route(ctx);
documentTitle(ctx); documentTitle(ctx);
socket(ctx); socket(ctx);

View File

@ -1,22 +1,13 @@
/* eslint-disable no-underscore-dangle */ /* eslint-disable no-underscore-dangle */
import Cookie from 'js-cookie';
import { socket as socketActions } from 'state/actions'; import { socket as socketActions } from 'state/actions';
import { getWrapWidth, setConnectDefaults, appSet } from 'state/app'; import { getWrapWidth, setConnectDefaults, appSet } from 'state/app';
import { addMessages } from 'state/messages'; import { addMessages } from 'state/messages';
import { setSettings } from 'state/settings'; import { setSettings } from 'state/settings';
import { select, updateSelection } from 'state/tab';
import { find } from 'utils';
import { when } from 'utils/observe'; import { when } from 'utils/observe';
import { replace } from 'utils/router';
function loadState({ store }, env) { function loadState({ store }, env) {
store.dispatch(setConnectDefaults(env.defaults)); store.dispatch(setConnectDefaults(env.defaults));
store.dispatch(
appSet({
hexIP: env.hexIP,
version: env.version
})
);
store.dispatch(setSettings(env.settings, true)); store.dispatch(setSettings(env.settings, true));
if (env.servers) { if (env.servers) {
@ -24,53 +15,6 @@ function loadState({ store }, env) {
type: socketActions.SERVERS, type: socketActions.SERVERS,
data: env.servers data: env.servers
}); });
const { router } = store.getState();
if (!router.route || router.route === 'chat') {
const tabs = [];
if (router.route === 'chat') {
tabs.push(router.params);
}
const cookie = Cookie.get('tab');
if (cookie) {
const [server, name = null] = cookie.split(/;(.+)/);
tabs.push({
server,
name
});
}
let found = false;
let i = 0;
while (!found) {
const tab = tabs[i];
i++;
if (
tab.name &&
find(
env.channels,
chan => chan.server === tab.server && chan.name === tab.name
)
) {
found = true;
store.dispatch(select(tab.server, tab.name, true));
} else if (find(env.servers, srv => srv.host === tab.server)) {
found = true;
store.dispatch(select(tab.server, null, true));
}
}
if (!found) {
store.dispatch(updateSelection());
}
}
} else {
store.dispatch(replace('/connect'));
} }
if (env.channels) { if (env.channels) {
@ -87,6 +31,14 @@ function loadState({ store }, env) {
}); });
} }
store.dispatch(
appSet({
initialized: true,
hexIP: env.hexIP,
version: env.version
})
);
// Wait until wrapWidth gets initialized so that height calculations // Wait until wrapWidth gets initialized so that height calculations
// only happen once for these messages // only happen once for these messages
when(store, getWrapWidth, () => { when(store, getWrapWidth, () => {

View File

@ -0,0 +1,59 @@
import Cookie from 'js-cookie';
import { select, updateSelection, tabExists } from 'state/tab';
import { observe, when } from 'utils/observe';
export default function route({ store }) {
let first = true;
when(
store,
state => state.app.initialized,
() =>
observe(
store,
state => state.router,
router => {
if (!router.route || router.route === 'chat') {
const state = store.getState();
let redirect = true;
const tabs = [];
if (router.route === 'chat') {
if (tabExists(router.params, state)) {
redirect = false;
} else {
tabs.push(router.params);
}
}
if (redirect && first) {
const cookie = Cookie.get('tab');
if (cookie) {
const [server, name = null] = cookie.split(/;(.+)/);
tabs.unshift({ server, name });
}
}
if (redirect) {
let found = false;
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
if (tabExists(tab, state)) {
store.dispatch(select(tab.server, tab.name, true));
found = true;
break;
}
}
if (!found) {
store.dispatch(updateSelection());
}
}
first = false;
}
}
)
);
}

View File

@ -46,9 +46,11 @@ describe('channel reducer', () => {
expect(state).toEqual({ expect(state).toEqual({
srv: { srv: {
chan1: { chan1: {
joined: true,
users: [{ mode: '', nick: 'nick1', renderName: 'nick1' }] users: [{ mode: '', nick: 'nick1', renderName: 'nick1' }]
}, },
chan2: { chan2: {
joined: true,
users: [{ mode: '', nick: 'nick2', renderName: 'nick2' }] users: [{ mode: '', nick: 'nick2', renderName: 'nick2' }]
} }
} }
@ -61,6 +63,7 @@ describe('channel reducer', () => {
expect(state).toEqual({ expect(state).toEqual({
srv: { srv: {
chan1: { chan1: {
joined: true,
users: [{ mode: '', nick: 'nick1', renderName: 'nick1' }] users: [{ mode: '', nick: 'nick1', renderName: 'nick1' }]
} }
} }
@ -81,9 +84,11 @@ describe('channel reducer', () => {
expect(state).toEqual({ expect(state).toEqual({
srv: { srv: {
chan1: { chan1: {
joined: true,
users: [{ mode: '', nick: 'nick1', renderName: 'nick1' }] users: [{ mode: '', nick: 'nick1', renderName: 'nick1' }]
}, },
chan2: { chan2: {
joined: true,
users: [] users: []
} }
} }
@ -105,12 +110,14 @@ describe('channel reducer', () => {
expect(state).toEqual({ expect(state).toEqual({
srv: { srv: {
chan1: { chan1: {
joined: true,
users: [ users: [
{ mode: '', nick: 'nick3', renderName: 'nick3' }, { mode: '', nick: 'nick3', renderName: 'nick3' },
{ mode: '', nick: 'nick2', renderName: 'nick2' } { mode: '', nick: 'nick2', renderName: 'nick2' }
] ]
}, },
chan2: { chan2: {
joined: true,
users: [{ mode: '', nick: 'nick2', renderName: 'nick2' }] users: [{ mode: '', nick: 'nick2', renderName: 'nick2' }]
} }
} }
@ -118,7 +125,8 @@ describe('channel reducer', () => {
}); });
it('handles SOCKET_USERS', () => { it('handles SOCKET_USERS', () => {
const state = reducer(undefined, { let state = reducer(undefined, socket_join('srv', 'chan1', 'nick1'));
state = reducer(state, {
type: actions.socket.USERS, type: actions.socket.USERS,
server: 'srv', server: 'srv',
channel: 'chan1', channel: 'chan1',
@ -128,6 +136,7 @@ describe('channel reducer', () => {
expect(state).toEqual({ expect(state).toEqual({
srv: { srv: {
chan1: { chan1: {
joined: true,
users: [ users: [
{ mode: '', nick: 'user3', renderName: 'user3' }, { mode: '', nick: 'user3', renderName: 'user3' },
{ mode: '', nick: 'user2', renderName: 'user2' }, { mode: '', nick: 'user2', renderName: 'user2' },
@ -141,18 +150,18 @@ describe('channel reducer', () => {
}); });
it('handles SOCKET_TOPIC', () => { it('handles SOCKET_TOPIC', () => {
const state = reducer(undefined, { let state = reducer(undefined, socket_join('srv', 'chan1', 'nick1'));
state = reducer(state, {
type: actions.socket.TOPIC, type: actions.socket.TOPIC,
server: 'srv', server: 'srv',
channel: 'chan1', channel: 'chan1',
topic: 'the topic' topic: 'the topic'
}); });
expect(state).toEqual({ expect(state).toMatchObject({
srv: { srv: {
chan1: { chan1: {
topic: 'the topic', topic: 'the topic'
users: []
} }
} }
}); });
@ -165,7 +174,7 @@ describe('channel reducer', () => {
state = reducer(state, socket_mode('srv', 'chan1', 'nick1', 'o', '')); state = reducer(state, socket_mode('srv', 'chan1', 'nick1', 'o', ''));
expect(state).toEqual({ expect(state).toMatchObject({
srv: { srv: {
chan1: { chan1: {
users: [ users: [
@ -183,7 +192,7 @@ describe('channel reducer', () => {
state = reducer(state, socket_mode('srv', 'chan1', 'nick2', 'o', '')); state = reducer(state, socket_mode('srv', 'chan1', 'nick2', 'o', ''));
state = reducer(state, socket_mode('srv', 'chan2', 'not_there', 'x', '')); state = reducer(state, socket_mode('srv', 'chan2', 'not_there', 'x', ''));
expect(state).toEqual({ expect(state).toMatchObject({
srv: { srv: {
chan1: { chan1: {
users: [ users: [
@ -210,11 +219,11 @@ describe('channel reducer', () => {
expect(state).toEqual({ expect(state).toEqual({
srv: { srv: {
chan1: { topic: 'the topic', users: [] }, chan1: { joined: true, topic: 'the topic', users: [] },
chan2: { users: [] } chan2: { joined: true, users: [] }
}, },
srv2: { srv2: {
chan1: { users: [] } chan1: { joined: true, users: [] }
} }
}); });
}); });

View File

@ -61,7 +61,7 @@ function init(state, server, channel) {
state[server] = {}; state[server] = {};
} }
if (channel && !state[server][channel]) { if (channel && !state[server][channel]) {
state[server][channel] = { users: [] }; state[server][channel] = { users: [], joined: false };
} }
} }
@ -124,6 +124,10 @@ export const getSelectedChannelUsers = createSelector(
export default createReducer( export default createReducer(
{}, {},
{ {
[actions.JOIN](state, { server, channels }) {
channels.forEach(channel => init(state, server, channel));
},
[actions.PART](state, { server, channels }) { [actions.PART](state, { server, channels }) {
channels.forEach(channel => delete state[server][channel]); channels.forEach(channel => delete state[server][channel]);
}, },
@ -131,6 +135,7 @@ export default createReducer(
[actions.socket.JOIN](state, { server, channels, user }) { [actions.socket.JOIN](state, { server, channels, user }) {
const channel = channels[0]; const channel = channels[0];
init(state, server, channel); init(state, server, channel);
state[server][channel].joined = true;
state[server][channel].users.push(createUser(user)); state[server][channel].users.push(createUser(user));
}, },
@ -160,12 +165,10 @@ export default createReducer(
}, },
[actions.socket.USERS](state, { server, channel, users }) { [actions.socket.USERS](state, { server, channel, users }) {
init(state, server, channel);
state[server][channel].users = users.map(nick => loadUser(nick)); state[server][channel].users = users.map(nick => loadUser(nick));
}, },
[actions.socket.TOPIC](state, { server, channel, topic }) { [actions.socket.TOPIC](state, { server, channel, topic }) {
init(state, server, channel);
state[server][channel].topic = topic; state[server][channel].topic = topic;
}, },
@ -191,6 +194,7 @@ export default createReducer(
if (data) { if (data) {
data.forEach(({ server, name, topic }) => { data.forEach(({ server, name, topic }) => {
init(state, server, name); init(state, server, name);
state[server][name].joined = true;
state[server][name].topic = topic; state[server][name].topic = topic;
}); });
} }

View File

@ -1,6 +1,8 @@
import get from 'lodash/get';
import createReducer from 'utils/createReducer'; import createReducer from 'utils/createReducer';
import { push, replace, LOCATION_CHANGED } from 'utils/router'; import { push, replace, LOCATION_CHANGED } from 'utils/router';
import * as actions from './actions'; import * as actions from './actions';
import { find } from '../utils';
const initialState = { const initialState = {
selected: {}, selected: {},
@ -54,19 +56,35 @@ export function select(server, name, doReplace) {
return navigate(`/${server}`); return navigate(`/${server}`);
} }
export function tabExists(
{ server, name },
{ servers, channels, privateChats }
) {
return (
(name && get(channels, [server, name])) ||
(!name && server && servers[server]) ||
(name && find(privateChats[server], nick => nick === name))
);
}
export function updateSelection() { export function updateSelection() {
return (dispatch, getState) => { return (dispatch, getState) => {
const state = getState(); const state = getState();
const { history } = state.tab; const { history } = state.tab;
const { servers } = state; const { servers } = state;
const { server } = state.tab.selected; const { server, name } = state.tab.selected;
if (tabExists({ server, name }, state)) {
return;
}
const serverAddrs = Object.keys(servers); const serverAddrs = Object.keys(servers);
if (serverAddrs.length === 0) { if (serverAddrs.length === 0) {
dispatch(replace('/connect')); dispatch(replace('/connect'));
} else if ( } else if (
history.length > 0 && history.length > 0 &&
history[history.length - 1] !== state.tab.selected tabExists(history[history.length - 1], state)
) { ) {
const tab = history[history.length - 1]; const tab = history[history.length - 1];
dispatch(select(tab.server, tab.name, true)); dispatch(select(tab.server, tab.name, true));