Add Get Apps screen, rename "Leap" to "AppSearch"

AppSearch is just for apps. Real Leap to come later.
Make sure we respect calm settings for notifications.
Create WayfindingAppLink component.
Use hard coded list of apps for Get Apps.
This commit is contained in:
Patrick O'Sullivan 2023-03-08 09:49:36 -06:00
parent 5fafa7ac88
commit d953ffe501
15 changed files with 357 additions and 160 deletions

View File

@ -16,10 +16,7 @@ import useKilnState from './state/kiln';
import useContactState from './state/contact';
import api from './state/api';
import { useMedia } from './logic/useMedia';
import {
useSettingsState,
useTheme,
} from './state/settings';
import { useCalm, useSettingsState, useTheme } from './state/settings';
import { useBrowserId, useLocalState } from './state/local';
import { ErrorAlert } from './components/ErrorAlert';
import { useErrorHandler } from './logic/useErrorHandler';
@ -56,10 +53,13 @@ const AppRoutes = () => {
const { search } = useLocation();
const handleError = useErrorHandler();
const browserId = useBrowserId();
const {
display: { doNotDisturb },
} = useSettingsState.getState();
const { count, unreadNotifications } = useNotifications();
useEffect(() => {
if ('Notification' in window) {
if ('Notification' in window && !doNotDisturb) {
if (count > 0 && Notification.permission === 'granted') {
unreadNotifications.forEach((bin) => {
makeBrowserNotification(bin);
@ -120,7 +120,7 @@ const AppRoutes = () => {
useHarkState.getState().start();
Mousetrap.bind(['command+/', 'ctrl+/'], () => {
push('/leap/search');
push('/search');
});
}),
[]
@ -129,7 +129,7 @@ const AppRoutes = () => {
return (
<Switch>
<Route path="/perma" component={PermalinkRoutes} />
<Route path={['/leap/:menu', '/']} component={Grid} />
<Route path={['/:menu', '/']} component={Grid} />
</Switch>
);
};

View File

@ -5,6 +5,7 @@ import { Dialog, DialogContent } from './Dialog';
import { Button } from './Button';
import { useCharges } from '../state/docket';
import { GroupLink } from './GroupLink';
import WayfindingAppLink from './WayfindingAppLink';
interface Group {
title: string;
@ -14,14 +15,6 @@ interface Group {
link: string;
}
interface App {
title: string;
description: string;
image: string;
color: string;
link: string;
}
const groups: Record<string, Group> = {
foundation: {
title: 'Urbit Foundation',
@ -46,35 +39,6 @@ const groups: Record<string, Group> = {
},
};
const AppLink = ({ link, title, description, image, color }: App) => {
return (
<div className="flex items-center justify-between py-2">
<div className="flex items-center space-x-2">
{image !== '' ? (
<img
src={image}
className="h-8 w-8 rounded"
style={{ backgroundColor: color }}
/>
) : (
<div className="h-8 w-8 rounded" style={{ backgroundColor: color }} />
)}
<div className="flex flex-col">
<span className="font-semibold">{title}</span>
{description && (
<span className="text-sm font-semibold text-gray-400">
{description}
</span>
)}
</div>
</div>
<Button variant="alt-primary" as="a" href={link} target="_blank">
Open App
</Button>
</div>
);
};
function LandscapeDescription() {
const charges = useCharges();
return (
@ -92,26 +56,29 @@ function LandscapeDescription() {
software developer, like ~paldev.
</p>
<div className="mt-8 space-y-2">
<AppLink
<WayfindingAppLink
title="Groups"
description="Build or join Urbit-based communities"
link="/apps/groups"
image={charges.groups?.image || ''}
color={charges.groups?.color || 'bg-gray'}
installed={charges['groups'] ? true : false}
/>
<AppLink
<WayfindingAppLink
title="Talk"
description="Simple instant messaging app"
link="/apps/talk"
image={charges.talk?.image || ''}
color={charges.talk?.color || 'bg-blue'}
installed={charges['talk'] ? true : false}
/>
<AppLink
<WayfindingAppLink
title="Terminal"
description="Pop open the hood of your urbit"
link="/apps/webterm"
image={charges.webterm?.image || ''}
color={charges.webterm?.color || 'bg-black'}
installed={charges['terminal'] ? true : false}
/>
</div>
<h1 className="my-8 text-2xl font-bold">Where are the people?</h1>

View File

@ -0,0 +1,55 @@
import React from 'react';
import { Button } from './Button';
interface WayfindingAppLinkProps {
title: string;
description: string;
image?: string | null;
color: string;
link: string;
installed: boolean;
}
const WayfindingAppLink = ({
link,
title,
description,
image = null,
color,
installed,
}: WayfindingAppLinkProps) => {
return (
<div className="flex items-center justify-between py-2">
<div className="flex items-center space-x-2">
{image !== null && image !== '' ? (
<img
src={image}
className="h-8 w-8 rounded"
style={{ backgroundColor: color }}
/>
) : (
<div className="h-8 w-8 rounded" style={{ backgroundColor: color }} />
)}
<div className="flex flex-col">
<span className="font-semibold text-gray-800">{title}</span>
{description && (
<span className="text-sm font-semibold text-gray-400">
{description}
</span>
)}
</div>
</div>
{installed ? (
<Button variant="alt-primary" as="a" href={link} target="_blank">
Open App
</Button>
) : (
<Button variant="alt-primary" as="a" href={link} target="_blank">
Install App
</Button>
)}
</div>
);
};
export default WayfindingAppLink;

View File

@ -0,0 +1,21 @@
import React from 'react';
export default function MagnifyingGlass16Icon(
props: React.SVGProps<SVGSVGElement>
) {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="none"
>
<path
className="fill-current"
fillRule="evenodd"
d="M6.5 7a3 3 0 1 1 6 0 3 3 0 0 1-6 0Zm3-5a5 5 0 0 0-4.172 7.757l-.535.536-2 2a1 1 0 1 0 1.414 1.414l2-2 .536-.535A5 5 0 1 0 9.5 2Z"
clipRule="evenodd"
/>
</svg>
);
}

View File

@ -8,14 +8,15 @@ import React, {
useCallback,
useImperativeHandle,
useRef,
useEffect
useEffect,
useState,
} from 'react';
import { Link, useHistory, useLocation, useRouteMatch } from 'react-router-dom';
import { Cross } from '../components/icons/Cross';
import { useDebounce } from '../logic/useDebounce';
import { useErrorHandler } from '../logic/useErrorHandler';
import { useMedia } from '../logic/useMedia';
import { MenuState, useLeapStore } from './Nav';
import { MenuState, useAppSearchStore } from './Nav';
function normalizePathEnding(path: string) {
const end = path.length - 1;
@ -37,7 +38,6 @@ type LeapProps = {
menu: MenuState;
dropdown: string;
navOpen: boolean;
systemMenuOpen: boolean;
} & HTMLAttributes<HTMLDivElement>;
function normalizeMatchString(match: string, keepAltChars: boolean): string {
@ -50,19 +50,21 @@ function normalizeMatchString(match: string, keepAltChars: boolean): string {
return normalizedString;
}
export const Leap = React.forwardRef(
({ menu, dropdown, navOpen, systemMenuOpen, className }: LeapProps, ref) => {
export const AppSearch = React.forwardRef(
({ menu, dropdown, navOpen, className }: LeapProps, ref) => {
const { push } = useHistory();
const location = useLocation();
const isMobile = useMedia('(max-width: 639px)');
const deskMatch = useRouteMatch<{ menu?: MenuState; query?: string; desk?: string }>(
`/leap/${menu}/:query?/(apps)?/:desk?`
const deskMatch = useRouteMatch<{
menu?: MenuState;
query?: string;
desk?: string;
}>(`/${menu}/:query?/(apps)?/:desk?`);
const appsMatch = useRouteMatch(
`/${menu}/${deskMatch?.params.query}/apps`
);
const systemPrefMatch = useRouteMatch<{ submenu: string }>(`/leap/system-preferences/:submenu`);
const appsMatch = useRouteMatch(`/leap/${menu}/${deskMatch?.params.query}/apps`);
const inputRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => inputRef.current);
const { rawInput, selectedMatch, matches, selection, select } = useLeapStore();
const { rawInput, selectedMatch, matches, selection, select } =
useAppSearchStore();
const handleError = useErrorHandler();
useEffect(() => {
@ -77,16 +79,24 @@ export const Leap = React.forwardRef(
useEffect(() => {
const newMatch = getMatch(rawInput);
if (newMatch && rawInput) {
useLeapStore.setState({ selectedMatch: newMatch });
useAppSearchStore.setState({ selectedMatch: newMatch });
}
}, [rawInput, matches]);
useEffect(() => {
if (menu === 'search') {
inputRef.current?.focus();
} else {
inputRef.current?.blur();
}
}, [menu]);
const toggleSearch = useCallback(() => {
if (selection || menu === 'search') {
return;
}
push('/leap/search');
push('/search');
}, [selection, menu]);
const onFocus = useCallback(
@ -119,7 +129,7 @@ export const Leap = React.forwardRef(
.trim()
.replace('%', '')
.replace(/(~?[\w^_-]{3,13})\//, '$1/apps/$1/');
push(`/leap/${menu}/${normalizedValue}`);
push(`/${menu}/${normalizedValue}`);
},
[menu]
);
@ -130,7 +140,7 @@ export const Leap = React.forwardRef(
return;
}
useLeapStore.setState({ searchInput: input });
useAppSearchStore.setState({ searchInput: input });
navigateByInput(input);
},
300,
@ -139,52 +149,12 @@ export const Leap = React.forwardRef(
const handleSearch = useCallback(debouncedSearch, [deskMatch]);
const matchSystemPrefs = useCallback(
(target: string) => {
if (isMobile) {
return false;
}
if (!systemPrefMatch && target === 'system-updates') {
return true;
}
return systemPrefMatch?.params.submenu === target;
},
[location, systemPrefMatch]
);
const getPlaceholder = () => {
if (systemMenuOpen) {
switch (true) {
case matchSystemPrefs('system-updates'):
return 'My Urbit: About';
case matchSystemPrefs('help'):
return 'My Urbit: Help';
case matchSystemPrefs('security'):
return 'My Urbit: Security';
case matchSystemPrefs('notifications'):
return 'My Urbit: Notifications';
case matchSystemPrefs('privacy'):
return 'My Urbit: Attention & Privacy';
case matchSystemPrefs('appearance'):
return 'My Urbit: Appearance';
case matchSystemPrefs('shortcuts'):
return 'My Urbit: Shortcuts';
case matchSystemPrefs('interface'):
return 'My Urbit: Interface Settings';
default:
return 'Settings';
}
}
return 'Search';
};
const onChange = useCallback(
handleError((e: ChangeEvent<HTMLInputElement>) => {
const input = e.target as HTMLInputElement;
const value = input.value.trim();
const isDeletion = (e.nativeEvent as InputEvent).inputType === 'deleteContentBackward';
const isDeletion =
(e.nativeEvent as InputEvent).inputType === 'deleteContentBackward';
const inputMatch = getMatch(value);
const matchValue = inputMatch?.value;
@ -192,16 +162,17 @@ export const Leap = React.forwardRef(
inputRef.current.value = matchValue;
const start = matchValue.startsWith(value)
? value.length
: matchValue.substring(0, matchValue.indexOf(value)).length + value.length;
: matchValue.substring(0, matchValue.indexOf(value)).length +
value.length;
inputRef.current.setSelectionRange(start, matchValue.length);
useLeapStore.setState({
useAppSearchStore.setState({
rawInput: matchValue,
selectedMatch: inputMatch
selectedMatch: inputMatch,
});
} else {
useLeapStore.setState({
useAppSearchStore.setState({
rawInput: value,
selectedMatch: matches[0]
selectedMatch: matches[0],
});
}
@ -227,7 +198,7 @@ export const Leap = React.forwardRef(
}
push(currentMatch.url);
useLeapStore.setState({ rawInput: '' });
useAppSearchStore.setState({ rawInput: '' });
}),
[deskMatch, selectedMatch]
);
@ -239,7 +210,12 @@ export const Leap = React.forwardRef(
if (deletion && !rawInput && selection) {
e.preventDefault();
select(null, appsMatch && !appsMatch.isExact ? undefined : deskMatch?.params.query);
select(
null,
appsMatch && !appsMatch.isExact
? undefined
: deskMatch?.params.query
);
const pathBack = createPreviousPath(deskMatch?.url || '');
push(pathBack);
}
@ -257,14 +233,15 @@ export const Leap = React.forwardRef(
return matchValue === searchValue;
})
: 0;
const unsafeIndex = e.key === 'ArrowUp' ? currentIndex - 1 : currentIndex + 1;
const unsafeIndex =
e.key === 'ArrowUp' ? currentIndex - 1 : currentIndex + 1;
const index = (unsafeIndex + matches.length) % matches.length;
const newMatch = matches[index];
useLeapStore.setState({
useAppSearchStore.setState({
rawInput: newMatch.value,
// searchInput: matchValue,
selectedMatch: newMatch
selectedMatch: newMatch,
});
}
}),
@ -302,7 +279,7 @@ export const Leap = React.forwardRef(
id="leap"
type="text"
ref={inputRef}
placeholder={selection ? '' : getPlaceholder()}
placeholder={selection ? '' : 'e.g., ~paldev or ~paldev/pals'}
// TODO: style placeholder text with 100% opacity.
// Not immediately clear how to do this within tailwind.
className="outline-none h-full w-full flex-1 bg-transparent px-2 text-lg text-gray-800 sm:text-base"
@ -320,7 +297,7 @@ export const Leap = React.forwardRef(
</form>
{menu === 'search' && (
<Link
to="/"
to="/get-apps"
className="circle-button default-ring absolute top-1/2 right-2 h-8 w-8 flex-none -translate-y-1/2 text-gray-600"
onClick={() => select(null)}
>

160
ui/src/nav/GetApps.tsx Normal file
View File

@ -0,0 +1,160 @@
import React, { useRef } from 'react';
import { useParams } from 'react-router-dom';
import WayfindingAppLink from '../components/WayfindingAppLink';
import { useCharges } from '../state/docket';
import { AppSearch } from './AppSearch';
import { MenuState} from './Nav';
const SECTIONS = {
SELECTS: 'Tlon Selects',
PALS: 'Powered by Pals',
DEV: 'Develop on Urbit',
USEFUL: 'Make Urbit Useful',
};
const APPS = [
{
title: 'pals',
description: 'friendlist for peer discovery',
color: '#99D3BD',
link: '/apps/pals',
source: '~paldev',
section: SECTIONS.SELECTS,
desk: 'pals',
},
{
title: 'Terminal',
description: "A web interface to your Urbit's command line",
color: '#233D34',
link: '/apps/webterm',
section: SECTIONS.SELECTS,
desk: 'webterm',
},
{
title: 'face',
description: 'see your friends',
color: '#3B5998',
link: '/apps/face',
section: SECTIONS.PALS,
desk: 'face',
},
{
title: 'rumors',
description: 'Anonymous gossip from friends of friends',
color: '#BB77DD',
link: '/apps/rumors',
section: SECTIONS.PALS,
desk: 'rumors',
},
{
title: 'Contacts',
description: 'Contact book',
color: '#338899',
link: '/apps/whom',
section: SECTIONS.PALS,
desk: 'whom',
},
{
title: "sc'o're",
description: "leaderboard for groups' ['o']s, as seen on tv!",
color: '#FFFF00',
link: '/apps/scooore',
section: SECTIONS.PALS,
desk: 'scooore',
},
{
title: 'Docs',
description: 'User and developer documentation for Urbit apps',
color: '#FFCF00',
link: '/apps/docs',
section: SECTIONS.DEV,
desk: 'docs',
},
{
title: 'Quorum',
description:
'A choral explanations app (a la Stack Overflow or Quora) for Urbit',
color: '#F2F2F2',
link: '/apps/quorom',
section: SECTIONS.DEV,
desk: 'quorum',
image: 'https://ladrut.xyz/quorum/quorum-logo.png',
},
{
title: 'silo',
description: 'An S3 storage manager',
color: '#4F46E5',
link: '/apps/silo',
section: SECTIONS.USEFUL,
desk: 'silo',
},
{
title: 'hodl',
description: 'A portfolio for all that you hodl',
color: '#B8A3D1',
link: '/apps/hodl',
section: SECTIONS.USEFUL,
desk: 'hodl',
image:
'https://user-images.githubusercontent.com/16504501/194947852-8802fd63-5954-4ce8-b147-2072bd929242.png',
},
];
export default function GetApps() {
const charges = useCharges();
const { menu } = useParams<{ menu?: MenuState }>();
const inputRef = useRef<HTMLInputElement>(null);
const menuState = menu || 'closed';
const isOpen = menuState !== 'upgrading' && menuState !== 'closed';
return (
<div className="flex h-full flex-col space-y-8 overflow-y-scroll p-8">
<h1 className="text-xl font-bold text-gray-800">Find Urbit Apps</h1>
<div className="flex flex-col space-y-2">
<h2 className="font-semibold text-gray-800">
Find Urbit App Developers
</h2>
<AppSearch
ref={inputRef}
menu={menuState}
dropdown="leap-items"
navOpen={isOpen}
/>
</div>
{Object.entries(SECTIONS).map(([key, name]) => (
<div key={key} className="flex flex-col space-y-2">
<h2 className="text-lg font-bold text-gray-800">{name}</h2>
<div className="flex flex-col space-y-2 px-2">
{APPS.map((app) => {
if (app.section === name) {
return (
<WayfindingAppLink
key={app.desk}
title={
charges[app.desk] ? charges[app.desk].title : app.title
}
description={
charges[app.desk]
? charges[app.desk]?.info ?? app.description
: app.description
}
color={
charges[app.desk] ? charges[app.desk].color : app.color
}
image={
charges[app.desk]
? charges[app.desk].image
: app.image ?? ''
}
link={charges[app.desk] ? app.link : ''}
installed={charges[app.desk] ? true : false}
/>
);
}
})}
</div>
</div>
))}
</div>
);
}

View File

@ -22,7 +22,7 @@ import { Avatar } from '../components/Avatar';
import { Dialog } from '../components/Dialog';
import { ErrorAlert } from '../components/ErrorAlert';
import { Help } from './Help';
import { Leap } from './Leap';
import { AppSearch } from './AppSearch';
import { Notifications } from './notifications/Notifications';
import { NotificationsLink } from './notifications/NotificationsLink';
import { Search } from './Search';
@ -30,6 +30,8 @@ import { SystemPreferences } from '../preferences/SystemPreferences';
import { useSystemUpdate } from '../logic/useSystemUpdate';
import { Bullet } from '../components/icons/Bullet';
import { Cross } from '../components/icons/Cross';
import MagnifyingGlass16Icon from '../components/icons/MagnifyingGlass16Icon';
import GetApps from './GetApps';
export interface MatchItem {
url: string;
@ -38,7 +40,7 @@ export interface MatchItem {
display?: string;
}
interface LeapStore {
interface AppSearchStore {
rawInput: string;
searchInput: string;
matches: MatchItem[];
@ -47,7 +49,7 @@ interface LeapStore {
select: (selection: React.ReactNode, input?: string) => void;
}
export const useLeapStore = create<LeapStore>((set) => ({
export const useAppSearchStore = create<AppSearchStore>((set) => ({
rawInput: '',
searchInput: '',
matches: [],
@ -61,7 +63,7 @@ export const useLeapStore = create<LeapStore>((set) => ({
}),
}));
window.leap = useLeapStore.getState;
window.appSearch = useAppSearchStore.getState;
export type MenuState =
| 'closed'
@ -99,7 +101,7 @@ export const SystemPrefsLink = ({
}
return (
<Link to="/leap/system-preferences" className="relative flex-none">
<Link to="/system-preferences" className="relative flex-none">
<Avatar shipName={window.ship} size="nav" />
{systemBlocked && (
<Bullet
@ -111,15 +113,28 @@ export const SystemPrefsLink = ({
);
};
export const GetAppsLink = ({ menuState }: PrefsLinkProps) => {
const active = ['get-apps'].indexOf(menuState) >= 0;
return (
<Link
to="/get-apps"
className="flex h-9 w-[150px] items-center justify-center space-x-2 rounded-lg bg-blue-soft px-3 py-2.5"
>
<MagnifyingGlass16Icon className="h-4 w-4 fill-current text-blue" />
<span className="whitespace-nowrap text-blue">Get Urbit Apps</span>
</Link>
);
};
export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
const { push } = useHistory();
const inputRef = useRef<HTMLInputElement>(null);
const navRef = useRef<HTMLDivElement>(null);
const dialogNavRef = useRef<HTMLDivElement>(null);
const systemMenuOpen = useRouteMatch('/leap/system-preferences');
const { systemBlocked } = useSystemUpdate();
const [dialogContentOpen, setDialogContentOpen] = useState(false);
const select = useLeapStore((state) => state.select);
const select = useAppSearchStore((state) => state.select);
const menuState = menu || 'closed';
const isOpen = menuState !== 'upgrading' && menuState !== 'closed';
@ -172,13 +187,16 @@ export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
navOpen={isOpen}
notificationsOpen={menu === 'notifications'}
/>
<Leap
ref={inputRef}
menu={menuState}
dropdown="leap-items"
navOpen={isOpen}
systemMenuOpen={!!systemMenuOpen}
/>
{menuState === 'search' ? (
<AppSearch
ref={inputRef}
menu={menuState}
dropdown="leap-items"
navOpen={isOpen}
/>
) : (
<GetAppsLink menuState={menuState} />
)}
</Portal.Root>
<div
ref={navRef}
@ -213,13 +231,11 @@ export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
role="listbox"
>
<Switch>
<Route path="/leap/notifications" component={Notifications} />
<Route
path="/leap/system-preferences"
component={SystemPreferences}
/>
<Route path="/leap/help-and-support" component={Help} />
<Route path={['/leap/search', '/leap']} component={Search} />
<Route path="/notifications" component={Notifications} />
<Route path="/system-preferences" component={SystemPreferences} />
<Route path="/help-and-support" component={Help} />
<Route path="/get-apps" component={GetApps} />
<Route path={['/search']} component={Search} />
</Switch>
</div>
</DialogContent>

View File

@ -9,7 +9,7 @@ import { usePike } from '../state/kiln';
import { disableDefault, handleDropdownLink } from '../state/util';
import { useMedia } from '../logic/useMedia';
import { Cross } from '../components/icons/Cross';
import { useLeapStore } from './Nav';
import { useAppSearchStore } from './Nav';
type SystemMenuProps = HTMLAttributes<HTMLButtonElement> & {
open: boolean;
@ -33,7 +33,7 @@ export const SystemMenu = ({
const garden = usePike(window.desk);
const hash = garden ? getHash(garden) : null;
const isMobile = useMedia('(max-width: 639px)');
const select = useLeapStore((s) => s.select);
const select = useAppSearchStore((s) => s.select);
const clearSelection = useCallback(() => select(null), [select]);
const copyHash = useCallback(

View File

@ -2,7 +2,7 @@ import classNames from 'classnames';
import React, { useCallback } from 'react';
import { Link, LinkProps } from 'react-router-dom';
import { Cross } from '../../components/icons/Cross';
import { useLeapStore } from '../Nav';
import { useAppSearchStore } from '../Nav';
import { SettingsState, useSettingsState } from '../../state/settings';
import BellIcon from '../../components/icons/BellIcon';
import { useNotifications } from './useNotifications';
@ -32,12 +32,12 @@ export const NotificationsLink = ({ navOpen, notificationsOpen }: NotificationsL
const dnd = useSettingsState(selDnd);
const { count } = useNotifications();
const state = getNotificationsState(notificationsOpen, count, dnd);
const select = useLeapStore((s) => s.select);
const select = useAppSearchStore((s) => s.select);
const clearSelection = useCallback(() => select(null), [select]);
return (
<Link
to={state === 'open' ? '/' : '/leap/notifications'}
to={state === 'open' ? '/' : '/notifications'}
className={classNames(
'relative z-50 flex-none circle-button h4 default-ring',
navOpen && 'text-opacity-60',

View File

@ -4,7 +4,7 @@ import fuzzy from 'fuzzy';
import { Treaty } from '@urbit/api';
import { ShipName } from '../../components/ShipName';
import { useAllyTreaties } from '../../state/docket';
import { useLeapStore } from '../Nav';
import { useAppSearchStore } from '../Nav';
import { AppList } from '../../components/AppList';
import { addRecentDev } from './Home';
import { Spinner } from '../../components/Spinner';
@ -12,7 +12,7 @@ import { Spinner } from '../../components/Spinner';
type AppsProps = RouteComponentProps<{ ship: string }>;
export const Apps = ({ match }: AppsProps) => {
const { searchInput, selectedMatch, select } = useLeapStore((state) => ({
const { searchInput, selectedMatch, select } = useAppSearchStore((state) => ({
searchInput: state.searchInput,
select: state.select,
selectedMatch: state.selectedMatch
@ -61,7 +61,7 @@ export const Apps = ({ match }: AppsProps) => {
useEffect(() => {
if (results) {
useLeapStore.setState({
useAppSearchStore.setState({
matches: results.map((r) => ({
url: getAppPath(r),
openInNewTab: false,

View File

@ -3,7 +3,7 @@ import create from 'zustand';
import _ from 'lodash';
import React, { useEffect } from 'react';
import { persist } from 'zustand/middleware';
import { MatchItem, useLeapStore } from '../Nav';
import { MatchItem, useAppSearchStore } from '../Nav';
import { providerMatch } from './Providers';
import { AppList } from '../../components/AppList';
import { ProviderList } from '../../components/ProviderList';
@ -88,7 +88,7 @@ function getApps(desks: string[], charges: ChargesWithDesks) {
}
export const Home = () => {
const selectedMatch = useLeapStore((state) => state.selectedMatch);
const selectedMatch = useAppSearchStore((state) => state.selectedMatch);
const { recentApps, recentDevs } = useRecentsStore();
const charges = useCharges();
const groups = charges?.landscape;
@ -108,7 +108,7 @@ export const Home = () => {
}));
const devs = recentDevs.map(providerMatch);
useLeapStore.setState({
useAppSearchStore.setState({
matches: ([] as MatchItem[]).concat(appMatches, devs)
});
}, [recentApps, recentDevs]);

View File

@ -3,7 +3,7 @@ import { RouteComponentProps } from 'react-router-dom';
import fuzzy from 'fuzzy';
import { Provider, deSig } from '@urbit/api';
import * as ob from 'urbit-ob';
import { MatchItem, useLeapStore } from '../Nav';
import { MatchItem, useAppSearchStore } from '../Nav';
import { useAllies, useCharges } from '../../state/docket';
import { ProviderList } from '../../components/ProviderList';
import useContactState from '../../state/contact';
@ -19,7 +19,7 @@ export function providerMatch(provider: Provider | string): MatchItem {
return {
value,
display,
url: `/leap/search/${value}/apps`,
url: `/search/${value}/apps`,
openInNewTab: false
};
}
@ -34,7 +34,7 @@ function fuzzySort(search: string) {
}
export const Providers = ({ match }: ProvidersProps) => {
const selectedMatch = useLeapStore((state) => state.selectedMatch);
const selectedMatch = useAppSearchStore((state) => state.selectedMatch);
const provider = match?.params.ship;
const contacts = useContactState((s) => s.contacts);
const charges = useCharges();
@ -87,7 +87,7 @@ export const Providers = ({ match }: ProvidersProps) => {
useEffect(() => {
if (search) {
useLeapStore.setState({ rawInput: search });
useAppSearchStore.setState({ rawInput: search });
}
}, []);
@ -106,7 +106,7 @@ export const Providers = ({ match }: ProvidersProps) => {
const newProviderMatches = isValidPatp
? [
{
url: `/leap/search/${patp}/apps`,
url: `/search/${patp}/apps`,
value: patp,
display: patp,
openInNewTab: false
@ -114,7 +114,7 @@ export const Providers = ({ match }: ProvidersProps) => {
]
: [];
useLeapStore.setState({
useAppSearchStore.setState({
matches: ([] as MatchItem[]).concat(appMatches, providerMatches, newProviderMatches)
});
}

View File

@ -5,10 +5,10 @@ import { Spinner } from '../../components/Spinner';
import useDocketState, { useCharge, useTreaty } from '../../state/docket';
import { usePike } from '../../state/kiln';
import { getAppName } from '../../state/util';
import { useLeapStore } from '../Nav';
import { useAppSearchStore } from '../Nav';
export const TreatyInfo = () => {
const select = useLeapStore((state) => state.select);
const select = useAppSearchStore((state) => state.select);
const { host, desk } = useParams<{ host: string; desk: string }>();
const treaty = useTreaty(host, desk);
const pike = usePike(desk);
@ -23,7 +23,7 @@ export const TreatyInfo = () => {
useEffect(() => {
select(<>{name}</>);
useLeapStore.setState({ matches: [] });
useAppSearchStore.setState({ matches: [] });
}, [name]);
if (!treaty) {

View File

@ -1,4 +1,4 @@
import { useLeapStore } from './nav/Nav';
import { useAppSearchStore } from './nav/Nav';
import { useRecentsStore } from './nav/search/Home';
import useDocketState from './state/docket';
@ -8,7 +8,7 @@ declare global {
desk: string;
recents: typeof useRecentsStore.getState;
docket: typeof useDocketState.getState;
leap: typeof useLeapStore.getState;
appSearch: typeof useAppSearchStore.getState;
}
}

View File

@ -66,6 +66,7 @@ const base = new Theme().addColors({
},
blue: {
DEFAULT: '#008EFF',
soft: '#E5F4FF',
50: '#EFF9FF',
100: '#C8EDFF',
200: '#A0E1FF',