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

File diff suppressed because one or more lines are too long

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:

View File

@ -11,14 +11,8 @@ import (
)
type connectDefaults struct {
Name string
Host string
Port int
Channels []string
Password bool
SSL bool
ReadOnly bool
ShowDetails bool
*config.Defaults
Password bool
}
type dispatchVersion struct {
@ -28,7 +22,7 @@ type dispatchVersion struct {
}
type indexData struct {
Defaults *config.Defaults
Defaults connectDefaults
Servers []Server
Channels []*storage.Channel
OpenDMs []storage.Tab
@ -48,7 +42,7 @@ func (d *Dispatch) getIndexData(r *http.Request, state *State) *indexData {
cfg := d.Config()
data := indexData{
Defaults: &cfg.Defaults,
Defaults: connectDefaults{Defaults: &cfg.Defaults},
HexIP: cfg.HexIP,
Version: dispatchVersion{
Tag: version.Tag,
@ -57,9 +51,7 @@ func (d *Dispatch) getIndexData(r *http.Request, state *State) *indexData {
},
}
if data.Defaults.Password != "" {
data.Defaults.Password = "******"
}
data.Defaults.Password = cfg.Defaults.Password != ""
if state == nil {
data.Settings = storage.DefaultClientSettings()

View File

@ -39,14 +39,8 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchServer(in *jlexer.Lexer, out
}
switch key {
case "defaults":
if in.IsNull() {
in.Skip()
out.Defaults = nil
} else {
if out.Defaults == nil {
out.Defaults = new(config.Defaults)
}
easyjson7e607aefDecodeGithubComKhliengDispatchConfig(in, out.Defaults)
if data := in.Raw(); in.Ok() {
in.AddError((out.Defaults).UnmarshalJSON(data))
}
case "servers":
if in.IsNull() {
@ -183,11 +177,11 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchServer(out *jwriter.Writer, i
out.RawByte('{')
first := true
_ = first
if in.Defaults != nil {
if true {
const prefix string = ",\"defaults\":"
first = false
out.RawString(prefix[1:])
easyjson7e607aefEncodeGithubComKhliengDispatchConfig(out, *in.Defaults)
out.Raw((in.Defaults).MarshalJSON())
}
if len(in.Servers) != 0 {
const prefix string = ",\"servers\":"
@ -448,163 +442,6 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchStorage(out *jwriter.Writer,
}
out.RawByte('}')
}
func easyjson7e607aefDecodeGithubComKhliengDispatchConfig(in *jlexer.Lexer, out *config.Defaults) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeFieldName(false)
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "name":
out.Name = string(in.String())
case "host":
out.Host = string(in.String())
case "port":
out.Port = int(in.Int())
case "channels":
if in.IsNull() {
in.Skip()
out.Channels = nil
} else {
in.Delim('[')
if out.Channels == nil {
if !in.IsDelim(']') {
out.Channels = make([]string, 0, 4)
} else {
out.Channels = []string{}
}
} else {
out.Channels = (out.Channels)[:0]
}
for !in.IsDelim(']') {
var v10 string
v10 = string(in.String())
out.Channels = append(out.Channels, v10)
in.WantComma()
}
in.Delim(']')
}
case "password":
out.Password = string(in.String())
case "ssl":
out.SSL = bool(in.Bool())
case "readOnly":
out.ReadOnly = bool(in.Bool())
case "showDetails":
out.ShowDetails = bool(in.Bool())
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson7e607aefEncodeGithubComKhliengDispatchConfig(out *jwriter.Writer, in config.Defaults) {
out.RawByte('{')
first := true
_ = first
if in.Name != "" {
const prefix string = ",\"name\":"
first = false
out.RawString(prefix[1:])
out.String(string(in.Name))
}
if in.Host != "" {
const prefix string = ",\"host\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(in.Host))
}
if in.Port != 0 {
const prefix string = ",\"port\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Int(int(in.Port))
}
if len(in.Channels) != 0 {
const prefix string = ",\"channels\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
{
out.RawByte('[')
for v11, v12 := range in.Channels {
if v11 > 0 {
out.RawByte(',')
}
out.String(string(v12))
}
out.RawByte(']')
}
}
if in.Password != "" {
const prefix string = ",\"password\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(in.Password))
}
if in.SSL {
const prefix string = ",\"ssl\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Bool(bool(in.SSL))
}
if in.ReadOnly {
const prefix string = ",\"readOnly\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Bool(bool(in.ReadOnly))
}
if in.ShowDetails {
const prefix string = ",\"showDetails\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Bool(bool(in.ShowDetails))
}
out.RawByte('}')
}
func easyjson7e607aefDecodeGithubComKhliengDispatchServer1(in *jlexer.Lexer, out *dispatchVersion) {
isTopLevel := in.IsStart()
if in.IsNull() {
@ -705,6 +542,7 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchServer2(in *jlexer.Lexer, out
in.Skip()
return
}
out.Defaults = new(config.Defaults)
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeFieldName(false)
@ -715,6 +553,8 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchServer2(in *jlexer.Lexer, out
continue
}
switch key {
case "password":
out.Password = bool(in.Bool())
case "name":
out.Name = string(in.String())
case "host":
@ -737,15 +577,13 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchServer2(in *jlexer.Lexer, out
out.Channels = (out.Channels)[:0]
}
for !in.IsDelim(']') {
var v13 string
v13 = string(in.String())
out.Channels = append(out.Channels, v13)
var v10 string
v10 = string(in.String())
out.Channels = append(out.Channels, v10)
in.WantComma()
}
in.Delim(']')
}
case "password":
out.Password = bool(in.Bool())
case "ssl":
out.SSL = bool(in.Bool())
case "readOnly":
@ -766,10 +604,20 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchServer2(out *jwriter.Writer,
out.RawByte('{')
first := true
_ = first
if in.Name != "" {
const prefix string = ",\"name\":"
if in.Password {
const prefix string = ",\"password\":"
first = false
out.RawString(prefix[1:])
out.Bool(bool(in.Password))
}
if in.Name != "" {
const prefix string = ",\"name\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(in.Name))
}
if in.Host != "" {
@ -802,25 +650,15 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchServer2(out *jwriter.Writer,
}
{
out.RawByte('[')
for v14, v15 := range in.Channels {
if v14 > 0 {
for v11, v12 := range in.Channels {
if v11 > 0 {
out.RawByte(',')
}
out.String(string(v15))
out.String(string(v12))
}
out.RawByte(']')
}
}
if in.Password {
const prefix string = ",\"password\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.Bool(bool(in.Password))
}
if in.SSL {
const prefix string = ",\"ssl\":"
if first {