diff --git a/pkg/interface/chat/src/js/components/lib/invite-search.js b/pkg/interface/chat/src/js/components/lib/invite-search.js
index f1340d08e3..c5f59b201b 100644
--- a/pkg/interface/chat/src/js/components/lib/invite-search.js
+++ b/pkg/interface/chat/src/js/components/lib/invite-search.js
@@ -1,4 +1,6 @@
import React, { Component } from "react";
+import _ from 'lodash';
+import Mousetrap from 'mousetrap';
import urbitOb from "urbit-ob";
import { Sigil } from "../lib/icons/sigil";
@@ -14,13 +16,17 @@ export class InviteSearch extends Component {
groups: [],
ships: []
},
+ selected: null,
inviteError: false
};
this.search = this.search.bind(this);
+
+ this.textarea = React.createRef();
}
componentDidMount() {
this.peerUpdate();
+ this.bindShortcuts();
}
componentDidUpdate(prevProps) {
@@ -126,12 +132,91 @@ export class InviteSearch extends Component {
}
}
+ let { selected } = this.state;
+ let groupIdx = groupMatches.findIndex(([path]) => path === selected);
+ let shipIdx = shipMatches.findIndex(ship => ship === selected);
+ let staleSelection = groupIdx < 0 && shipIdx < 0;
+ if(!selected || staleSelection) {
+ const newSelection = _.get(groupMatches, '[0][0]') || shipMatches[0];
+ this.setState({ selected: newSelection })
+ }
+
this.setState({
searchResults: { groups: groupMatches, ships: shipMatches }
});
}
}
+ bindShortcuts() {
+ let mousetrap = Mousetrap(this.textarea.current);
+ mousetrap.bind(['down', 'tab'], e => {
+ e.preventDefault();
+ e.stopPropagation();
+ this.nextSelection();
+ });
+
+ mousetrap.bind(['up', 'shift+tab'], e => {
+ e.preventDefault();
+ e.stopPropagation();
+ this.nextSelection('backward');
+ });
+
+ mousetrap.bind('enter', e => {
+ e.preventDefault();
+ e.stopPropagation();
+ const { selected } = this.state;
+ if(selected.startsWith('/')) {
+ this.addGroup(selected)
+ } else {
+ this.addShip(selected);
+ }
+ this.setState({ selected: null })
+ })
+ }
+ nextSelection(backward = false) {
+ let { selected, searchResults } = this.state;
+ const { ships, groups } = searchResults;
+ if(!selected) {
+ return;
+ }
+ let groupIdx = groups.findIndex(([path]) => path === selected);
+ let shipIdx = ships.findIndex(ship => ship === selected);
+ if(groupIdx >= 0) {
+ backward ? groupIdx-- : groupIdx++;
+ let selected = _.get(groups,[groupIdx], '[0]');
+ if(groupIdx === groups.length) {
+ selected = ships.length === 0
+ ? groups[0][0]
+ : ships[0];
+
+ }
+ if(groupIdx < 0) {
+ selected = ships.length === 0
+ ? groups[groups.length - 1][0]
+ : ships[ships.length - 1];
+ }
+ this.setState({ selected });
+ return;
+ }
+ if(shipIdx >= 0) {
+ backward ? shipIdx-- : shipIdx++;
+ let selected = ships[shipIdx];
+ if(shipIdx === ships.length) {
+ selected = groups.length === 0
+ ? ships[0]
+ : groups[0][0];
+ }
+
+ if(shipIdx < 0) {
+ selected = groups.length === 0
+ ? ships[ships.length - 1]
+ : groups[groups.length - 1][0];
+ }
+
+ this.setState({ selected });
+ }
+
+ }
deleteGroup() {
let { ships } = this.props.invites;
this.setState({
@@ -251,7 +336,8 @@ export class InviteSearch extends Component {
className={
"list white-d f8 pv2 ph3 pointer" +
" hover-bg-gray4 hover-bg-gray1-d " +
- ((group[1]) ? "inter" : "mono")
+ ((group[1]) ? "inter" : "mono") +
+ ( group[0] === state.selected ? ' bg-gray1-d bg-gray4' : '')
}
onClick={() => this.addGroup(group[0])}>
{(group[1]) ? group[1] : group[0]}
@@ -270,7 +356,8 @@ export class InviteSearch extends Component {
key={ship}
className={
"list mono white-d f8 pv1 ph3 pointer" +
- " hover-bg-gray4 hover-bg-gray1-d relative"
+ " hover-bg-gray4 hover-bg-gray1-d relative" +
+ ( ship === state.selected ? ' bg-gray1-d bg-gray4' : '')
}
onClick={e => this.addShip(ship)}>