mirror of
https://github.com/urbit/shrub.git
synced 2024-12-01 06:35:32 +03:00
Merge branch 'mp/leap-tweaks' (#3420)
* origin/mp/leap-tweaks: leap: better compensate for long titles leap: show group's name, not the host ship leap: add "other" actions, prepopulate results leap: move leap to its own subfolder Signed-off-by: Matilde Park <matilde.park@sunshinegardens.org>
This commit is contained in:
commit
4c8c2eaa6d
@ -1,10 +1,12 @@
|
||||
import defaultApps from './default-apps';
|
||||
import { cite } from '~/logic/lib/util';
|
||||
|
||||
const indexes = new Map([
|
||||
['commands', []],
|
||||
['subscriptions', []],
|
||||
['groups', []],
|
||||
['apps', []]
|
||||
['apps', []],
|
||||
['other', []]
|
||||
]);
|
||||
|
||||
// result schematic
|
||||
@ -41,8 +43,6 @@ const commandIndex = function () {
|
||||
}
|
||||
});
|
||||
|
||||
commands.push(result('Profile', '/~profile', 'profile', null));
|
||||
|
||||
return commands;
|
||||
};
|
||||
|
||||
@ -54,6 +54,9 @@ const appIndex = function (apps) {
|
||||
.filter((e) => {
|
||||
return apps[e]?.type?.basic;
|
||||
})
|
||||
.sort((a,b) => {
|
||||
return a.localeCompare(b);
|
||||
})
|
||||
.map((e) => {
|
||||
const obj = result(
|
||||
apps[e].type.basic.title,
|
||||
@ -70,6 +73,14 @@ const appIndex = function (apps) {
|
||||
return applications;
|
||||
};
|
||||
|
||||
const otherIndex = function() {
|
||||
const other = [];
|
||||
other.push(result('Profile and Settings', '/~profile/identity', 'profile', null));
|
||||
other.push(result('Log Out', '/~/logout', 'logout', null));
|
||||
|
||||
return other;
|
||||
};
|
||||
|
||||
export default function index(associations, apps) {
|
||||
// all metadata from all apps is indexed
|
||||
// into subscriptions and groups
|
||||
@ -99,7 +110,7 @@ export default function index(associations, apps) {
|
||||
title,
|
||||
`/~${app}${each['app-path']}`,
|
||||
app.charAt(0).toUpperCase() + app.slice(1),
|
||||
shipStart.slice(0, shipStart.indexOf('/'))
|
||||
cite(shipStart.slice(0, shipStart.indexOf('/')))
|
||||
);
|
||||
groups.push(obj);
|
||||
} else {
|
||||
@ -107,7 +118,7 @@ export default function index(associations, apps) {
|
||||
title,
|
||||
`/~${each['app-name']}/join${each['app-path']}`,
|
||||
app.charAt(0).toUpperCase() + app.slice(1),
|
||||
shipStart.slice(0, shipStart.indexOf('/'))
|
||||
(associations?.contacts?.[each['group-path']]?.metadata?.title || null)
|
||||
);
|
||||
subscriptions.push(obj);
|
||||
}
|
||||
@ -118,6 +129,7 @@ export default function index(associations, apps) {
|
||||
indexes.set('subscriptions', subscriptions);
|
||||
indexes.set('groups', groups);
|
||||
indexes.set('apps', appIndex(apps));
|
||||
indexes.set('other', otherIndex());
|
||||
|
||||
return indexes;
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { hot } from 'react-hot-loader/root';
|
||||
import 'react-hot-loader';
|
||||
import * as React from 'react';
|
||||
import { BrowserRouter as Router, Route, withRouter, Switch } from 'react-router-dom';
|
||||
import { BrowserRouter as Router, withRouter } from 'react-router-dom';
|
||||
import styled, { ThemeProvider, createGlobalStyle } from 'styled-components';
|
||||
import { sigil as sigiljs, stringRenderer } from 'urbit-sigil-js';
|
||||
import Helmet from 'react-helmet';
|
||||
@ -16,8 +16,7 @@ import dark from './themes/old-dark';
|
||||
|
||||
import { Content } from './components/Content';
|
||||
import StatusBar from './components/StatusBar';
|
||||
import Omnibox from './components/Omnibox';
|
||||
import ErrorComponent from './components/Error';
|
||||
import Omnibox from './components/leap/Omnibox';
|
||||
|
||||
import GlobalStore from '~/logic/store/store';
|
||||
import GlobalSubscription from '~/logic/subscription/global';
|
||||
@ -36,7 +35,7 @@ const Root = styled.div`
|
||||
background-size: cover;
|
||||
` : p.background?.type === 'color' ? `
|
||||
background-color: ${p.background.color};
|
||||
` : ``
|
||||
` : ''
|
||||
}
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
@ -135,7 +134,8 @@ class App extends React.Component {
|
||||
ship={this.ship}
|
||||
api={this.api}
|
||||
subscription={this.subscription}
|
||||
{...state} />
|
||||
{...state}
|
||||
/>
|
||||
</Router>
|
||||
</Root>
|
||||
</ThemeProvider>
|
||||
@ -143,6 +143,5 @@ class App extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default process.env.NODE_ENV === 'production' ? App : hot(App);
|
||||
|
||||
|
@ -1,96 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Row, Icon, Text } from '@tlon/indigo-react';
|
||||
import defaultApps from '~/logic/lib/default-apps';
|
||||
|
||||
export class OmniboxResult extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isSelected: false,
|
||||
hovered: false
|
||||
};
|
||||
this.setHover = this.setHover.bind(this);
|
||||
this.result = React.createRef();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { props, state } = this;
|
||||
if (prevProps &&
|
||||
!state.hovered &&
|
||||
prevProps.selected !== props.selected &&
|
||||
props.selected === props.link
|
||||
) {
|
||||
this.result.current.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
}
|
||||
|
||||
setHover(boolean) {
|
||||
this.setState({ hovered: boolean });
|
||||
}
|
||||
render() {
|
||||
const { icon, text, subtext, link, navigate, selected, dark } = this.props;
|
||||
|
||||
let invertGraphic = {};
|
||||
|
||||
if (icon.toLowerCase() !== 'dojo') {
|
||||
invertGraphic = (!dark && this.state.hovered) ||
|
||||
selected === link ||
|
||||
(dark && !(this.state.hovered || selected === link))
|
||||
? { filter: 'invert(1)', paddingTop: 2 }
|
||||
: { filter: 'invert(0)', paddingTop: 2 };
|
||||
} else {
|
||||
invertGraphic =
|
||||
(!dark && this.state.hovered) ||
|
||||
selected === link ||
|
||||
(dark && !(this.state.hovered || selected === link))
|
||||
? { filter: 'invert(0)', paddingTop: 2 }
|
||||
: { filter: 'invert(1)', paddingTop: 2 };
|
||||
}
|
||||
|
||||
let graphic = <div />;
|
||||
if (defaultApps.includes(icon.toLowerCase()) || icon.toLowerCase() === 'links') {
|
||||
graphic = <img className="mr2 v-mid" height="12" width="12" src={`/~landscape/img/${icon.toLowerCase()}.png`} style={invertGraphic} />;
|
||||
} else {
|
||||
graphic = <Icon verticalAlign="middle" mr={2} size="12px" />;
|
||||
}
|
||||
return (
|
||||
<Row
|
||||
py='2'
|
||||
px='2'
|
||||
display='flex'
|
||||
flexDirection='row'
|
||||
style={{ cursor: 'pointer' }}
|
||||
onMouseEnter={() => this.setHover(true)}
|
||||
onMouseLeave={() => this.setHover(false)}
|
||||
backgroundColor={
|
||||
this.state.hovered || selected === link ? 'blue' : 'white'
|
||||
}
|
||||
onClick={navigate}
|
||||
width="100%"
|
||||
ref={this.result}
|
||||
>
|
||||
{this.state.hovered || selected === link ? (
|
||||
<>
|
||||
{graphic}
|
||||
<Text color='white' mr='1' style={{ 'flex-shrink': 0 }}>
|
||||
{text}
|
||||
</Text>
|
||||
<Text pr='2' color='white' width='100%' textAlign='right'>
|
||||
{subtext}
|
||||
</Text>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{graphic}
|
||||
<Text mr='1' style={{ 'flex-shrink': 0 }}>{text}</Text>
|
||||
<Text pr='2' gray width='100%' textAlign='right'>
|
||||
{subtext}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default OmniboxResult;
|
@ -6,8 +6,6 @@ import Mousetrap from 'mousetrap';
|
||||
import OmniboxInput from './OmniboxInput';
|
||||
import OmniboxResult from './OmniboxResult';
|
||||
|
||||
import { cite } from '~/logic/lib/util';
|
||||
|
||||
export class Omnibox extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -26,11 +24,15 @@ export class Omnibox extends Component {
|
||||
this.renderResults = this.renderResults.bind(this);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevProps !== this.props) {
|
||||
this.setState({ index: index(this.props.associations, this.props.apps.tiles) });
|
||||
}
|
||||
|
||||
if (prevProps && (prevProps.apps !== this.props.apps) && (this.state.query === '')) {
|
||||
this.setState({ results: this.initialResults() });
|
||||
}
|
||||
|
||||
if (prevProps && this.props.show && prevProps.show !== this.props.show) {
|
||||
Mousetrap.bind('escape', () => this.props.api.local.setOmnibox());
|
||||
document.addEventListener('mousedown', this.handleClickOutside);
|
||||
@ -48,7 +50,7 @@ export class Omnibox extends Component {
|
||||
}
|
||||
|
||||
getSearchedCategories() {
|
||||
return ['apps', 'commands', 'groups', 'subscriptions'];
|
||||
return ['apps', 'commands', 'groups', 'subscriptions', 'other'];
|
||||
}
|
||||
|
||||
control(evt) {
|
||||
@ -91,7 +93,18 @@ export class Omnibox extends Component {
|
||||
}
|
||||
|
||||
initialResults() {
|
||||
return new Map(this.getSearchedCategories().map(category => [category, []]));
|
||||
return new Map(this.getSearchedCategories().map((category) => {
|
||||
if (!this.state) {
|
||||
return [category, []];
|
||||
}
|
||||
if (category === 'apps') {
|
||||
return ['apps', this.state.index.get('apps')];
|
||||
}
|
||||
if (category === 'other') {
|
||||
return ['other', this.state.index.get('other')];
|
||||
}
|
||||
return [category, []];
|
||||
}));
|
||||
}
|
||||
|
||||
navigate(link) {
|
||||
@ -202,23 +215,26 @@ export class Omnibox extends Component {
|
||||
{this.getSearchedCategories()
|
||||
.map(category => Object({ category, categoryResults: state.results.get(category) }))
|
||||
.filter(category => category.categoryResults.length > 0)
|
||||
.map(({ category, categoryResults }, i) => (
|
||||
<Box key={i} width='max(50vw, 300px)' maxWidth='600px'>
|
||||
.map(({ category, categoryResults }, i) => {
|
||||
const categoryTitle = (category === 'other')
|
||||
? null : <Text gray ml={2}>{category.charAt(0).toUpperCase() + category.slice(1)}</Text>;
|
||||
return (<Box key={i} width='max(50vw, 300px)' maxWidth='600px'>
|
||||
<Rule borderTopWidth="0.5px" color="washedGray" />
|
||||
<Text gray ml={2}>{category.charAt(0).toUpperCase() + category.slice(1)}</Text>
|
||||
{categoryTitle}
|
||||
{categoryResults.map((result, i2) => (
|
||||
<OmniboxResult
|
||||
key={i2}
|
||||
icon={result.app}
|
||||
text={result.title}
|
||||
subtext={cite(result.host)}
|
||||
subtext={result.host}
|
||||
link={result.link}
|
||||
navigate={() => this.navigate(result.link)}
|
||||
selected={this.state.selected}
|
||||
dark={props.dark} />
|
||||
))}
|
||||
</Box>
|
||||
))
|
||||
);
|
||||
})
|
||||
}
|
||||
</Box>;
|
||||
}
|
147
pkg/interface/src/views/components/leap/OmniboxResult.js
Normal file
147
pkg/interface/src/views/components/leap/OmniboxResult.js
Normal file
@ -0,0 +1,147 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Row, Icon, Text } from '@tlon/indigo-react';
|
||||
import defaultApps from '~/logic/lib/default-apps';
|
||||
import Sigil from '~/logic/lib/sigil';
|
||||
|
||||
export class OmniboxResult extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isSelected: false,
|
||||
hovered: false
|
||||
};
|
||||
this.setHover = this.setHover.bind(this);
|
||||
this.result = React.createRef();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { props, state } = this;
|
||||
if (prevProps &&
|
||||
!state.hovered &&
|
||||
prevProps.selected !== props.selected &&
|
||||
props.selected === props.link
|
||||
) {
|
||||
this.result.current.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
}
|
||||
|
||||
getIcon(icon, dark, selected, link) {
|
||||
// graphicStyle is only necessary for pngs
|
||||
//
|
||||
//TODO can be removed after indigo-react 1.2
|
||||
//which includes icons for apps
|
||||
let graphicStyle = {};
|
||||
|
||||
if (icon.toLowerCase() !== 'dojo') {
|
||||
graphicStyle = (!dark && this.state.hovered) ||
|
||||
selected === link ||
|
||||
(dark && !(this.state.hovered || selected === link))
|
||||
? { filter: 'invert(1)' }
|
||||
: { filter: 'invert(0)' };
|
||||
} else {
|
||||
graphicStyle =
|
||||
(!dark && this.state.hovered) ||
|
||||
selected === link ||
|
||||
(dark && !(this.state.hovered || selected === link))
|
||||
? { filter: 'invert(0)' }
|
||||
: { filter: 'invert(1)' };
|
||||
}
|
||||
|
||||
const iconFill = this.state.hovered || selected === link ? 'white' : 'black';
|
||||
const sigilFill = this.state.hovered || selected === link ? '#3a8ff7' : '#ffffff';
|
||||
|
||||
let graphic = <div />;
|
||||
if (defaultApps.includes(icon.toLowerCase()) || icon.toLowerCase() === 'links') {
|
||||
graphic =
|
||||
<img className="mr2 v-mid dib" height="16"
|
||||
width="16" src={`/~landscape/img/${icon.toLowerCase()}.png`}
|
||||
style={graphicStyle}
|
||||
/>;
|
||||
} else if (icon === 'logout') {
|
||||
graphic = <Icon display="inline-block" verticalAlign="middle" icon='ArrowWest' mr='2' size='16px' fill={iconFill} />;
|
||||
} else if (icon === 'profile') {
|
||||
graphic = <Sigil color={sigilFill} classes='dib v-mid mr2' ship={window.ship} size={16} />;
|
||||
} else {
|
||||
graphic = <Icon verticalAlign="middle" mr='2' size="16px" fill={iconFill} />;
|
||||
}
|
||||
|
||||
return graphic;
|
||||
}
|
||||
|
||||
setHover(boolean) {
|
||||
this.setState({ hovered: boolean });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { icon, text, subtext, link, navigate, selected, dark } = this.props;
|
||||
|
||||
const graphic = this.getIcon(icon, dark, selected, link);
|
||||
|
||||
return (
|
||||
<Row
|
||||
py='2'
|
||||
px='2'
|
||||
display='flex'
|
||||
flexDirection='row'
|
||||
style={{ cursor: 'pointer' }}
|
||||
onMouseEnter={() => this.setHover(true)}
|
||||
onMouseLeave={() => this.setHover(false)}
|
||||
backgroundColor={
|
||||
this.state.hovered || selected === link ? 'blue' : 'white'
|
||||
}
|
||||
onClick={navigate}
|
||||
width="100%"
|
||||
ref={this.result}
|
||||
>
|
||||
{this.state.hovered || selected === link ? (
|
||||
<>
|
||||
{graphic}
|
||||
<Text
|
||||
display="inline-block"
|
||||
verticalAlign="middle"
|
||||
color='white'
|
||||
maxWidth="60%"
|
||||
style={{ 'flex-shrink': 0 }}
|
||||
mr='1'>
|
||||
{text}
|
||||
</Text>
|
||||
<Text pr='2'
|
||||
display="inline-block"
|
||||
verticalAlign="middle"
|
||||
color='white'
|
||||
width='100%'
|
||||
textAlign='right'
|
||||
>
|
||||
{subtext}
|
||||
</Text>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{graphic}
|
||||
<Text
|
||||
mr='1'
|
||||
display="inline-block"
|
||||
verticalAlign="middle"
|
||||
maxWidth="60%"
|
||||
style={{ 'flex-shrink': 0 }}
|
||||
>
|
||||
{text}
|
||||
</Text>
|
||||
<Text
|
||||
pr='2'
|
||||
display="inline-block"
|
||||
verticalAlign="middle"
|
||||
gray
|
||||
width='100%'
|
||||
textAlign='right'
|
||||
>
|
||||
{subtext}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default OmniboxResult;
|
Loading…
Reference in New Issue
Block a user