Experimenting with mulitple server selector

This commit is contained in:
James Barnsley
2021-10-03 21:18:21 +13:00
parent 7e18b2a8f3
commit 2e8205bd26
3 changed files with 219 additions and 107 deletions

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import { useStore, useDispatch } from 'react-redux';
import Link from './Link';
import Icon from './Icon';
@ -8,6 +8,185 @@ import { Button } from './Button';
import * as mopidyActions from '../services/mopidy/actions';
import { iconFromKeyword } from '../util/helpers';
import { I18n } from '../locale';
import LinksSentence from './LinksSentence';
import {
digestMopidyImages,
formatImages,
} from '../util/format';
import Thumbnail from './Thumbnail';
const callServer = ({ endpoint, method, params }) => new Promise((resolve, reject) => {
const body = {
jsonrpc: '2.0',
id: 1,
method,
params,
};
fetch(endpoint, {
method: 'POST',
mode: 'no-cors',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
.then((response) => response.json())
.then(({ result }) => resolve(result))
.catch((error) => { console.debug({ error }); reject() });
});
const Server = ({
server,
current_server,
}) => {
if (!server) return null;
const { id } = server;
const dispatch = useDispatch();
const [currentTrack, setCurrentTrack] = useState();
const [playState, setPlayState] = useState();
useEffect(() => {
const endpoint = `http://${server.host}:${server.port}/mopidy/rpc`;
callServer({
endpoint,
method: 'core.playback.get_current_track',
}).then(
(track) => {
callServer({
endpoint,
method: 'core.library.get_images',
params: {
uris: [track.uri],
},
}).then(({ [track.uri]: images }) => {
console.debug({ track })
setCurrentTrack({
...track,
images: images ? formatImages(digestMopidyImages(server, images)) : null,
});
});
},
() => setCurrentTrack(null),
);
callServer({
endpoint,
method: 'core.playback.get_state',
}).then(setPlayState, setPlayState);
}, [id]);
const remove = () => {
dispatch(mopidyActions.removeServer(server.id));
};
const setAsCurrent = () => {
dispatch(mopidyActions.setCurrentServer(server));
};
return (
<div className="sub-tabs__content">
<label className="field">
<div className="name">
<I18n path="settings.servers.current_track" />
{playState ? ` (${playState})` : ''}
</div>
<div className="input">
<div style={{ display: 'flex', alignItems: 'center' }}>
<Thumbnail images={currentTrack?.images} size="small" />
<div style={{ paddingLeft: '1rem' }}>
<div>{currentTrack?.name}</div>
<em>
<LinksSentence items={currentTrack?.artists} type="artist" nolinks />
</em>
</div>
</div>
</div>
</label>
<label className="field">
<div className="name">
<I18n path="settings.servers.name" />
</div>
<div className="input">
<TextField
type="text"
value={server.name}
onChange={(value) => dispatch(mopidyActions.updateServer({ id: server.id, name: value }))}
autosave
/>
</div>
</label>
<label className="field">
<div className="name">
<I18n path="settings.servers.host" />
</div>
<div className="input">
<TextField
value={server.host}
onChange={(value) => dispatch(mopidyActions.updateServer({ id: server.id, host: value }))}
autosave
/>
</div>
</label>
<label className="field">
<div className="name">
<I18n path="settings.servers.port" />
</div>
<div className="input">
<TextField
type="text"
value={server.port}
onChange={(value) => dispatch(mopidyActions.updateServer({ id: server.id, port: value }))}
autosave
/>
</div>
</label>
<div className="field checkbox">
<div className="name">
<I18n path="settings.servers.encryption.label" />
</div>
<div className="input">
<label>
<input
type="checkbox"
name="ssl"
value={server.ssl}
checked={server.ssl}
onChange={() => dispatch(mopidyActions.updateServer({ id: server.id, ssl: !server.ssl }))}
/>
<span className="label tooltip">
<I18n path="settings.servers.encryption.sublabel" />
<span className="tooltip__content">
<I18n path="settings.servers.encryption.description" />
</span>
</span>
</label>
</div>
</div>
<Button
type="primary"
onClick={setAsCurrent}
tracking={{
category: 'Servers',
action: 'SetAsCurrent',
label: (server.id === current_server ? 'Reconnect' : 'Switch'),
}}
>
<I18n path={`settings.servers.${server.id === current_server ? 'reconnect' : 'switch'}`} />
</Button>
<Button
type="destructive"
disabled={server.id === current_server}
onClick={remove}
tracking={{ category: 'Servers', action: 'Delete' }}
>
<I18n path="actions.remove" />
</Button>
</div>
);
};
const Servers = ({
match: { params: { id: serverId } },
@ -19,7 +198,6 @@ const Servers = ({
mopidy: {
servers,
current_server,
ssl,
connected: mopidyConnected,
connecting: mopidyConnecting,
},
@ -35,7 +213,7 @@ const Servers = ({
history.push(`/settings/servers/${action.server.id}`);
};
const renderMenu = () => (
const Menu = () => (
<div className="sub-tabs__menu" id="servers-menu">
<div className="menu__inner">
{indexToArray(servers).map((server) => {
@ -102,110 +280,13 @@ const Servers = ({
</div>
);
const renderServer = () => {
if (!serverId) return null;
const server = servers[serverId];
if (!server) return null;
const remove = () => {
dispatch(mopidyActions.removeServer(server.id));
};
const setAsCurrent = () => {
dispatch(mopidyActions.setCurrentServer(server));
};
return (
<div className="sub-tabs__content">
<label className="field">
<div className="name">
<I18n path="settings.servers.name" />
</div>
<div className="input">
<TextField
type="text"
value={server.name}
onChange={(value) => dispatch(mopidyActions.updateServer({ id: server.id, name: value }))}
autosave
/>
</div>
</label>
<label className="field">
<div className="name">
<I18n path="settings.servers.host" />
</div>
<div className="input">
<TextField
value={server.host}
onChange={(value) => dispatch(mopidyActions.updateServer({ id: server.id, host: value }))}
autosave
/>
</div>
</label>
<label className="field">
<div className="name">
<I18n path="settings.servers.port" />
</div>
<div className="input">
<TextField
type="text"
value={server.port}
onChange={(value) => dispatch(mopidyActions.updateServer({ id: server.id, port: value }))}
autosave
/>
</div>
</label>
<div className="field checkbox">
<div className="name">
<I18n path="settings.servers.encryption.label" />
</div>
<div className="input">
<label>
<input
type="checkbox"
name="ssl"
value={server.ssl}
checked={server.ssl}
onChange={() => dispatch(mopidyActions.updateServer({ id: server.id, ssl: !server.ssl }))}
/>
<span className="label tooltip">
<I18n path="settings.servers.encryption.sublabel" />
<span className="tooltip__content">
<I18n path="settings.servers.encryption.description" />
</span>
</span>
</label>
</div>
</div>
<Button
type="primary"
onClick={setAsCurrent}
tracking={{
category: 'Servers',
action: 'SetAsCurrent',
label: (server.id === current_server ? 'Reconnect' : 'Switch'),
}}
>
<I18n path={`settings.servers.${server.id === current_server ? 'reconnect' : 'switch'}`} />
</Button>
<Button
type="destructive"
disabled={server.id === current_server}
onClick={remove}
tracking={{ category: 'Servers', action: 'Delete' }}
>
<I18n path="actions.remove" />
</Button>
</div>
);
};
return (
<div className="sub-tabs sub-tabs--servers">
{renderMenu()}
{renderServer()}
<Menu />
<Server
server={servers[serverId]}
current_server={current_server}
/>
</div>
);
};

View File

@ -1,16 +1,45 @@
import React from 'react';
import { connect } from 'react-redux';
import { connect, useSelector, useDispatch } from 'react-redux';
import { bindActionCreators } from 'redux';
import { withRouter } from 'react-router';
import Link from './Link';
import Icon from './Icon';
import Dropzones from './Fields/Dropzones';
import PinList from './Fields/PinList';
import DropdownField from './Fields/DropdownField';
import * as uiActions from '../services/ui/actions';
import * as mopidyActions from '../services/mopidy/actions';
import { I18n, i18n } from '../locale';
import { indexToArray } from '../util/arrays';
class Sidebar extends React.Component {
const ServerSwitcher = () => {
const servers = useSelector((state) => state.mopidy.servers);
const serversArray = indexToArray(servers);
const current_server = useSelector((state) => state.mopidy.current_server);
const dispatch = useDispatch();
if (!serversArray || serversArray.length <= 1) return null;
const onChange = (id) => dispatch(mopidyActions.setCurrentServer(servers[id]));
return (
<div style={{
width: '100%',
color: 'black',
}}>
<DropdownField
className="sidebar__menu__item"
icon="dns"
name="Server"
value={current_server}
valueAsLabel
options={serversArray.map((server) => ({ value: server.id, label: server.name }))}
handleChange={onChange}
/>
</div>
);
}
class Sidebar extends React.Component {
closeSidebar = () => {
const {
uiActions: {
@ -89,6 +118,7 @@ class Sidebar extends React.Component {
<aside className="sidebar">
<div className="sidebar__liner">
<nav className="sidebar__menu">
<ServerSwitcher />
<section className="sidebar__menu__section">
<Link to="/queue" history={history} className="sidebar__menu__item" activeClassName="sidebar__menu__item--active">

View File

@ -424,6 +424,7 @@ settings:
no_connections: No connections
you: ' (you)'
servers:
current_track: Current track
inactive: Inactive
connecting: Connecting
disconnected: Disconnected