Enable overriding connect defaults with query params, closes #49

This commit is contained in:
Ken-Håvard Lieng 2020-05-08 10:12:21 +02:00
parent dcbf3397c1
commit eab788a782
10 changed files with 177 additions and 328 deletions

View file

@ -14,6 +14,26 @@ const getSortedDefaultChannels = createSelector(
channels => channels.split(',').sort()
);
const transformChannels = channels => {
const comma = channels[channels.length - 1] === ',';
channels = channels
.split(',')
.map(channel => {
channel = channel.trim();
if (channel) {
if (isValidChannel(channel, false) && channel[0] !== '#') {
channel = `#${channel}`;
}
}
return channel;
})
.filter(s => s)
.join(',');
return comma ? `${channels},` : channels;
};
class Connect extends Component {
state = {
showOptionals: false
@ -51,26 +71,6 @@ class Connect extends Component {
return port;
};
transformChannels = channels => {
const comma = channels[channels.length - 1] === ',';
channels = channels
.split(',')
.map(channel => {
channel = channel.trim();
if (channel) {
if (isValidChannel(channel, false) && channel[0] !== '#') {
channel = `#${channel}`;
}
}
return channel;
})
.filter(s => s)
.join(',');
return comma ? `${channels},` : channels;
};
render() {
const { defaults, values } = this.props;
const { readOnly, showDetails } = defaults;
@ -117,7 +117,7 @@ class Connect extends Component {
<Error name="host" />
<Error name="port" />
<TextInput name="nick" />
<TextInput name="channels" transform={this.transformChannels} />
<TextInput name="channels" transform={transformChannels} />
{this.state.showOptionals && this.renderOptionals()}
<Button
className="connect-form-button-optionals"
@ -140,24 +140,38 @@ class Connect extends Component {
export default withFormik({
enableReinitialize: true,
mapPropsToValues: ({ defaults }) => {
let port = 6667;
if (defaults.port) {
({ port } = defaults);
mapPropsToValues: ({ defaults, query }) => {
let port = '6667';
if (query.port || defaults.port) {
port = query.port || defaults.port;
} else if (defaults.ssl) {
port = 6697;
port = '6697';
}
let { channels } = query;
if (channels) {
channels = transformChannels(channels);
}
let ssl;
if (query.ssl === 'true') {
ssl = true;
} else if (query.ssl === 'false') {
ssl = false;
} else {
ssl = defaults.ssl || false;
}
return {
name: defaults.name,
host: defaults.host,
name: query.name || defaults.name,
host: query.host || defaults.host,
port,
nick: '',
channels: defaults.channels.join(','),
username: '',
nick: query.nick || '',
channels: channels || defaults.channels.join(','),
username: query.username || '',
password: defaults.password ? ' ' : '',
realname: '',
tls: defaults.ssl || false
realname: query.realname || '',
tls: ssl
};
},
validate: values => {
@ -203,6 +217,8 @@ export default withFormik({
const channels = values.channels ? values.channels.split(',') : [];
delete values.channels;
values.password = values.password.trim();
values.port = `${values.port}`;
connect(values);
select(values.host);

View file

@ -8,7 +8,8 @@ import connect from 'utils/connect';
const mapState = createStructuredSelector({
defaults: getConnectDefaults,
hexIP: state => getApp(state).hexIP
hexIP: state => getApp(state).hexIP,
query: state => state.router.query
});
const mapDispatch = {

View file

@ -85,7 +85,7 @@ describe('tab reducer', () => {
it('clears the tab when navigating to a non-tab page', () => {
let state = reducer(undefined, setSelectedTab('srv', '#chan'));
state = reducer(state, locationChanged('settings'));
state = reducer(state, locationChanged('settings', {}, {}));
expect(state).toEqual({
selected: {},
@ -96,10 +96,14 @@ describe('tab reducer', () => {
it('selects the tab and adds it to history when navigating to a tab', () => {
const state = reducer(
undefined,
locationChanged('chat', {
server: 'srv',
name: '#chan'
})
locationChanged(
'chat',
{
server: 'srv',
name: '#chan'
},
{}
)
);
expect(state).toEqual({

View file

@ -15,7 +15,8 @@ const initialState = {
windowWidth: 0,
connectDefaults: {
name: '',
address: '',
host: '',
port: '',
channels: [],
ssl: false,
password: false,

View file

@ -6,11 +6,21 @@ export const PUSH = 'ROUTER_PUSH';
export const REPLACE = 'ROUTER_REPLACE';
export function locationChanged(route, params, location) {
Object.keys(params).forEach(key => {
params[key] = decodeURIComponent(params[key]);
});
const query = {};
new URLSearchParams(location.search).forEach((value, key) => {
query[key] = value;
});
return {
type: LOCATION_CHANGED,
route,
params,
location
query,
path: decodeURIComponent(location.pathname)
};
}
@ -28,13 +38,9 @@ export function replace(path) {
};
}
export function routeReducer(state = {}, action) {
if (action.type === LOCATION_CHANGED) {
return {
route: action.route,
params: action.params,
location: action.location
};
export function routeReducer(state = {}, { type, ...action }) {
if (type === LOCATION_CHANGED) {
return action;
}
return state;
@ -44,7 +50,7 @@ export function routeMiddleware() {
return next => action => {
switch (action.type) {
case PUSH:
history.push(action.path);
history.push(`${action.path}`);
break;
case REPLACE:
history.replace(action.path);
@ -55,24 +61,13 @@ export function routeMiddleware() {
};
}
function decode(location) {
location.pathname = decodeURIComponent(location.pathname);
return location;
}
function match(routes, location) {
let params;
for (let i = 0; i < routes.length; i++) {
params = routes[i].pattern.match(location.pathname);
const params = routes[i].pattern.match(location.pathname);
if (params !== null) {
const keys = Object.keys(params);
for (let j = 0; j < keys.length; j++) {
params[keys[j]] = decodeURIComponent(params[keys[j]]);
}
return locationChanged(routes[i].name, params, decode({ ...location }));
return locationChanged(routes[i].name, params, location);
}
}
return null;
}
export default function initRouter(routes, store) {
@ -91,16 +86,11 @@ export default function initRouter(routes, store) {
let matched = match(patterns, history.location);
if (matched) {
store.dispatch(matched);
} else {
matched = { location: {} };
}
history.listen(({ location }) => {
const nextMatch = match(patterns, location);
if (
nextMatch &&
nextMatch.location.pathname !== matched.location.pathname
) {
if (nextMatch && nextMatch.path !== matched?.path) {
matched = nextMatch;
store.dispatch(matched);
}

View file

@ -26,6 +26,7 @@
"babel-loader": "^8.1.0",
"brotli": "^1.3.1",
"copy-webpack-plugin": "^5.1.1",
"cross-env": "^7.0.2",
"css-loader": "^3.5.3",
"cssnano": "^4.1.10",
"del": "^5.1.0",
@ -92,9 +93,9 @@
"test": "jest",
"test:verbose": "jest --verbose",
"test:watch": "jest --watch",
"gen:install": "GO111MODULE=off go get -u github.com/andyleap/gencode github.com/mailru/easyjson/...",
"gen:install": "cross-env GO111MODULE=off go get -u github.com/andyleap/gencode github.com/mailru/easyjson/...",
"gen:binary": "gencode go -package storage -schema ../storage/storage.schema -unsafe",
"gen:json": "easyjson -all -lower_camel_case -omit_empty ../server/json.go ../server/index_data.go && easyjson -lower_camel_case -omit_empty ../storage/user.go"
"gen:json": "cross-env GO111MODULE=off easyjson -all -lower_camel_case -omit_empty ../server/json.go ../server/index_data.go && cross-env GO111MODULE=off easyjson -lower_camel_case -omit_empty ../storage/user.go"
},
"jest": {
"moduleNameMapper": {

View file

@ -2544,6 +2544,12 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
cross-env@^7.0.2:
version "7.0.2"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.2.tgz#bd5ed31339a93a3418ac4f3ca9ca3403082ae5f9"
dependencies:
cross-spawn "^7.0.1"
cross-spawn@^6.0.0, cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
@ -2554,7 +2560,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5:
shebang-command "^1.2.0"
which "^1.2.9"
cross-spawn@^7.0.0:
cross-spawn@^7.0.0, cross-spawn@^7.0.1:
version "7.0.2"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.2.tgz#d0d7dcfa74e89115c7619f4f721a94e1fdb716d6"
dependencies: