Add fancier server selector

This commit is contained in:
Tulir Asokan 2019-07-16 03:12:13 +03:00
parent 924f627c58
commit e5e614fc4b
6 changed files with 73 additions and 18 deletions

View File

@ -47,7 +47,7 @@ def generate_mac(secret: str, nonce: str, user: str, password: str, admin: bool
@routes.get("/client/auth/servers")
async def get_registerable_servers(_: web.Request) -> web.Response:
return web.json_response(list(registration_secrets().keys()))
return web.json_response({key: value["url"] for key, value in registration_secrets().items()})
AuthRequestInfo = NamedTuple("AuthRequestInfo", api=HTTPAPI, secret=str, username=str, password=str)

View File

@ -410,13 +410,14 @@ paths:
content:
application/json:
schema:
type: array
items:
type: object
description: Key-value map from server name to homeserver URL
additionalProperties:
type: string
description: The homeserver URL
example:
- maunium.net
- example.com
- matrix.org
maunium.net: https://maunium.net
example.com: https://matrix.example.org
401:
$ref: '#/components/responses/Unauthorized'
'/client/auth/{server}/register':

View File

@ -36,8 +36,8 @@ async function defaultDelete(type, id) {
return await resp.json()
}
async function defaultPut(type, entry, id = undefined) {
const resp = await fetch(`${BASE_PATH}/${type}/${id || entry.id}`, {
async function defaultPut(type, entry, id = undefined, suffix = undefined) {
const resp = await fetch(`${BASE_PATH}/${type}/${id || entry.id}${suffix}`, {
headers: getHeaders(),
body: JSON.stringify(entry),
method: "PUT",
@ -221,6 +221,17 @@ export function getAvatarURL({ id, avatar_url }) {
export const putClient = client => defaultPut("client", client)
export const deleteClient = id => defaultDelete("client", id)
export const getClientAuthServers = () => defaultGet("/client/auth/servers")
export async function doClientAuth(server, type, username, password) {
const resp = await fetch(`${BASE_PATH}/client/auth/${server}/${type}`, {
headers: getHeaders(),
body: JSON.stringify({ username, password }),
method: "POST",
})
return await resp.json()
}
export default {
BASE_PATH,
login, ping, getFeatures, remoteGetFeatures,
@ -230,4 +241,5 @@ export default {
getInstanceDatabase, queryInstanceDatabase,
getPlugins, getPlugin, uploadPlugin, deletePlugin,
getClients, getClient, uploadAvatar, getAvatarURL, putClient, deleteClient,
getClientAuthServers, doClientAuth
}

View File

@ -15,6 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import React from "react"
import Select from "react-select"
import CreatableSelect from "react-select/creatable"
import Switch from "./Switch"
export const PrefTable = ({ children, wrapperClass }) => {
@ -56,10 +57,12 @@ export const PrefSwitch = ({ rowName, active, origActive, fullWidth = false, ...
</PrefRow>
)
export const PrefSelect = ({ rowName, value, origValue, fullWidth = false, ...args }) => (
export const PrefSelect = ({ rowName, value, origValue, fullWidth = false, creatable = false, ...args }) => (
<PrefRow name={rowName} fullWidth={fullWidth} labelFor={rowName}
changed={origValue !== undefined && value.id !== origValue}>
<Select className="select" {...args} id={rowName} value={value}/>
{creatable
? <CreatableSelect className="select" {...args} id={rowName} value={value}/>
: <Select className="select" {...args} id={rowName} value={value}/>}
</PrefRow>
)

View File

@ -17,7 +17,7 @@ import React from "react"
import { NavLink, withRouter } from "react-router-dom"
import { ReactComponent as ChevronRight } from "../../res/chevron-right.svg"
import { ReactComponent as UploadButton } from "../../res/upload.svg"
import { PrefTable, PrefSwitch, PrefInput } from "../../components/PreferenceTable"
import { PrefTable, PrefSwitch, PrefInput, PrefSelect } from "../../components/PreferenceTable"
import Spinner from "../../components/Spinner"
import api from "../../api"
import BaseMainView from "./BaseMainView"
@ -48,7 +48,7 @@ class Client extends BaseMainView {
get entryKeys() {
return ["id", "displayname", "homeserver", "avatar_url", "access_token", "sync",
"autojoin", "enabled", "started"]
"autojoin", "enabled", "started"]
}
get initialState() {
@ -84,6 +84,36 @@ class Client extends BaseMainView {
return client
}
get selectedHomeserver() {
return this.state.homeserver
? this.homeserverEntry([this.props.ctx.homeserversByURL[this.state.homeserver],
this.state.homeserver])
: {}
}
homeserverEntry = ([serverName, serverURL]) => serverURL && {
id: serverURL,
value: serverURL,
label: serverName || serverURL,
}
componentWillReceiveProps(nextProps) {
super.componentWillReceiveProps(nextProps)
this.updateHomeserverOptions()
}
updateHomeserverOptions() {
this.homeserverOptions = Object.entries(this.props.ctx.homeserversByName).map(this.homeserverEntry)
}
isValidHomeserver(value) {
try {
return Boolean(new URL(value))
} catch (err) {
return false
}
}
avatarUpload = async event => {
const file = event.target.files[0]
this.setState({
@ -165,9 +195,10 @@ class Client extends BaseMainView {
name={this.isNew ? "id" : ""} className="id"
value={this.state.id} origValue={this.props.entry.id}
placeholder="@fancybot:example.com" onChange={this.inputChange}/>
<PrefInput rowName="Homeserver" type="text" name="homeserver"
value={this.state.homeserver} origValue={this.props.entry.homeserver}
placeholder="https://example.com" onChange={this.inputChange}/>
<PrefSelect rowName="Homeserver" options={this.homeserverOptions} isSearchable={true}
value={this.selectedHomeserver} origValue={this.props.entry.homeserver}
onChange={({ id }) => this.setState({ homeserver: id })}
creatable={true} isValidNewOption={this.isValidHomeserver}/>
<PrefInput rowName="Access token" type="text" name="access_token"
value={this.state.access_token} origValue={this.props.entry.access_token}
placeholder="MDAxYWxvY2F0aW9uIG1hdHJpeC5sb2NhbAowMDEzaWRlbnRpZmllc"

View File

@ -31,6 +31,8 @@ class Dashboard extends Component {
instances: {},
clients: {},
plugins: {},
homeserversByName: {},
homeserversByURL: {},
sidebarOpen: false,
modalOpen: false,
logFocus: null,
@ -50,8 +52,8 @@ class Dashboard extends Component {
}
async componentWillMount() {
const [instanceList, clientList, pluginList] = await Promise.all([
api.getInstances(), api.getClients(), api.getPlugins(),
const [instanceList, clientList, pluginList, homeservers] = await Promise.all([
api.getInstances(), api.getClients(), api.getPlugins(), api.getClientAuthServers(),
api.updateDebugOpenFileEnabled()])
const instances = {}
if (api.getFeatures().instance) {
@ -71,7 +73,13 @@ class Dashboard extends Component {
plugins[plugin.id] = plugin
}
}
this.setState({ instances, clients, plugins })
const homeserversByName = homeservers
const homeserversByURL = {}
for (const [key, value] of Object.entries(homeservers)) {
homeserversByURL[value] = key
}
console.log(homeserversByName, homeserversByURL)
this.setState({ instances, clients, plugins, homeserversByName, homeserversByURL })
await this.enableLogs()
}