From 657ffa8792308e49d33db3e3b03c1d6e89eb04f1 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Wed, 29 Jul 2020 16:20:31 -0400 Subject: [PATCH 01/13] interface: create 'default apps' library We don't want to maintain several indexes of the Landscape suite when we want to special case something. Now we don't. --- pkg/interface/src/apps/launch/components/tiles/basic.js | 5 ++++- pkg/interface/src/lib/default-apps.js | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 pkg/interface/src/lib/default-apps.js diff --git a/pkg/interface/src/apps/launch/components/tiles/basic.js b/pkg/interface/src/apps/launch/components/tiles/basic.js index 33f9244ed6..f6b3790ad2 100644 --- a/pkg/interface/src/apps/launch/components/tiles/basic.js +++ b/pkg/interface/src/apps/launch/components/tiles/basic.js @@ -1,6 +1,7 @@ import React from 'react'; import classnames from 'classnames'; import { Link } from 'react-router-dom'; +import defaultApps from '../../../../lib/default-apps'; import Tile from './tile'; @@ -29,7 +30,9 @@ export default class BasicTile extends React.PureComponent { ); - const routeList = ['/~chat', '/~publish', '/~link', '/~groups', '/~dojo']; + const routeList = defaultApps.map((e) => { + return `/~${e}`; + }); const tile = ( routeList.indexOf(props.linkedUrl) !== -1 ) ? ( diff --git a/pkg/interface/src/lib/default-apps.js b/pkg/interface/src/lib/default-apps.js new file mode 100644 index 0000000000..4fcbe37330 --- /dev/null +++ b/pkg/interface/src/lib/default-apps.js @@ -0,0 +1,3 @@ +const defaultApps = ['chat', 'dojo', 'groups', 'link', 'publish']; + +export default defaultApps; From 5ae12d492d8831ada4dda6ab6b43992b4af71eb7 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Thu, 30 Jul 2020 13:05:16 -0400 Subject: [PATCH 02/13] interface: remove groupfilter --- pkg/interface/src/App.js | 1 - pkg/interface/src/api/local.ts | 11 - pkg/interface/src/apps/chat/app.tsx | 11 +- .../src/apps/chat/components/sidebar.js | 11 - pkg/interface/src/apps/groups/app.tsx | 9 - .../groups/components/lib/group-sidebar.js | 10 - .../src/apps/groups/components/skeleton.js | 1 - pkg/interface/src/apps/links/app.js | 19 +- .../links/components/lib/channel-sidebar.js | 14 +- .../src/apps/links/components/skeleton.js | 1 - pkg/interface/src/apps/publish/app.js | 14 - .../apps/publish/components/lib/sidebar.js | 16 +- .../src/apps/publish/components/skeleton.js | 1 - pkg/interface/src/components/GroupFilter.js | 249 ------------------ pkg/interface/src/components/StatusBar.js | 7 +- pkg/interface/src/reducers/local.ts | 9 +- pkg/interface/src/store/store.ts | 1 - pkg/interface/src/store/type.ts | 2 - pkg/interface/src/types/local-update.ts | 8 - 19 files changed, 10 insertions(+), 385 deletions(-) delete mode 100644 pkg/interface/src/components/GroupFilter.js diff --git a/pkg/interface/src/App.js b/pkg/interface/src/App.js index 94dc9d0764..7135a3cd3b 100644 --- a/pkg/interface/src/App.js +++ b/pkg/interface/src/App.js @@ -84,7 +84,6 @@ class App extends React.Component { const channel = window.channel; const associations = this.state.associations ? this.state.associations : { contacts: {} }; - const selectedGroups = this.state.selectedGroups ? this.state.selectedGroups : []; const { state } = this; const theme = state.dark ? dark : light; diff --git a/pkg/interface/src/api/local.ts b/pkg/interface/src/api/local.ts index 8610284c23..4170fcbd6b 100644 --- a/pkg/interface/src/api/local.ts +++ b/pkg/interface/src/api/local.ts @@ -1,6 +1,5 @@ import BaseApi from "./base"; import { StoreState } from "../store/type"; -import { SelectedGroup } from "../types/local-update"; export default class LocalApi extends BaseApi { getBaseHash() { @@ -9,16 +8,6 @@ export default class LocalApi extends BaseApi { }); } - setSelected(selected: SelectedGroup[]) { - this.store.handleEvent({ - data: { - local: { - selected - } - } - }) - } - sidebarToggle() { this.store.handleEvent({ data: { diff --git a/pkg/interface/src/apps/chat/app.tsx b/pkg/interface/src/apps/chat/app.tsx index 8f9adc8432..fc8c4e0b3a 100644 --- a/pkg/interface/src/apps/chat/app.tsx +++ b/pkg/interface/src/apps/chat/app.tsx @@ -53,7 +53,6 @@ export default class ChatApp extends React.Component { const unreads = {}; let totalUnreads = 0; - const selectedGroups = props.selectedGroups ? props.selectedGroups : []; const associations = props.associations ? props.associations : { chat: {}, contacts: {} }; @@ -74,14 +73,7 @@ export default class ChatApp extends React.Component { unreads[stat] = Boolean(unread); if ( unread && - stat in associations.chat && - (selectedGroups.length === 0 || - selectedGroups - .map((e) => { - return e[0]; - }) - .includes(associations.chat?.[stat]?.['group-path']) || - props.groups[associations.chat?.[stat]?.['group-path']]?.hidden) + stat in associations.chat ) { totalUnreads += unread; } @@ -111,7 +103,6 @@ export default class ChatApp extends React.Component { inbox={inbox} messagePreviews={messagePreviews} associations={associations} - selectedGroups={selectedGroups} contacts={contacts} invites={invites['/chat'] || {}} unreads={unreads} diff --git a/pkg/interface/src/apps/chat/components/sidebar.js b/pkg/interface/src/apps/chat/components/sidebar.js index d7d63a9849..95abbca9ac 100644 --- a/pkg/interface/src/apps/chat/components/sidebar.js +++ b/pkg/interface/src/apps/chat/components/sidebar.js @@ -13,8 +13,6 @@ export class Sidebar extends Component { render() { const { props } = this; - const selectedGroups = props.selectedGroups ? props.selectedGroups : []; - const contactAssoc = (props.associations && 'contacts' in props.associations) ? alphabetiseAssociations(props.associations.contacts) : {}; @@ -61,15 +59,6 @@ export class Sidebar extends Component { const groupedItems = Object.keys(contactAssoc) .filter(each => (groupedChannels[each] || []).length !== 0) - .filter((each) => { - if (selectedGroups.length === 0) { - return true; - } - const selectedPaths = selectedGroups.map((e) => { - return e[0]; - }); - return selectedPaths.includes(each); - }) .map((each, i) => { const channels = groupedChannels[each] || []; return( diff --git a/pkg/interface/src/apps/groups/app.tsx b/pkg/interface/src/apps/groups/app.tsx index a88da3eb39..cefd0d6ec5 100644 --- a/pkg/interface/src/apps/groups/app.tsx +++ b/pkg/interface/src/apps/groups/app.tsx @@ -48,7 +48,6 @@ export default class GroupsApp extends Component { const invites = (Boolean(props.invites) && '/contacts' in props.invites) ? props.invites['/contacts'] : {}; - const selectedGroups = props.selectedGroups ? props.selectedGroups : []; const s3 = props.s3 ? props.s3 : {}; const groups = props.groups || {}; const associations = props.associations || {}; @@ -62,7 +61,6 @@ export default class GroupsApp extends Component { return ( { return ( { return ( { return ( { return ( { { { { - const selectedGroups = props.selectedGroups ? props.selectedGroups : []; - if (selectedGroups.length === 0) { - return true; - } - const selectedPaths = selectedGroups.map(((e) => { - return e[0]; - })); - return (selectedPaths.includes(path)); - }) .sort((a, b) => { let aName = a.substr(1); let bName = b.substr(1); diff --git a/pkg/interface/src/apps/groups/components/skeleton.js b/pkg/interface/src/apps/groups/components/skeleton.js index e1bc4a5473..a3118703ea 100644 --- a/pkg/interface/src/apps/groups/components/skeleton.js +++ b/pkg/interface/src/apps/groups/components/skeleton.js @@ -17,7 +17,6 @@ export class Skeleton extends Component { invites={props.invites} activeDrawer={props.activeDrawer} selected={props.selected} - selectedGroups={props.selectedGroups} history={props.history} api={props.api} associations={props.associations} diff --git a/pkg/interface/src/apps/links/app.js b/pkg/interface/src/apps/links/app.js index 940d42af80..0f1522e721 100644 --- a/pkg/interface/src/apps/links/app.js +++ b/pkg/interface/src/apps/links/app.js @@ -41,7 +41,7 @@ export class LinksApp extends Component { render() { const { props } = this; - const contacts = props.contacts ? props.contacts : {}; + const contacts = props.contacts ? props.contacts : {}; const groups = props.groups ? props.groups : {}; @@ -51,18 +51,9 @@ export class LinksApp extends Component { const seen = props.linksSeen ? props.linksSeen : {}; - const selectedGroups = props.selectedGroups ? props.selectedGroups : []; - - const selGroupPaths = selectedGroups.map(g => g[0]); const totalUnseen = _.reduce( links, - (acc, collection, path) => { - if(selGroupPaths.length > 0 - && !selGroupPaths.includes(associations.link?.[path]?.['group-path'])) { - return acc; - } - return acc + collection.unseenCount; - }, + (acc, collection) => acc + collection.unseenCount, 0 ); @@ -91,7 +82,6 @@ export class LinksApp extends Component { groups={groups} rightPanelHide={true} sidebarShown={sidebarShown} - selectedGroups={selectedGroups} links={links} listening={listening} api={api} @@ -109,7 +99,6 @@ export class LinksApp extends Component { invites={invites} groups={groups} sidebarShown={sidebarShown} - selectedGroups={selectedGroups} links={links} listening={listening} api={api} @@ -157,7 +146,6 @@ export class LinksApp extends Component { groups={groups} selected={resourcePath} sidebarShown={sidebarShown} - selectedGroups={selectedGroups} links={links} listening={listening} api={api} @@ -198,7 +186,6 @@ export class LinksApp extends Component { groups={groups} selected={resourcePath} sidebarShown={sidebarShown} - selectedGroups={selectedGroups} popout={popout} links={links} listening={listening} @@ -253,7 +240,6 @@ export class LinksApp extends Component { groups={groups} selected={resourcePath} sidebarShown={sidebarShown} - selectedGroups={selectedGroups} sidebarHideMobile={true} popout={popout} links={links} @@ -311,7 +297,6 @@ export class LinksApp extends Component { groups={groups} selected={resourcePath} sidebarShown={sidebarShown} - selectedGroups={selectedGroups} sidebarHideMobile={true} popout={popout} links={links} diff --git a/pkg/interface/src/apps/links/components/lib/channel-sidebar.js b/pkg/interface/src/apps/links/components/lib/channel-sidebar.js index a17ac35850..e33960d3bc 100644 --- a/pkg/interface/src/apps/links/components/lib/channel-sidebar.js +++ b/pkg/interface/src/apps/links/components/lib/channel-sidebar.js @@ -51,24 +51,14 @@ export class ChannelsSidebar extends Component { } }); - const selectedGroups = props.selectedGroups ? props.selectedGroups : []; let i = -1; const groupedItems = Object.keys(associations) - .filter((each) => { - if (selectedGroups.length === 0) { - return true; - }; - const selectedPaths = selectedGroups.map((e) => { - return e[0]; - }); - return selectedPaths.includes(each); - }) .map((each) => { const channels = groupedChannels[each]; if (!channels || channels.length === 0) return; i++; - if ((selectedGroups.length === 0) && groupedChannels['/~/'] && groupedChannels['/~/'].length !== 0) { + if (groupedChannels['/~/'] && groupedChannels['/~/'].length !== 0) { i++; } @@ -84,7 +74,7 @@ export class ChannelsSidebar extends Component { /> ); }); - if ((selectedGroups.length === 0) && groupedChannels['/~/'] && groupedChannels['/~/'].length !== 0) { + if (groupedChannels['/~/'] && groupedChannels['/~/'].length !== 0) { groupedItems.unshift( { - return ((selectedGroups.map((e) => { - return e[0]; - }).includes(each?.['writers-group-path'])) || - (selectedGroups.length === 0)); - }) .map('num-unread') .reduce((acc, count) => acc + count, 0) .value(); @@ -80,7 +73,6 @@ export default class PublishApp extends React.Component { invites={invites} notebooks={notebooks} associations={associations} - selectedGroups={selectedGroups} contacts={contacts} api={api} > @@ -111,7 +103,6 @@ export default class PublishApp extends React.Component { invites={invites} notebooks={notebooks} associations={associations} - selectedGroups={selectedGroups} contacts={contacts} api={api} > @@ -142,7 +133,6 @@ export default class PublishApp extends React.Component { invites={invites} notebooks={notebooks} associations={associations} - selectedGroups={selectedGroups} contacts={contacts} api={api} > @@ -188,7 +178,6 @@ export default class PublishApp extends React.Component { invites={invites} notebooks={notebooks} associations={associations} - selectedGroups={selectedGroups} contacts={contacts} path={path} api={api} @@ -215,7 +204,6 @@ export default class PublishApp extends React.Component { notebooks={notebooks} associations={associations} contacts={contacts} - selectedGroups={selectedGroups} path={path} api={api} > @@ -265,7 +253,6 @@ export default class PublishApp extends React.Component { sidebarShown={sidebarShown} invites={invites} notebooks={notebooks} - selectedGroups={selectedGroups} associations={associations} contacts={contacts} path={path} @@ -293,7 +280,6 @@ export default class PublishApp extends React.Component { invites={invites} notebooks={notebooks} associations={associations} - selectedGroups={selectedGroups} contacts={contacts} path={path} api={api} diff --git a/pkg/interface/src/apps/publish/components/lib/sidebar.js b/pkg/interface/src/apps/publish/components/lib/sidebar.js index b801d464d9..5a9d3809e9 100644 --- a/pkg/interface/src/apps/publish/components/lib/sidebar.js +++ b/pkg/interface/src/apps/publish/components/lib/sidebar.js @@ -64,23 +64,12 @@ export class Sidebar extends Component { } }); - const selectedGroups = props.selectedGroups ? props.selectedGroups: []; const groupedItems = Object.keys(associations) - .filter((each) => { - if (selectedGroups.length === 0) { - return true; - } - const selectedPaths = selectedGroups.map((e) => { - return e[0]; - }); - return (selectedPaths.includes(each)); - }) .map((each, i) => { const books = groupedNotebooks[each] || []; if (books.length === 0) return; - if ((selectedGroups.length === 0) && - groupedNotebooks['/~/'] && + if (groupedNotebooks['/~/'] && groupedNotebooks['/~/'].length !== 0) { i = i + 1; } @@ -95,8 +84,7 @@ export class Sidebar extends Component { /> ); }); - if ((selectedGroups.length === 0) && - groupedNotebooks['/~/'] && + if (groupedNotebooks['/~/'] && groupedNotebooks['/~/'].length !== 0) { groupedItems.unshift(
{ - this.props.api.local.setSelected(this.state.selected); - })); - } - } - - componentWillUnmount() { - document.removeEventListener('mousedown', this.handleClickOutside); - } - - componentDidUpdate(prevProps) { - if (prevProps !== this.props) { - this.groupIndex(); - } - } - - handleClickOutside(evt) { - if ((this.dropdown && !this.dropdown.contains(evt.target)) - && (this.toggleButton && !this.toggleButton.contains(evt.target))) { - this.setState({ open: false }); - } - } - - toggleOpen() { - this.setState({ open: !this.state.open }); - } - - groupIndex() { - const { props } = this; - let index = []; - const associations = - (props.associations && 'contacts' in props.associations) ? - props.associations.contacts : {}; - index = Object.keys(associations).map((each) => { - const eachGroup = []; - eachGroup.push(each); - let name = each; - if (associations[each].metadata) { - name = (associations[each].metadata.title !== '') - ? associations[each].metadata.title : name; - } - eachGroup.push(name); - return eachGroup; - }); - this.setState({ groups: index }); - } - - search(evt) { - this.setState({ searchTerm: evt.target.value }); - const term = evt.target.value.toLowerCase(); - - if (term.length < 3) { - return this.setState({ results: [] }); - } - - let groupMatches = []; - groupMatches = this.state.groups.filter((e) => { - return (e[0].includes(term) || e[1].includes(term)); - }); - this.setState({ results: groupMatches }); - } - - addGroup(group) { - const selected = this.state.selected; - if (!(group in selected)) { - selected.push(group); - } - this.setState({ - searchTerm: '', - selected: selected, - results: [] - }, (() => { - this.props.api.local.setSelected(this.state.selected); - localStorage.setItem('urbit-selectedGroups', JSON.stringify(this.state.selected)); - })); - } - - deleteGroup(group) { - let selected = this.state.selected; - selected = selected.filter((e) => { - return e !== group; - }); - this.setState({ selected: selected }, (() => { - this.props.api.local.setSelected(this.state.selected); - localStorage.setItem('urbit-selectedGroups', JSON.stringify(this.state.selected)); - })); - } - - render() { - const { props, state } = this; - - let currentGroup = 'All Groups'; - - if (state.selected.length > 0) { - const titles = state.selected.map((each) => { - return each[1]; - }); - currentGroup = titles.join(' + '); - } - - const buttonOpened = (state.open) - ? 'bg-gray5 bg-gray1-d white-d' : 'hover-bg-gray5 hover-bg-gray1-d white-d'; - - const dropdownClass = (state.open) - ? 'absolute db z-2 bg-white bg-gray0-d white-d ba b--gray3 b--gray1-d' - : 'dn'; - - const inviteCount = (props.invites && Object.keys(props.invites).length > 0) - ? - : ; - - let selectedGroups =
; - let searchResults =
; - - if (state.results.length > 0) { - const groupResults = state.results.map(((group) => { - return( -
  • this.addGroup(group)} - > - {(group[1]) ? group[1] : group[0]} -
  • - ); - })); - searchResults = ( -
    -

    Groups

    - {groupResults} -
    - ); - } - - if (state.selected.length > 0) { - const allSelected = this.state.selected.map((each) => { - const name = each[1]; - return( - - {name} - this.deleteGroup(each)} - > - x - - - ); - }); - selectedGroups = ( -
    - {allSelected} -
    - ); - } - - return ( -
    -
    this.toggleOpen()} - ref={el => this.toggleButton = el} - > -

    {currentGroup}

    -
    -
    { - this.dropdown = el; -}} - > -

    Group Select and Filter

    - this.setState({ open: false })} - > - Manage all Groups - {inviteCount} - -

    Filter Groups

    -
    - - {searchResults} - {selectedGroups} -
    -
    -
    - ); - } -} diff --git a/pkg/interface/src/components/StatusBar.js b/pkg/interface/src/components/StatusBar.js index 42793f5c32..be168d68e7 100644 --- a/pkg/interface/src/components/StatusBar.js +++ b/pkg/interface/src/components/StatusBar.js @@ -1,7 +1,5 @@ import React from 'react'; import { useLocation, Link } from 'react-router-dom'; - -import GroupFilter from './GroupFilter'; import { Sigil } from '../lib/sigil'; const getLocationName = (basePath) => { @@ -54,7 +52,6 @@ const StatusBar = (props) => { color={'#000000'} /> - / { location.pathname === '/' @@ -68,8 +65,8 @@ const StatusBar = (props) => { }

    {locationName}

    - { connection === 'disconnected' && - (Reconnect ↻ ) diff --git a/pkg/interface/src/reducers/local.ts b/pkg/interface/src/reducers/local.ts index ec11a23669..46b71e91c0 100644 --- a/pkg/interface/src/reducers/local.ts +++ b/pkg/interface/src/reducers/local.ts @@ -3,14 +3,13 @@ import { StoreState } from '../store/type'; import { Cage } from '../types/cage'; import { LocalUpdate } from '../types/local-update'; -type LocalState = Pick; +type LocalState = Pick; export default class LocalReducer { reduce(json: Cage, state: S) { const data = json['local']; if (data) { this.sidebarToggle(data, state); - this.setSelected(data, state); this.setDark(data, state); this.baseHash(data, state); } @@ -27,12 +26,6 @@ export default class LocalReducer { } } - setSelected(obj: LocalUpdate, state: S) { - if ('selected' in obj) { - state.selectedGroups = obj.selected; - } - } - setDark(obj: LocalUpdate, state: S) { if('setDark' in obj) { state.dark = obj.setDark; diff --git a/pkg/interface/src/store/store.ts b/pkg/interface/src/store/store.ts index 76cdad3c2d..6d43b94308 100644 --- a/pkg/interface/src/store/store.ts +++ b/pkg/interface/src/store/store.ts @@ -72,7 +72,6 @@ export default class GlobalStore extends BaseStore { linkComments: {}, notebooks: {}, contacts: {}, - selectedGroups: [], dark: false, inbox: {}, chatSynced: null, diff --git a/pkg/interface/src/store/type.ts b/pkg/interface/src/store/type.ts index 875f80318a..018ab3c878 100644 --- a/pkg/interface/src/store/type.ts +++ b/pkg/interface/src/store/type.ts @@ -2,7 +2,6 @@ import { Inbox, Envelope } from '../types/chat-update'; import { ChatHookUpdate } from '../types/chat-hook-update'; import { Path } from '../types/noun'; import { Invites } from '../types/invite-update'; -import { SelectedGroup } from '../types/local-update'; import { Associations } from '../types/metadata-update'; import { Rolodex } from '../types/contact-update'; import { Notebooks } from '../types/publish-update'; @@ -16,7 +15,6 @@ import { ConnectionStatus } from '../types/connection'; export interface StoreState { // local state sidebarShown: boolean; - selectedGroups: SelectedGroup[]; dark: boolean; connection: ConnectionStatus; baseHash: string | null; diff --git a/pkg/interface/src/types/local-update.ts b/pkg/interface/src/types/local-update.ts index 326d845400..28d06dd645 100644 --- a/pkg/interface/src/types/local-update.ts +++ b/pkg/interface/src/types/local-update.ts @@ -1,8 +1,6 @@ -import { Path } from './noun'; export type LocalUpdate = LocalUpdateSidebarToggle -| LocalUpdateSelectedGroups | LocalUpdateSetDark | LocalUpdateBaseHash; @@ -10,10 +8,6 @@ interface LocalUpdateSidebarToggle { sidebarToggle: boolean; } -interface LocalUpdateSelectedGroups { - selected: SelectedGroup[]; -} - interface LocalUpdateSetDark { setDark: boolean; } @@ -21,5 +15,3 @@ interface LocalUpdateSetDark { interface LocalUpdateBaseHash { baseHash: string; } - -export type SelectedGroup = [Path, string]; From 1be4aed640547dc06810f90cb187e50943a290fb Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Thu, 30 Jul 2020 19:14:03 -0400 Subject: [PATCH 03/13] interface: add omnibox, new statusbar --- pkg/arvo/app/landscape/img/groups.png | Bin 0 -> 508 bytes pkg/arvo/app/landscape/img/icon-home.png | Bin 255 -> 564 bytes pkg/interface/src/App.js | 153 ++++++----- pkg/interface/src/api/local.ts | 10 + .../apps/chat/components/lib/chat-input.js | 8 +- pkg/interface/src/components/Omnibox.js | 241 ++++++++++++++++++ pkg/interface/src/components/OmniboxResult.js | 67 +++++ pkg/interface/src/components/StatusBar.js | 144 +++++++---- pkg/interface/src/lib/omnibox.js | 106 ++++++++ pkg/interface/src/lib/util.js | 3 + pkg/interface/src/reducers/local.ts | 9 +- pkg/interface/src/store/store.ts | 1 + pkg/interface/src/store/type.ts | 1 + pkg/interface/src/types/local-update.ts | 6 +- 14 files changed, 629 insertions(+), 120 deletions(-) create mode 100644 pkg/arvo/app/landscape/img/groups.png create mode 100644 pkg/interface/src/components/Omnibox.js create mode 100644 pkg/interface/src/components/OmniboxResult.js create mode 100644 pkg/interface/src/lib/omnibox.js diff --git a/pkg/arvo/app/landscape/img/groups.png b/pkg/arvo/app/landscape/img/groups.png new file mode 100644 index 0000000000000000000000000000000000000000..0c9687b46d8025d93ebb236bde2a3830ec0dcc55 GIT binary patch literal 508 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9EO-XP4l)OOlRpde#$ zkh>GZx^prwfgF}}M_)$E)e-c@NauYpW978G?-`=>Jdssn)^}%Jm3q1!oBpZ2W zot?xxN7Tx56XVSVcMq`M43ibts5c9-n_nWosz@Y9C`Zr11p=n{zuNU8XxFcedAqD) zlbUNx*<;iH_}mJ8UYPT;CN#|{ZR)QByB_{jD*Al2be{PF)~ zp>_`|{bxL1y<6!oTZ~R+@xpbsPLZFk9{p36tG)Nk{%Sk#s85k`6V|&&?z5bke=z;X z5Bt4~Z1>&$%iTJ42P6NI-O-=i&;OXLI)CXA-X9LTcB|e$_f0M&h!_f`}v65mH!$`e7`YU9hv*Q_&4WO&l~2;FMthU>P@g` pc*DLy-azn&`(Fkn2)H#rzh;R5>l&j)4#3D_@O1TaS?83{1OTb#-#P#Q literal 0 HcmV?d00001 diff --git a/pkg/arvo/app/landscape/img/icon-home.png b/pkg/arvo/app/landscape/img/icon-home.png index 04b1e7b87022b502fffb58c9cd29c050c57272ba..ba79e2a70f4d079b527029c727096590bd39fdb4 100644 GIT binary patch delta 545 zcmV++0^a@q0ki}*iBL{Q4GJ0x0000DNk~Le0000m0000m2nGNE09OL}hX4Qo32;bR za{vG=O8@{YO97=lmZ_08AAbSKNkl2F|>E+Jv^l5x;MH|ekI4u9giSvB>>9bkxF z;J-6B5)Cj$pHW-9=Q~}19*G5*rymF|-tQjmazD2UC$!%Ov^cGL6 z2=&opI*Sw`wz3{+?0;Kv-8j9ZlV}}Z^hPmWRd1L8%k%`!j?+qJ;5;E^XJ|KW6RKUPi1gj@bydA>d8`r_MPm9=XI}gg>xCvs1~^1* z8{nsa7y{U%Mbp>-rm+D`V*{AR1~82cV4AjVr;lg_dQ=+BZy?&?a7i^N4zJ`x+D!e; jstS-P>>@Tz<^%izXA5WK1dm?)00000NkvXXu0mjf@`LRQ delta 234 zcmdnO@}JSHGr-TCmrII^fq{Y7)59eQNIQTq2OE%Fm05TXNO2Z;L>4nJh^c}wqi2xH zhlytL^$b%yT^vIy7~fut<~n2`;(9T-@E+gZs0;I(uDAzzyoVM||IW?L+;KpLcwC{$r!EiIja>%dyAD zyDntbZ0GQny8X{cR+m5X#iU0p9)(OxEEt3Cn5tXvI<8&4U-_F%qlmggm$1Uosr(>| b7_Jnt@ASyLV14Y)QV`eE)z4*}Q$iB}seo6v diff --git a/pkg/interface/src/App.js b/pkg/interface/src/App.js index 7135a3cd3b..a7ba161c3d 100644 --- a/pkg/interface/src/App.js +++ b/pkg/interface/src/App.js @@ -4,6 +4,8 @@ import * as React from 'react'; import { BrowserRouter as Router, Route, withRouter, Switch } from 'react-router-dom'; import styled, { ThemeProvider, createGlobalStyle } from 'styled-components'; +import Mousetrap from 'mousetrap'; + import './css/indigo-static.css'; import './css/fonts.css'; import light from './themes/light'; @@ -17,6 +19,7 @@ import LinksApp from './apps/links/app'; import PublishApp from './apps/publish/app'; import StatusBar from './components/StatusBar'; +import Omnibox from './components/Omnibox'; import ErrorComponent from './components/Error'; import GlobalStore from './store/store'; @@ -70,6 +73,10 @@ class App extends React.Component { this.api.local.setDark(this.themeWatcher.matches); this.themeWatcher.addListener(this.updateTheme); this.api.local.getBaseHash(); + Mousetrap.bind(['command+l', 'ctrl+l'], (e) => { + e.preventDefault(); + this.api.local.setOmnibox(); + }); } componentWillUnmount() { @@ -91,81 +98,99 @@ class App extends React.Component { - + - - ( - + ( + + )} /> - )} - /> - ( - ( + + )} /> - )} - /> - ( - ( + + )} /> - )} - /> - ( - ( + + )} /> - )} - /> - ( - ( + + )} /> - )} - /> - ( - ( + + )} /> - )} - /> ( + render={props => ( )} - /> + /> diff --git a/pkg/interface/src/api/local.ts b/pkg/interface/src/api/local.ts index 4170fcbd6b..77ff9b2b3b 100644 --- a/pkg/interface/src/api/local.ts +++ b/pkg/interface/src/api/local.ts @@ -28,4 +28,14 @@ export default class LocalApi extends BaseApi { }); } + setOmnibox() { + this.store.handleEvent({ + data: { + local: { + omniboxShown: true + }, + }, + }); + } + } diff --git a/pkg/interface/src/apps/chat/components/lib/chat-input.js b/pkg/interface/src/apps/chat/components/lib/chat-input.js index 685a7d24ca..006cbb8dea 100644 --- a/pkg/interface/src/apps/chat/components/lib/chat-input.js +++ b/pkg/interface/src/apps/chat/components/lib/chat-input.js @@ -339,7 +339,13 @@ export class ChatInput extends Component { 'Shift-3': cm => cm.getValue().length === 0 ? this.toggleCode() - : CodeMirror.Pass + : CodeMirror.Pass, + 'Cmd-L': () => { + this.props.api.local.setOmnibox(); + }, + 'Ctrl-L': () => { + this.props.api.local.setOmnibox(); + } } }; diff --git a/pkg/interface/src/components/Omnibox.js b/pkg/interface/src/components/Omnibox.js new file mode 100644 index 0000000000..53a904e0a8 --- /dev/null +++ b/pkg/interface/src/components/Omnibox.js @@ -0,0 +1,241 @@ +import React, { Component } from 'react'; +import { withRouter } from 'react-router-dom'; +import { Box, Row, Rule, Text } from '@tlon/indigo-react'; +import index from '../lib/omnibox'; +import Mousetrap from 'mousetrap'; +import OmniboxResult from './OmniboxResult'; + +import { cite } from '../lib/util'; + +export class Omnibox extends Component { + constructor(props) { + super(props); + this.state = { + index: new Map([]), + query: '', + results: this.initialResults(), + selected: '' + }; + this.handleClickOutside = this.handleClickOutside.bind(this); + this.search = this.search.bind(this); + this.navigate = this.navigate.bind(this); + this.control - this.control.bind(this); + this.setPreviousSelected = this.setPreviousSelected.bind(this); + this.setNextSelected = this.setNextSelected.bind(this); + } + + componentDidUpdate(prevProps) { + if (prevProps !== this.props) { + this.setState({ index: index(this.props.associations, this.props.apps.tiles) }); + } + + if (prevProps && this.props.show && prevProps.show !== this.props.show) { + Mousetrap.bind('escape', () => this.props.api.local.setOmnibox()); + document.addEventListener('mousedown', this.handleClickOutside); + this.input.focus(); + } + } + + componentWillUpdate(prevProps) { + if (!this.props.show && prevProps.show !== this.props.show) { + Mousetrap.unbind('escape'); + document.removeEventListener('mousedown', this.handleClickOutside); + } + } + + control(evt) { + if (evt.key === 'Escape') { + if (this.state.query.length > 0) { + this.setState({ query: '', results: this.initialResults() }); + } else if (this.props.show) { + this.props.api.local.setOmnibox(); + } + }; + + if (evt.key === 'ArrowDown') { + this.setNextSelected(); + } + + if (evt.key === 'ArrowUp') { + this.setPreviousSelected(); + } + + if (evt.key === 'Enter') { + if (this.state.selected !== '') { + this.navigate(this.state.selected); + } else { + this.navigate(Array.from(this.state.results.values()).flat()[0].link); + } + } + } + + handleClickOutside(evt) { + if (this.props.show && !this.omniBox.contains(evt.target)) { + this.props.api.local.setOmnibox(); + } + } + + initialResults() { + return new Map([ + ['commands', []], + ['subscriptions', []], + ['groups', []], + ['apps', []] + ]); + } + + navigate(link) { + const { props } = this; + this.setState({ results: this.initialResults(), query: '' }, () => { + props.api.local.setOmnibox(); + props.history.push(link); + }); + } + + search(event) { + const { state } = this; + const query = event.target.value; + const results = this.initialResults(); + + this.setState({ query: query }); + + // wipe results if backspacing + if (query.length === 0) { + this.setState({ results: results }); + return; + } + + // don't search for single characters + if (query.length === 1) { + return; + } + + ['commands', 'subscriptions', 'groups', 'apps'].map((category) => { + const categoryIndex = state.index.get(category); + results.set(category, + categoryIndex.filter((result) => { + return ( + result.title.includes(query) || + result.link.includes(query) || + result.app.includes(query) || + (result.host !== null ? result.host.includes(query) : false)); + }) + ); + }); + + this.setState({ results: results }); + } + + setPreviousSelected() { + const current = this.state.selected; + const flattenedResults = Array.from(this.state.results.values()).flat(); + if (current !== '') { + const currentIndex = flattenedResults.indexOf( + ...flattenedResults.filter((e) => { + return e.link === current; + }) + ); + if (currentIndex > 0) { + const nextLink = flattenedResults[currentIndex - 1].link; + this.setState({ selected: nextLink }); + } + } else { + const nextLink = flattenedResults[0].link; + this.setState({ selected: nextLink }); + } + } + + setNextSelected() { + const current = this.state.selected; + const flattenedResults = Array.from(this.state.results.values()).flat(); + if (current !== '') { + const currentIndex = flattenedResults.indexOf( + ...flattenedResults.filter((e) => { + return e.link === current; + }) + ); + if (currentIndex < flattenedResults.length - 1) { + const nextLink = flattenedResults[currentIndex + 1].link; + this.setState({ selected: nextLink }); + } + } else { + const nextLink = flattenedResults[0].link; + this.setState({ selected: nextLink }); + } + } + + render() { + const { props, state } = this; + const categoryResult = []; + + const renderResults = + {categoryResult} + ; + + ['commands', 'subscriptions', 'groups', 'apps'].map((category, i) => { + const categoryResults = state.results.get(category); + if (categoryResults.length > 0) { + const each = categoryResults.map((result, i) => { + return this.navigate(result.link)} + selected={this.state.selected} + dark={props.dark} + />; + }); + categoryResult.push( + + {category.charAt(0).toUpperCase() + category.slice(1)} + {each} + ); + } + }); + + return ( + <> + + + { + this.omniBox = el; + }} + > + { + this.input = el; + }} + className="ba b--transparent w-100 br2 white-d bg-gray0-d inter f9 pa2" + style={{ maxWidth: 'calc(600px - 1.15rem)', boxSizing: 'border-box' }} + placeholder="Search..." + onKeyUp={e => this.control(e) } + onChange={this.search} + value={state.query} + /> + {renderResults} + + + + + ); + } +} + +export default withRouter(Omnibox); diff --git a/pkg/interface/src/components/OmniboxResult.js b/pkg/interface/src/components/OmniboxResult.js new file mode 100644 index 0000000000..050757f6f7 --- /dev/null +++ b/pkg/interface/src/components/OmniboxResult.js @@ -0,0 +1,67 @@ +import React, { Component } from 'react'; +import { Row, Icon, Text } from '@tlon/indigo-react'; +import defaultApps from '../lib/default-apps'; + +export class OmniboxResult extends Component { + constructor(props) { + super(props); + this.state = { + isSelected: false, + hovered: false + }; + this.setHover = this.setHover.bind(this); + } + + setHover(boolean) { + this.setState({ hovered: boolean }); + } + render() { + const { icon, text, subtext, link, navigate, selected, dark } = this.props; + + const invertGraphic = ((!dark && this.state.hovered || selected === link) || (dark && !(this.state.hovered || selected === link))) + ? { filter: 'invert(1)' } + : { filter: 'invert(0)' }; + + let graphic =
    ; + if (defaultApps.includes(icon.toLowerCase())) { + graphic = ; + } else { + graphic = ; + } + return ( + this.setHover(true)} + onMouseLeave={() => this.setHover(false)} + backgroundColor={ + this.state.hovered || selected === link ? "blue" : "white" + } + onClick={navigate}> + {this.state.hovered || selected === link ? ( + <> + {graphic} + + {text} + + + {subtext} + + + ) : ( + <> + {graphic} + {text} + + {subtext} + + + )} + + ); + } +} + +export default OmniboxResult; diff --git a/pkg/interface/src/components/StatusBar.js b/pkg/interface/src/components/StatusBar.js index be168d68e7..6d31ed0fab 100644 --- a/pkg/interface/src/components/StatusBar.js +++ b/pkg/interface/src/components/StatusBar.js @@ -1,40 +1,35 @@ import React from 'react'; import { useLocation, Link } from 'react-router-dom'; -import { Sigil } from '../lib/sigil'; - -const getLocationName = (basePath) => { - if (basePath === '~chat') - return 'Chat'; - else if (basePath === '~dojo') - return 'Dojo'; - else if (basePath === '~groups') - return 'Groups'; - else if (basePath === '~link') - return 'Links'; - else if (basePath === '~publish') - return 'Publish'; - else - return 'Unknown'; -}; +import { Box, Text, Icon } from '@tlon/indigo-react'; const StatusBar = (props) => { const location = useLocation(); - const basePath = location.pathname.split('/')[1]; - const locationName = location.pathname === '/' - ? 'Home' - : getLocationName(basePath); + const atHome = Boolean(location.pathname === '/'); - const display = (!window.location.href.includes('popout/') && - (locationName !== 'Unknown')) + const display = (!window.location.href.includes('popout/')) ? 'db' : 'dn'; const invites = (props.invites && props.invites['/contacts']) ? props.invites['/contacts'] : {}; + + const Notification = (Object.keys(invites).length > 0) + ? + : null; + const connection = props.connection || 'connected'; const reconnect = props.subscription.restart.bind(props.subscription); + const metaKey = (window.navigator.platform.includes('Mac')) ? '⌘' : 'Ctrl+'; + + const mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test( + navigator.userAgent + ); + return (
    { } style={{ height: 45 }} > -
    - - + {atHome ? null : ( + props.history.push('/')} + > + + + )} + props.api.local.setOmnibox()} + > + + ↩ + + + Leap + + + {metaKey}L + + + {connection === 'disconnected' && ( + + Reconnect ↻ + + )} + {connection === 'reconnecting' && ( + + Reconnecting + + )} +
    +
    + props.history.push('/~groups')} + > + - - / - { - location.pathname === '/' - ? null - : - ⟵ - - } -

    {locationName}

    - { connection === 'disconnected' && - (Reconnect ↻ ) - } - { connection === 'reconnecting' && - (Reconnecting ) - } + {Notification} + Groups +
    ); diff --git a/pkg/interface/src/lib/omnibox.js b/pkg/interface/src/lib/omnibox.js new file mode 100644 index 0000000000..4b9698a8aa --- /dev/null +++ b/pkg/interface/src/lib/omnibox.js @@ -0,0 +1,106 @@ +import defaultApps from './default-apps'; + +export default function index(associations, apps) { + const index = new Map([ + ['commands', []], + ['subscriptions', []], + ['groups', []], + ['apps', []] + ]); + + // result schematic + const result = function(title, link, app, host) { + return { + 'title': title, + 'link': link, + 'app': app, + 'host': host + }; + }; + + // commands are special cased for default suite + const commands = []; + defaultApps.filter((e) => { + return (e !== 'dojo'); + }).map((e) => { + let title = e; + if (e === 'link') { + title = 'Links'; + } + + title = title.charAt(0).toUpperCase() + title.slice(1); + + let obj = result(`${title}: Create`, `/~${e}/new`, title, null); + commands.push(obj); + + if (title === 'Groups') { + obj = result(`${title}: Join Group`, `/~${e}/join`, title, null); + commands.push(obj); + } + }); + index.set('commands', commands); + + // all metadata from all apps is indexed + // into subscriptions and groups + const subscriptions = []; + const groups = []; + Object.keys(associations).filter((e) => { + // skip apps with no metadata + return Object.keys(associations[e]).length > 0; + }).map((e) => { + // iterate through each app's metadata object + Object.keys(associations[e]).map((association) => { + const each = associations[e][association]; + let title = each['app-path']; + if (each.metadata.title !== '') { + title = each.metadata.title; + } + + let app = each['app-name']; + if (each['app-name'] === 'contacts') { + app = 'groups'; + }; + + const shipStart = each['app-path'].substr(each['app-path'].indexOf('~')); + + if (app === 'groups') { + const obj = result( + title, + `/~${app}${each['app-path']}`, + app.charAt(0).toUpperCase() + app.slice(1), + shipStart.slice(0, shipStart.indexOf('/')) + ); + groups.push(obj); + } else { + let endpoint = ''; + if (app === 'chat') { + endpoint = '/room'; + } else if (app === 'publish') { + endpoint = '/notebook'; + } + const obj = result( + title, + `/~${each['app-name']}${endpoint}${each['app-path']}`, + app.charAt(0).toUpperCase() + app.slice(1), + shipStart.slice(0, shipStart.indexOf('/')) + ); + subscriptions.push(obj); + } + }); + }); + index.set('subscriptions', subscriptions); + index.set('groups', groups); + + // all apps are indexed from launch data + // indexed into 'apps' + const applications = []; + Object.keys(apps).filter((e) => { + return (apps[e]?.type?.basic); + }).map((e) => { + const obj = result(apps[e].type.basic.title, apps[e].type.basic.linkedUrl, apps[e].type.basic.title, null); + applications.push(obj); + }); + index.set('apps', applications); + + return index; +}; diff --git a/pkg/interface/src/lib/util.js b/pkg/interface/src/lib/util.js index 0b20a8f43d..bfc42709aa 100644 --- a/pkg/interface/src/lib/util.js +++ b/pkg/interface/src/lib/util.js @@ -119,6 +119,9 @@ export function writeText(str) { // trim patps to match dojo, chat-cli export function cite(ship) { let patp = ship, shortened = ''; + if (patp === null || patp === '') { + return null; + } if (patp.startsWith('~')) { patp = patp.substr(1); } diff --git a/pkg/interface/src/reducers/local.ts b/pkg/interface/src/reducers/local.ts index 46b71e91c0..f5a526653c 100644 --- a/pkg/interface/src/reducers/local.ts +++ b/pkg/interface/src/reducers/local.ts @@ -3,7 +3,7 @@ import { StoreState } from '../store/type'; import { Cage } from '../types/cage'; import { LocalUpdate } from '../types/local-update'; -type LocalState = Pick; +type LocalState = Pick; export default class LocalReducer { reduce(json: Cage, state: S) { @@ -12,6 +12,7 @@ export default class LocalReducer { this.sidebarToggle(data, state); this.setDark(data, state); this.baseHash(data, state); + this.omniboxShown(data, state); } } baseHash(obj: LocalUpdate, state: S) { @@ -20,6 +21,12 @@ export default class LocalReducer { } } + omniboxShown(obj: LocalUpdate, state: S) { + if ('omniboxShown' in obj) { + state.omniboxShown = !state.omniboxShown; + } + } + sidebarToggle(obj: LocalUpdate, state: S) { if ('sidebarToggle' in obj) { state.sidebarShown = !state.sidebarShown; diff --git a/pkg/interface/src/store/store.ts b/pkg/interface/src/store/store.ts index 6d43b94308..bc840f0856 100644 --- a/pkg/interface/src/store/store.ts +++ b/pkg/interface/src/store/store.ts @@ -41,6 +41,7 @@ export default class GlobalStore extends BaseStore { chatInitialized: false, connection: 'connected', sidebarShown: true, + omniboxShown: false, baseHash: null, invites: {}, associations: { diff --git a/pkg/interface/src/store/type.ts b/pkg/interface/src/store/type.ts index 018ab3c878..6ff247105e 100644 --- a/pkg/interface/src/store/type.ts +++ b/pkg/interface/src/store/type.ts @@ -15,6 +15,7 @@ import { ConnectionStatus } from '../types/connection'; export interface StoreState { // local state sidebarShown: boolean; + omniboxShown: boolean; dark: boolean; connection: ConnectionStatus; baseHash: string | null; diff --git a/pkg/interface/src/types/local-update.ts b/pkg/interface/src/types/local-update.ts index 28d06dd645..0ea170da0a 100644 --- a/pkg/interface/src/types/local-update.ts +++ b/pkg/interface/src/types/local-update.ts @@ -1,7 +1,7 @@ - export type LocalUpdate = LocalUpdateSidebarToggle | LocalUpdateSetDark +| LocalUpdateSetOmniboxShown | LocalUpdateBaseHash; interface LocalUpdateSidebarToggle { @@ -15,3 +15,7 @@ interface LocalUpdateSetDark { interface LocalUpdateBaseHash { baseHash: string; } + +interface LocalUpdateSetOmniboxShown { + omniboxShown: boolean; +} From 1c567ad4d956ecc020b99b6a43bf0721c16a1095 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Thu, 30 Jul 2020 19:17:17 -0400 Subject: [PATCH 04/13] omniboxresult: add catch for dojo's graphic --- pkg/interface/src/components/OmniboxResult.js | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/pkg/interface/src/components/OmniboxResult.js b/pkg/interface/src/components/OmniboxResult.js index 050757f6f7..eef4f76e6e 100644 --- a/pkg/interface/src/components/OmniboxResult.js +++ b/pkg/interface/src/components/OmniboxResult.js @@ -18,13 +18,26 @@ export class OmniboxResult extends Component { render() { const { icon, text, subtext, link, navigate, selected, dark } = this.props; - const invertGraphic = ((!dark && this.state.hovered || selected === link) || (dark && !(this.state.hovered || selected === link))) - ? { filter: 'invert(1)' } - : { filter: 'invert(0)' }; + let invertGraphic = {}; + + if (icon.toLowerCase() !== 'dojo') { + invertGraphic = (!dark && this.state.hovered) || + selected === link || + (dark && !(this.state.hovered || selected === link)) + ? { filter: "invert(1)" } + : { filter: "invert(0)" }; + } else { + invertGraphic = + (!dark && this.state.hovered) || + selected === link || + (dark && !(this.state.hovered || selected === link)) + ? { filter: "invert(0)" } + : { filter: "invert(1)" }; + } let graphic =
    ; if (defaultApps.includes(icon.toLowerCase())) { - graphic = ; + graphic = ; } else { graphic = ; } From 7d1c53ea4461c13699e1f4fd53fafd551c3a1603 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Thu, 30 Jul 2020 20:56:51 -0400 Subject: [PATCH 05/13] omnibox: address @liam-fitzgerald feedback --- pkg/interface/src/components/Omnibox.js | 29 +++++++++++++++++-------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/pkg/interface/src/components/Omnibox.js b/pkg/interface/src/components/Omnibox.js index 53a904e0a8..511fd77f1f 100644 --- a/pkg/interface/src/components/Omnibox.js +++ b/pkg/interface/src/components/Omnibox.js @@ -52,12 +52,16 @@ export class Omnibox extends Component { } }; - if (evt.key === 'ArrowDown') { - this.setNextSelected(); + if ( + evt.key === 'ArrowUp' || + (evt.shiftKey && evt.key === 'Tab')) { + evt.preventDefault(); + return this.setPreviousSelected(); } - if (evt.key === 'ArrowUp') { - this.setPreviousSelected(); + if (evt.key === 'ArrowDown' || evt.key === 'Tab') { + evt.preventDefault(); + this.setNextSelected(); } if (evt.key === 'Enter') { @@ -101,7 +105,7 @@ export class Omnibox extends Component { // wipe results if backspacing if (query.length === 0) { - this.setState({ results: results }); + this.setState({ results: results, selected: '' }); return; } @@ -129,6 +133,7 @@ export class Omnibox extends Component { setPreviousSelected() { const current = this.state.selected; const flattenedResults = Array.from(this.state.results.values()).flat(); + const totalLength = flattenedResults.length; if (current !== '') { const currentIndex = flattenedResults.indexOf( ...flattenedResults.filter((e) => { @@ -138,9 +143,12 @@ export class Omnibox extends Component { if (currentIndex > 0) { const nextLink = flattenedResults[currentIndex - 1].link; this.setState({ selected: nextLink }); + } else { + const nextLink = flattenedResults[totalLength - 1].link; + this.setState({ selected: nextLink }); } } else { - const nextLink = flattenedResults[0].link; + const nextLink = flattenedResults[totalLength - 1].link; this.setState({ selected: nextLink }); } } @@ -157,6 +165,9 @@ export class Omnibox extends Component { if (currentIndex < flattenedResults.length - 1) { const nextLink = flattenedResults[currentIndex + 1].link; this.setState({ selected: nextLink }); + } else { + const nextLink = flattenedResults[0].link; + this.setState({ selected: nextLink }); } } else { const nextLink = flattenedResults[0].link; @@ -187,7 +198,7 @@ export class Omnibox extends Component { dark={props.dark} />; }); - categoryResult.push( + categoryResult.push( {category.charAt(0).toUpperCase() + category.slice(1)} {each} @@ -210,7 +221,7 @@ export class Omnibox extends Component { this.control(e) } + onKeyDown={e => this.control(e) } onChange={this.search} value={state.query} /> From 8230b02ad36cc2110f8851990bd73b6893e140fb Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Thu, 30 Jul 2020 21:02:10 -0400 Subject: [PATCH 06/13] omnibox: fix autofocus on safari --- pkg/interface/src/components/Omnibox.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/interface/src/components/Omnibox.js b/pkg/interface/src/components/Omnibox.js index 511fd77f1f..6358836de6 100644 --- a/pkg/interface/src/components/Omnibox.js +++ b/pkg/interface/src/components/Omnibox.js @@ -32,6 +32,8 @@ export class Omnibox extends Component { if (prevProps && this.props.show && prevProps.show !== this.props.show) { Mousetrap.bind('escape', () => this.props.api.local.setOmnibox()); document.addEventListener('mousedown', this.handleClickOutside); + const touchstart = new Event('touchstart'); + this.input.dispatchEvent(touchstart); this.input.focus(); } } From 8e1274caf3c3cbff8b5143ad0014fa58c0a20c87 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Thu, 30 Jul 2020 21:04:45 -0400 Subject: [PATCH 07/13] omnibox: add overflow: scroll on vertical --- pkg/interface/src/components/Omnibox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/interface/src/components/Omnibox.js b/pkg/interface/src/components/Omnibox.js index 6358836de6..93e9d4f9de 100644 --- a/pkg/interface/src/components/Omnibox.js +++ b/pkg/interface/src/components/Omnibox.js @@ -181,7 +181,7 @@ export class Omnibox extends Component { const { props, state } = this; const categoryResult = []; - const renderResults = + const renderResults = {categoryResult} ; From 83456246ba7845804451556221b634c117914607 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Wed, 5 Aug 2020 19:36:07 -0400 Subject: [PATCH 08/13] statusBar: reconnect button into component --- .../src/components/ReconnectButton.js | 46 +++++++++++++++++++ pkg/interface/src/components/StatusBar.js | 25 +++------- 2 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 pkg/interface/src/components/ReconnectButton.js diff --git a/pkg/interface/src/components/ReconnectButton.js b/pkg/interface/src/components/ReconnectButton.js new file mode 100644 index 0000000000..2ffb026f90 --- /dev/null +++ b/pkg/interface/src/components/ReconnectButton.js @@ -0,0 +1,46 @@ +import React from 'react'; +import { Box, Text } from '@tlon/indigo-react'; + +const ReconnectButton = ({ connection, subscription }) => { + const connectedStatus = connection || 'connected'; + const reconnect = subscription.restart.bind(subscription); + if (connectedStatus === 'disconnected') { + return ( + <> + + Reconnect ↻ + + + ); + } else if (connectedStatus === 'reconnecting') { + return ( + <> + + Reconnecting + + + ); + } else { + return null; + } + }; + +export default ReconnectButton; diff --git a/pkg/interface/src/components/StatusBar.js b/pkg/interface/src/components/StatusBar.js index 630e803a4c..cbc01d4827 100644 --- a/pkg/interface/src/components/StatusBar.js +++ b/pkg/interface/src/components/StatusBar.js @@ -1,6 +1,7 @@ import React from 'react'; import { useLocation } from 'react-router-dom'; import { Box, Text, Icon } from '@tlon/indigo-react'; +import ReconnectButton from './ReconnectButton'; const StatusBar = (props) => { const location = useLocation(); @@ -20,10 +21,6 @@ const StatusBar = (props) => { /> : null; - const connection = props.connection || 'connected'; - - const reconnect = props.subscription.restart.bind(props.subscription); - const metaKey = (window.navigator.platform.includes('Mac')) ? '⌘' : 'Ctrl+'; const mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test( @@ -37,7 +34,7 @@ const StatusBar = (props) => { } style={{ height: 45 }} > -
    +
    {atHome ? null : ( { color='washedGray' display='inline-block' style={{ cursor: 'pointer' }} + lineHeight="min" py={1} px={2} onClick={() => props.api.local.setOmnibox()} @@ -78,19 +76,10 @@ const StatusBar = (props) => { {metaKey}L - {connection === 'disconnected' && ( - - Reconnect ↻ - - )} - {connection === 'reconnecting' && ( - - Reconnecting - - )} +
    Date: Wed, 5 Aug 2020 21:32:19 -0400 Subject: [PATCH 09/13] omnibox: use current assets --- pkg/arvo/app/landscape/img/groups.png | Bin 508 -> 693 bytes pkg/arvo/app/landscape/img/icon-home.png | Bin 564 -> 582 bytes pkg/interface/src/components/StatusBar.js | 21 +++++++++------------ 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/pkg/arvo/app/landscape/img/groups.png b/pkg/arvo/app/landscape/img/groups.png index 0c9687b46d8025d93ebb236bde2a3830ec0dcc55..fa14f36da54e3e6911f8d7b5507271e4512df29f 100644 GIT binary patch delta 619 zcmV-x0+jvy1GNQ^Reu6XNklv$P%Jc7!@Fmzo%=*|H8dx~^FASYX9Q^m3`h+-3cw=a1X5mN7`Yp#4{v=vk%EE_R zzvZ-qKl;nqEJJSn*4}a1dpd;hV=ShAsz2~#aLO#6aUnw(vGp76J!9gS2H>b&6Z{ET zzC-B$>JQiqz<)d9b>M(%3HI4xK>aURpx__C=%jEz&lR(N87#yIL`<I1av1EfCS;q|3Hpddl~P;}?uAFBU~ z5!VOw9b}77FegJNjI2Ijg=&_sf1LCB`I!183gMBQfPM;pL8FpaujS5Y%V}VL_|bHL`0NN>bmGsy85whx;kBAgM2jyU1$7E z+EwRqZ0USeWyPxuSZ$FIhk+4XTKVZ}Ie6BV6 z0Y}eR+j;(hbWQdHI>eplbNdO^N!T~1(DS@4A6f#9U@AWV><6$PFcJ7i{Qx2&A|fIp bBHEvCe54QstTLiN00000NkvXXu0mjft2WVI diff --git a/pkg/arvo/app/landscape/img/icon-home.png b/pkg/arvo/app/landscape/img/icon-home.png index ba79e2a70f4d079b527029c727096590bd39fdb4..9eb1d0289332f49a08dd27c8b49db114fdab48c5 100644 GIT binary patch delta 541 zcmV+&0^Y|w5HZvY#>26Pif$Os*QCU?&DNCd>jjuoWu zPJf6+taq{VtN@Cl6vX-vN!7*0M+IRt_%D8l211~U7bC(Bw~HTQhZkhi#R1b|e|?O9 zKlq%NpfBW%X_0LgpFUdR`WY`Ee}VyBT4>&Lv;g-9rk&YS+UE@7>XOSKY zMy|2c>xzYIG$?CWOv(Ej@NjFU#5}kiS{7Y~7SFl6TrA=mE2MLGxyb4oe{&g})Hu*{ zeVR2CKj083z#ATOiO4BQ(0tz^92EehDG1OIf52pi!fLT z&|#(dZaa2n+u0&?9b_a`8ijR50EIiyo- zFvUtFjz5mYo3Iue!WR2q#^AzY%+`U46QiA0D(r91osDstgOG(r@5 zP9qvoC?q1`o$PMx&CTBJ-t6A&Ugk-jW@az>-o2gK@8&uHxtf4f;V^~N1wh+g2oc+W>V41E#}Fh-wITfFBxU4R~m z1(>HF2rl059_?~Jw+bh;-v_ifz;4LG{XW5Yq>2Iap#tQf#{fO0Q;5z8y`!(tKqtT` zJ%`@H?00RNX1BH#dXIQ^6)mC$5Z7Qw~@>Ss*pz4Sr_Xw=V` z@{LY63Q$1!e#Vq*K>_wtUbx>aIlyg33in%r+st*-Gj~8Bw$V8{fpk%(S8(bT{CE?p zU8sok-SKr*y={4{5*J1KVNz#a{1WShCP@Z3L~R@3r+^p&*rP?$*Z`)n0Zd~9n8pS$ zjSXO$wr!`6Xa#yy8q9AX+Tn0XH7E|R { className={ 'bg-white bg-gray0-d w-100 justify-between relative tc pt3 ' + display } - style={{ height: 45 }} - > + style={{ height: 45 }}>
    {atHome ? null : ( { py={1} px={2} mr={2} - onClick={() => props.history.push('/')} - > + onClick={() => props.history.push('/')}> { color='washedGray' display='inline-block' style={{ cursor: 'pointer' }} - lineHeight="min" + lineHeight='min' py={1} px={2} - onClick={() => props.api.local.setOmnibox()} - > + onClick={() => props.api.local.setOmnibox()}> @@ -77,8 +74,8 @@ const StatusBar = (props) => {
    @@ -87,13 +84,13 @@ const StatusBar = (props) => { display='inline-block' borderRadius={2} color='washedGray' + lineHeight='min' border={1} px={2} py={1} - onClick={() => props.history.push('/~groups')} - > + onClick={() => props.history.push('/~groups')}> Date: Wed, 5 Aug 2020 21:32:31 -0400 Subject: [PATCH 10/13] groups: autofocus join prompt --- pkg/interface/src/apps/groups/components/join.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/interface/src/apps/groups/components/join.js b/pkg/interface/src/apps/groups/components/join.js index a092dd749d..2db84d1c4e 100644 --- a/pkg/interface/src/apps/groups/components/join.js +++ b/pkg/interface/src/apps/groups/components/join.js @@ -103,6 +103,7 @@ export class JoinScreen extends Component { spellCheck="false" rows={1} cols={32} + autoFocus={true} onKeyPress={(e) => { if (e.key === 'Enter') { e.preventDefault(); @@ -110,7 +111,7 @@ export class JoinScreen extends Component { } }} style={{ - resize: 'none', + resize: 'none' }} onChange={this.groupChange} value={this.state.group} From 7f185d0667766a2bd0a02bcc46a8871519d466ff Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Wed, 5 Aug 2020 21:33:59 -0400 Subject: [PATCH 11/13] omnibox: address @tacryt-socryp review comments --- pkg/interface/src/components/Omnibox.js | 77 +++++++++---------- pkg/interface/src/components/OmniboxInput.js | 25 ++++++ pkg/interface/src/components/OmniboxResult.js | 31 ++++---- pkg/interface/src/lib/omnibox.js | 2 +- 4 files changed, 80 insertions(+), 55 deletions(-) create mode 100644 pkg/interface/src/components/OmniboxInput.js diff --git a/pkg/interface/src/components/Omnibox.js b/pkg/interface/src/components/Omnibox.js index 93e9d4f9de..93d4a7efb8 100644 --- a/pkg/interface/src/components/Omnibox.js +++ b/pkg/interface/src/components/Omnibox.js @@ -3,6 +3,7 @@ import { withRouter } from 'react-router-dom'; import { Box, Row, Rule, Text } from '@tlon/indigo-react'; import index from '../lib/omnibox'; import Mousetrap from 'mousetrap'; +import OmniboxInput from './OmniboxInput'; import OmniboxResult from './OmniboxResult'; import { cite } from '../lib/util'; @@ -33,8 +34,8 @@ export class Omnibox extends Component { Mousetrap.bind('escape', () => this.props.api.local.setOmnibox()); document.addEventListener('mousedown', this.handleClickOutside); const touchstart = new Event('touchstart'); - this.input.dispatchEvent(touchstart); - this.input.focus(); + this.omniInput.input.dispatchEvent(touchstart); + this.omniInput.input.focus(); } } @@ -77,7 +78,9 @@ export class Omnibox extends Component { handleClickOutside(evt) { if (this.props.show && !this.omniBox.contains(evt.target)) { - this.props.api.local.setOmnibox(); + this.setState({ results: this.initialResults(), query: '' }, () => { + this.props.api.local.setOmnibox(); + }); } } @@ -121,10 +124,11 @@ export class Omnibox extends Component { results.set(category, categoryIndex.filter((result) => { return ( - result.title.includes(query) || - result.link.includes(query) || - result.app.includes(query) || - (result.host !== null ? result.host.includes(query) : false)); + result.title.toLowerCase().includes(query) || + result.link.toLowerCase().includes(query) || + result.app.toLowerCase().includes(query) || + (result.host !== null ? result.host.includes(query) : false) + ); }) ); }); @@ -133,26 +137,26 @@ export class Omnibox extends Component { } setPreviousSelected() { - const current = this.state.selected; - const flattenedResults = Array.from(this.state.results.values()).flat(); - const totalLength = flattenedResults.length; - if (current !== '') { - const currentIndex = flattenedResults.indexOf( - ...flattenedResults.filter((e) => { - return e.link === current; - }) - ); - if (currentIndex > 0) { - const nextLink = flattenedResults[currentIndex - 1].link; - this.setState({ selected: nextLink }); + const current = this.state.selected; + const flattenedResults = Array.from(this.state.results.values()).flat(); + const totalLength = flattenedResults.length; + if (current !== '') { + const currentIndex = flattenedResults.indexOf( + ...flattenedResults.filter((e) => { + return e.link === current; + }) + ); + if (currentIndex > 0) { + const nextLink = flattenedResults[currentIndex - 1].link; + this.setState({ selected: nextLink }); + } else { + const nextLink = flattenedResults[totalLength - 1].link; + this.setState({ selected: nextLink }); + } } else { - const nextLink = flattenedResults[totalLength - 1].link; - this.setState({ selected: nextLink }); + const nextLink = flattenedResults[totalLength - 1].link; + this.setState({ selected: nextLink }); } - } else { - const nextLink = flattenedResults[totalLength - 1].link; - this.setState({ selected: nextLink }); - } } setNextSelected() { @@ -181,7 +185,7 @@ export class Omnibox extends Component { const { props, state } = this; const categoryResult = []; - const renderResults = + const renderResults = {categoryResult} ; @@ -218,8 +222,7 @@ export class Omnibox extends Component { top='0' right='0' zIndex='9' - display={props.show ? 'block' : 'none'} - > + display={props.show ? 'block' : 'none'}> { this.omniBox = el; - }} - > - { - this.input = el; - }} - className="ba b--transparent w-100 br2 white-d bg-gray0-d inter f9 pa2" - style={{ maxWidth: 'calc(600px - 1.15rem)', boxSizing: 'border-box' }} - placeholder="Search..." - onKeyDown={e => this.control(e) } - onChange={this.search} - value={state.query} + }}> + { this.omniInput = el; }} + control={e => this.control(e)} + search={this.search} + query={state.query} /> {renderResults} diff --git a/pkg/interface/src/components/OmniboxInput.js b/pkg/interface/src/components/OmniboxInput.js new file mode 100644 index 0000000000..c86e714b7c --- /dev/null +++ b/pkg/interface/src/components/OmniboxInput.js @@ -0,0 +1,25 @@ + +import React, { Component } from 'react'; + +export class OmniboxInput extends Component { + render() { + const { props } = this; + return ( + { + this.input = el; + } + } + className='ba b--transparent w-100 br2 white-d bg-gray0-d inter f9 pa2' + style={{ maxWidth: 'calc(600px - 1.15rem)', boxSizing: 'border-box' }} + placeholder='Search...' + onKeyDown={props.control} + onChange={props.search} + value={props.query} + /> + ); + } +} + +export default OmniboxInput; + diff --git a/pkg/interface/src/components/OmniboxResult.js b/pkg/interface/src/components/OmniboxResult.js index eef4f76e6e..5e93a95382 100644 --- a/pkg/interface/src/components/OmniboxResult.js +++ b/pkg/interface/src/components/OmniboxResult.js @@ -24,50 +24,53 @@ export class OmniboxResult extends Component { invertGraphic = (!dark && this.state.hovered) || selected === link || (dark && !(this.state.hovered || selected === link)) - ? { filter: "invert(1)" } - : { filter: "invert(0)" }; + ? { 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)" } - : { filter: "invert(1)" }; + ? { filter: 'invert(0)', paddingTop: 2 } + : { filter: 'invert(1)', paddingTop: 2 }; } let graphic =
    ; - if (defaultApps.includes(icon.toLowerCase())) { + if (defaultApps.includes(icon.toLowerCase()) || icon.toLowerCase() === 'links') { graphic = ; } else { - graphic = ; + graphic = ; } return ( this.setHover(true)} onMouseLeave={() => this.setHover(false)} backgroundColor={ - this.state.hovered || selected === link ? "blue" : "white" + this.state.hovered || selected === link ? 'blue' : 'white' } - onClick={navigate}> + onClick={navigate} + width="100%" + > {this.state.hovered || selected === link ? ( <> {graphic} - + {text} - + {subtext} ) : ( <> {graphic} - {text} - + {text} + {subtext} diff --git a/pkg/interface/src/lib/omnibox.js b/pkg/interface/src/lib/omnibox.js index 4b9698a8aa..2be8824f21 100644 --- a/pkg/interface/src/lib/omnibox.js +++ b/pkg/interface/src/lib/omnibox.js @@ -30,7 +30,7 @@ export default function index(associations, apps) { title = title.charAt(0).toUpperCase() + title.slice(1); - let obj = result(`${title}: Create`, `/~${e}/new`, title, null); + let obj = result(`${title}: Create`, `/~${e}/new`, e, null); commands.push(obj); if (title === 'Groups') { From 6e15e955fbf4036576211f2eadb8c37f3f61fd54 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Wed, 5 Aug 2020 21:45:17 -0400 Subject: [PATCH 12/13] omnibox: remove fragment wrapper --- pkg/interface/src/components/Omnibox.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/interface/src/components/Omnibox.js b/pkg/interface/src/components/Omnibox.js index 93d4a7efb8..7bffbc858c 100644 --- a/pkg/interface/src/components/Omnibox.js +++ b/pkg/interface/src/components/Omnibox.js @@ -213,7 +213,6 @@ export class Omnibox extends Component { }); return ( - <> - ); } } From a393cdf665812fd0df7377d372e016032cd49210 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Wed, 5 Aug 2020 22:01:07 -0400 Subject: [PATCH 13/13] omnibox: move to cmd+L, globally bind the keys --- pkg/interface/package-lock.json | 5 +++++ pkg/interface/package.json | 1 + pkg/interface/src/App.js | 3 ++- pkg/interface/src/apps/chat/components/lib/chat-input.js | 8 +------- pkg/interface/src/components/Omnibox.js | 2 +- pkg/interface/src/components/StatusBar.js | 2 +- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pkg/interface/package-lock.json b/pkg/interface/package-lock.json index 27a526c506..070f88ac5a 100644 --- a/pkg/interface/package-lock.json +++ b/pkg/interface/package-lock.json @@ -6380,6 +6380,11 @@ "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz", "integrity": "sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==" }, + "mousetrap-global-bind": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mousetrap-global-bind/-/mousetrap-global-bind-1.1.0.tgz", + "integrity": "sha1-zX3pIivQZG+i4BDVTISnTCaojt0=" + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", diff --git a/pkg/interface/package.json b/pkg/interface/package.json index 1e21e1322e..d58279b6f3 100644 --- a/pkg/interface/package.json +++ b/pkg/interface/package.json @@ -18,6 +18,7 @@ "markdown-to-jsx": "^6.11.4", "moment": "^2.20.1", "mousetrap": "^1.6.5", + "mousetrap-global-bind": "^1.1.0", "prop-types": "^15.7.2", "react": "^16.5.2", "react-codemirror2": "^6.0.1", diff --git a/pkg/interface/src/App.js b/pkg/interface/src/App.js index d4f69b33ec..b6488b1b0e 100644 --- a/pkg/interface/src/App.js +++ b/pkg/interface/src/App.js @@ -6,6 +6,7 @@ import styled, { ThemeProvider, createGlobalStyle } from 'styled-components'; import { sigil as sigiljs, stringRenderer } from 'urbit-sigil-js'; import Mousetrap from 'mousetrap'; +import 'mousetrap-global-bind'; import './css/indigo-static.css'; import './css/fonts.css'; @@ -77,7 +78,7 @@ class App extends React.Component { this.api.local.setDark(this.themeWatcher.matches); this.themeWatcher.addListener(this.updateTheme); this.api.local.getBaseHash(); - Mousetrap.bind(['command+l', 'ctrl+l'], (e) => { + Mousetrap.bindGlobal(['command+/', 'ctrl+/'], (e) => { e.preventDefault(); this.api.local.setOmnibox(); }); diff --git a/pkg/interface/src/apps/chat/components/lib/chat-input.js b/pkg/interface/src/apps/chat/components/lib/chat-input.js index 3b658f684d..9e844b94d6 100644 --- a/pkg/interface/src/apps/chat/components/lib/chat-input.js +++ b/pkg/interface/src/apps/chat/components/lib/chat-input.js @@ -363,13 +363,7 @@ export class ChatInput extends Component { 'Shift-3': cm => cm.getValue().length === 0 ? this.toggleCode() - : CodeMirror.Pass, - 'Cmd-L': () => { - this.props.api.local.setOmnibox(); - }, - 'Ctrl-L': () => { - this.props.api.local.setOmnibox(); - } + : CodeMirror.Pass } }; diff --git a/pkg/interface/src/components/Omnibox.js b/pkg/interface/src/components/Omnibox.js index 7bffbc858c..14072bfde5 100644 --- a/pkg/interface/src/components/Omnibox.js +++ b/pkg/interface/src/components/Omnibox.js @@ -40,7 +40,7 @@ export class Omnibox extends Component { } componentWillUpdate(prevProps) { - if (!this.props.show && prevProps.show !== this.props.show) { + if (this.props.show && prevProps.show !== this.props.show) { Mousetrap.unbind('escape'); document.removeEventListener('mousedown', this.handleClickOutside); } diff --git a/pkg/interface/src/components/StatusBar.js b/pkg/interface/src/components/StatusBar.js index f73de587b8..da80a9be20 100644 --- a/pkg/interface/src/components/StatusBar.js +++ b/pkg/interface/src/components/StatusBar.js @@ -70,7 +70,7 @@ const StatusBar = (props) => { Leap - {metaKey}L + {metaKey}/