mirror of
https://github.com/tloncorp/landscape.git
synced 2024-12-03 02:00:18 +03:00
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:
parent
5fafa7ac88
commit
d953ffe501
@ -16,10 +16,7 @@ import useKilnState from './state/kiln';
|
|||||||
import useContactState from './state/contact';
|
import useContactState from './state/contact';
|
||||||
import api from './state/api';
|
import api from './state/api';
|
||||||
import { useMedia } from './logic/useMedia';
|
import { useMedia } from './logic/useMedia';
|
||||||
import {
|
import { useCalm, useSettingsState, useTheme } from './state/settings';
|
||||||
useSettingsState,
|
|
||||||
useTheme,
|
|
||||||
} from './state/settings';
|
|
||||||
import { useBrowserId, useLocalState } from './state/local';
|
import { useBrowserId, useLocalState } from './state/local';
|
||||||
import { ErrorAlert } from './components/ErrorAlert';
|
import { ErrorAlert } from './components/ErrorAlert';
|
||||||
import { useErrorHandler } from './logic/useErrorHandler';
|
import { useErrorHandler } from './logic/useErrorHandler';
|
||||||
@ -56,10 +53,13 @@ const AppRoutes = () => {
|
|||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
const handleError = useErrorHandler();
|
const handleError = useErrorHandler();
|
||||||
const browserId = useBrowserId();
|
const browserId = useBrowserId();
|
||||||
|
const {
|
||||||
|
display: { doNotDisturb },
|
||||||
|
} = useSettingsState.getState();
|
||||||
const { count, unreadNotifications } = useNotifications();
|
const { count, unreadNotifications } = useNotifications();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if ('Notification' in window) {
|
if ('Notification' in window && !doNotDisturb) {
|
||||||
if (count > 0 && Notification.permission === 'granted') {
|
if (count > 0 && Notification.permission === 'granted') {
|
||||||
unreadNotifications.forEach((bin) => {
|
unreadNotifications.forEach((bin) => {
|
||||||
makeBrowserNotification(bin);
|
makeBrowserNotification(bin);
|
||||||
@ -120,7 +120,7 @@ const AppRoutes = () => {
|
|||||||
useHarkState.getState().start();
|
useHarkState.getState().start();
|
||||||
|
|
||||||
Mousetrap.bind(['command+/', 'ctrl+/'], () => {
|
Mousetrap.bind(['command+/', 'ctrl+/'], () => {
|
||||||
push('/leap/search');
|
push('/search');
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
[]
|
[]
|
||||||
@ -129,7 +129,7 @@ const AppRoutes = () => {
|
|||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/perma" component={PermalinkRoutes} />
|
<Route path="/perma" component={PermalinkRoutes} />
|
||||||
<Route path={['/leap/:menu', '/']} component={Grid} />
|
<Route path={['/:menu', '/']} component={Grid} />
|
||||||
</Switch>
|
</Switch>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,7 @@ import { Dialog, DialogContent } from './Dialog';
|
|||||||
import { Button } from './Button';
|
import { Button } from './Button';
|
||||||
import { useCharges } from '../state/docket';
|
import { useCharges } from '../state/docket';
|
||||||
import { GroupLink } from './GroupLink';
|
import { GroupLink } from './GroupLink';
|
||||||
|
import WayfindingAppLink from './WayfindingAppLink';
|
||||||
|
|
||||||
interface Group {
|
interface Group {
|
||||||
title: string;
|
title: string;
|
||||||
@ -14,14 +15,6 @@ interface Group {
|
|||||||
link: string;
|
link: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface App {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
image: string;
|
|
||||||
color: string;
|
|
||||||
link: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const groups: Record<string, Group> = {
|
const groups: Record<string, Group> = {
|
||||||
foundation: {
|
foundation: {
|
||||||
title: 'Urbit 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() {
|
function LandscapeDescription() {
|
||||||
const charges = useCharges();
|
const charges = useCharges();
|
||||||
return (
|
return (
|
||||||
@ -92,26 +56,29 @@ function LandscapeDescription() {
|
|||||||
software developer, like “~paldev”.
|
software developer, like “~paldev”.
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-8 space-y-2">
|
<div className="mt-8 space-y-2">
|
||||||
<AppLink
|
<WayfindingAppLink
|
||||||
title="Groups"
|
title="Groups"
|
||||||
description="Build or join Urbit-based communities"
|
description="Build or join Urbit-based communities"
|
||||||
link="/apps/groups"
|
link="/apps/groups"
|
||||||
image={charges.groups?.image || ''}
|
image={charges.groups?.image || ''}
|
||||||
color={charges.groups?.color || 'bg-gray'}
|
color={charges.groups?.color || 'bg-gray'}
|
||||||
|
installed={charges['groups'] ? true : false}
|
||||||
/>
|
/>
|
||||||
<AppLink
|
<WayfindingAppLink
|
||||||
title="Talk"
|
title="Talk"
|
||||||
description="Simple instant messaging app"
|
description="Simple instant messaging app"
|
||||||
link="/apps/talk"
|
link="/apps/talk"
|
||||||
image={charges.talk?.image || ''}
|
image={charges.talk?.image || ''}
|
||||||
color={charges.talk?.color || 'bg-blue'}
|
color={charges.talk?.color || 'bg-blue'}
|
||||||
|
installed={charges['talk'] ? true : false}
|
||||||
/>
|
/>
|
||||||
<AppLink
|
<WayfindingAppLink
|
||||||
title="Terminal"
|
title="Terminal"
|
||||||
description="Pop open the hood of your urbit"
|
description="Pop open the hood of your urbit"
|
||||||
link="/apps/webterm"
|
link="/apps/webterm"
|
||||||
image={charges.webterm?.image || ''}
|
image={charges.webterm?.image || ''}
|
||||||
color={charges.webterm?.color || 'bg-black'}
|
color={charges.webterm?.color || 'bg-black'}
|
||||||
|
installed={charges['terminal'] ? true : false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<h1 className="my-8 text-2xl font-bold">Where are the people?</h1>
|
<h1 className="my-8 text-2xl font-bold">Where are the people?</h1>
|
||||||
|
55
ui/src/components/WayfindingAppLink.tsx
Normal file
55
ui/src/components/WayfindingAppLink.tsx
Normal 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;
|
21
ui/src/components/icons/MagnifyingGlass16Icon.tsx
Normal file
21
ui/src/components/icons/MagnifyingGlass16Icon.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -8,14 +8,15 @@ import React, {
|
|||||||
useCallback,
|
useCallback,
|
||||||
useImperativeHandle,
|
useImperativeHandle,
|
||||||
useRef,
|
useRef,
|
||||||
useEffect
|
useEffect,
|
||||||
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { Link, useHistory, useLocation, useRouteMatch } from 'react-router-dom';
|
import { Link, useHistory, useLocation, useRouteMatch } from 'react-router-dom';
|
||||||
import { Cross } from '../components/icons/Cross';
|
import { Cross } from '../components/icons/Cross';
|
||||||
import { useDebounce } from '../logic/useDebounce';
|
import { useDebounce } from '../logic/useDebounce';
|
||||||
import { useErrorHandler } from '../logic/useErrorHandler';
|
import { useErrorHandler } from '../logic/useErrorHandler';
|
||||||
import { useMedia } from '../logic/useMedia';
|
import { useMedia } from '../logic/useMedia';
|
||||||
import { MenuState, useLeapStore } from './Nav';
|
import { MenuState, useAppSearchStore } from './Nav';
|
||||||
|
|
||||||
function normalizePathEnding(path: string) {
|
function normalizePathEnding(path: string) {
|
||||||
const end = path.length - 1;
|
const end = path.length - 1;
|
||||||
@ -37,7 +38,6 @@ type LeapProps = {
|
|||||||
menu: MenuState;
|
menu: MenuState;
|
||||||
dropdown: string;
|
dropdown: string;
|
||||||
navOpen: boolean;
|
navOpen: boolean;
|
||||||
systemMenuOpen: boolean;
|
|
||||||
} & HTMLAttributes<HTMLDivElement>;
|
} & HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
function normalizeMatchString(match: string, keepAltChars: boolean): string {
|
function normalizeMatchString(match: string, keepAltChars: boolean): string {
|
||||||
@ -50,19 +50,21 @@ function normalizeMatchString(match: string, keepAltChars: boolean): string {
|
|||||||
return normalizedString;
|
return normalizedString;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Leap = React.forwardRef(
|
export const AppSearch = React.forwardRef(
|
||||||
({ menu, dropdown, navOpen, systemMenuOpen, className }: LeapProps, ref) => {
|
({ menu, dropdown, navOpen, className }: LeapProps, ref) => {
|
||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
const location = useLocation();
|
const deskMatch = useRouteMatch<{
|
||||||
const isMobile = useMedia('(max-width: 639px)');
|
menu?: MenuState;
|
||||||
const deskMatch = useRouteMatch<{ menu?: MenuState; query?: string; desk?: string }>(
|
query?: string;
|
||||||
`/leap/${menu}/:query?/(apps)?/:desk?`
|
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);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
useImperativeHandle(ref, () => inputRef.current);
|
useImperativeHandle(ref, () => inputRef.current);
|
||||||
const { rawInput, selectedMatch, matches, selection, select } = useLeapStore();
|
const { rawInput, selectedMatch, matches, selection, select } =
|
||||||
|
useAppSearchStore();
|
||||||
const handleError = useErrorHandler();
|
const handleError = useErrorHandler();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -77,16 +79,24 @@ export const Leap = React.forwardRef(
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newMatch = getMatch(rawInput);
|
const newMatch = getMatch(rawInput);
|
||||||
if (newMatch && rawInput) {
|
if (newMatch && rawInput) {
|
||||||
useLeapStore.setState({ selectedMatch: newMatch });
|
useAppSearchStore.setState({ selectedMatch: newMatch });
|
||||||
}
|
}
|
||||||
}, [rawInput, matches]);
|
}, [rawInput, matches]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (menu === 'search') {
|
||||||
|
inputRef.current?.focus();
|
||||||
|
} else {
|
||||||
|
inputRef.current?.blur();
|
||||||
|
}
|
||||||
|
}, [menu]);
|
||||||
|
|
||||||
const toggleSearch = useCallback(() => {
|
const toggleSearch = useCallback(() => {
|
||||||
if (selection || menu === 'search') {
|
if (selection || menu === 'search') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
push('/leap/search');
|
push('/search');
|
||||||
}, [selection, menu]);
|
}, [selection, menu]);
|
||||||
|
|
||||||
const onFocus = useCallback(
|
const onFocus = useCallback(
|
||||||
@ -119,7 +129,7 @@ export const Leap = React.forwardRef(
|
|||||||
.trim()
|
.trim()
|
||||||
.replace('%', '')
|
.replace('%', '')
|
||||||
.replace(/(~?[\w^_-]{3,13})\//, '$1/apps/$1/');
|
.replace(/(~?[\w^_-]{3,13})\//, '$1/apps/$1/');
|
||||||
push(`/leap/${menu}/${normalizedValue}`);
|
push(`/${menu}/${normalizedValue}`);
|
||||||
},
|
},
|
||||||
[menu]
|
[menu]
|
||||||
);
|
);
|
||||||
@ -130,7 +140,7 @@ export const Leap = React.forwardRef(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
useLeapStore.setState({ searchInput: input });
|
useAppSearchStore.setState({ searchInput: input });
|
||||||
navigateByInput(input);
|
navigateByInput(input);
|
||||||
},
|
},
|
||||||
300,
|
300,
|
||||||
@ -139,52 +149,12 @@ export const Leap = React.forwardRef(
|
|||||||
|
|
||||||
const handleSearch = useCallback(debouncedSearch, [deskMatch]);
|
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(
|
const onChange = useCallback(
|
||||||
handleError((e: ChangeEvent<HTMLInputElement>) => {
|
handleError((e: ChangeEvent<HTMLInputElement>) => {
|
||||||
const input = e.target as HTMLInputElement;
|
const input = e.target as HTMLInputElement;
|
||||||
const value = input.value.trim();
|
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 inputMatch = getMatch(value);
|
||||||
const matchValue = inputMatch?.value;
|
const matchValue = inputMatch?.value;
|
||||||
|
|
||||||
@ -192,16 +162,17 @@ export const Leap = React.forwardRef(
|
|||||||
inputRef.current.value = matchValue;
|
inputRef.current.value = matchValue;
|
||||||
const start = matchValue.startsWith(value)
|
const start = matchValue.startsWith(value)
|
||||||
? value.length
|
? 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);
|
inputRef.current.setSelectionRange(start, matchValue.length);
|
||||||
useLeapStore.setState({
|
useAppSearchStore.setState({
|
||||||
rawInput: matchValue,
|
rawInput: matchValue,
|
||||||
selectedMatch: inputMatch
|
selectedMatch: inputMatch,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
useLeapStore.setState({
|
useAppSearchStore.setState({
|
||||||
rawInput: value,
|
rawInput: value,
|
||||||
selectedMatch: matches[0]
|
selectedMatch: matches[0],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,7 +198,7 @@ export const Leap = React.forwardRef(
|
|||||||
}
|
}
|
||||||
|
|
||||||
push(currentMatch.url);
|
push(currentMatch.url);
|
||||||
useLeapStore.setState({ rawInput: '' });
|
useAppSearchStore.setState({ rawInput: '' });
|
||||||
}),
|
}),
|
||||||
[deskMatch, selectedMatch]
|
[deskMatch, selectedMatch]
|
||||||
);
|
);
|
||||||
@ -239,7 +210,12 @@ export const Leap = React.forwardRef(
|
|||||||
|
|
||||||
if (deletion && !rawInput && selection) {
|
if (deletion && !rawInput && selection) {
|
||||||
e.preventDefault();
|
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 || '');
|
const pathBack = createPreviousPath(deskMatch?.url || '');
|
||||||
push(pathBack);
|
push(pathBack);
|
||||||
}
|
}
|
||||||
@ -257,14 +233,15 @@ export const Leap = React.forwardRef(
|
|||||||
return matchValue === searchValue;
|
return matchValue === searchValue;
|
||||||
})
|
})
|
||||||
: 0;
|
: 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 index = (unsafeIndex + matches.length) % matches.length;
|
||||||
|
|
||||||
const newMatch = matches[index];
|
const newMatch = matches[index];
|
||||||
useLeapStore.setState({
|
useAppSearchStore.setState({
|
||||||
rawInput: newMatch.value,
|
rawInput: newMatch.value,
|
||||||
// searchInput: matchValue,
|
// searchInput: matchValue,
|
||||||
selectedMatch: newMatch
|
selectedMatch: newMatch,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@ -302,7 +279,7 @@ export const Leap = React.forwardRef(
|
|||||||
id="leap"
|
id="leap"
|
||||||
type="text"
|
type="text"
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
placeholder={selection ? '' : getPlaceholder()}
|
placeholder={selection ? '' : 'e.g., ~paldev or ~paldev/pals'}
|
||||||
// TODO: style placeholder text with 100% opacity.
|
// TODO: style placeholder text with 100% opacity.
|
||||||
// Not immediately clear how to do this within tailwind.
|
// 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"
|
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>
|
</form>
|
||||||
{menu === 'search' && (
|
{menu === 'search' && (
|
||||||
<Link
|
<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"
|
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)}
|
onClick={() => select(null)}
|
||||||
>
|
>
|
160
ui/src/nav/GetApps.tsx
Normal file
160
ui/src/nav/GetApps.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -22,7 +22,7 @@ import { Avatar } from '../components/Avatar';
|
|||||||
import { Dialog } from '../components/Dialog';
|
import { Dialog } from '../components/Dialog';
|
||||||
import { ErrorAlert } from '../components/ErrorAlert';
|
import { ErrorAlert } from '../components/ErrorAlert';
|
||||||
import { Help } from './Help';
|
import { Help } from './Help';
|
||||||
import { Leap } from './Leap';
|
import { AppSearch } from './AppSearch';
|
||||||
import { Notifications } from './notifications/Notifications';
|
import { Notifications } from './notifications/Notifications';
|
||||||
import { NotificationsLink } from './notifications/NotificationsLink';
|
import { NotificationsLink } from './notifications/NotificationsLink';
|
||||||
import { Search } from './Search';
|
import { Search } from './Search';
|
||||||
@ -30,6 +30,8 @@ import { SystemPreferences } from '../preferences/SystemPreferences';
|
|||||||
import { useSystemUpdate } from '../logic/useSystemUpdate';
|
import { useSystemUpdate } from '../logic/useSystemUpdate';
|
||||||
import { Bullet } from '../components/icons/Bullet';
|
import { Bullet } from '../components/icons/Bullet';
|
||||||
import { Cross } from '../components/icons/Cross';
|
import { Cross } from '../components/icons/Cross';
|
||||||
|
import MagnifyingGlass16Icon from '../components/icons/MagnifyingGlass16Icon';
|
||||||
|
import GetApps from './GetApps';
|
||||||
|
|
||||||
export interface MatchItem {
|
export interface MatchItem {
|
||||||
url: string;
|
url: string;
|
||||||
@ -38,7 +40,7 @@ export interface MatchItem {
|
|||||||
display?: string;
|
display?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LeapStore {
|
interface AppSearchStore {
|
||||||
rawInput: string;
|
rawInput: string;
|
||||||
searchInput: string;
|
searchInput: string;
|
||||||
matches: MatchItem[];
|
matches: MatchItem[];
|
||||||
@ -47,7 +49,7 @@ interface LeapStore {
|
|||||||
select: (selection: React.ReactNode, input?: string) => void;
|
select: (selection: React.ReactNode, input?: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useLeapStore = create<LeapStore>((set) => ({
|
export const useAppSearchStore = create<AppSearchStore>((set) => ({
|
||||||
rawInput: '',
|
rawInput: '',
|
||||||
searchInput: '',
|
searchInput: '',
|
||||||
matches: [],
|
matches: [],
|
||||||
@ -61,7 +63,7 @@ export const useLeapStore = create<LeapStore>((set) => ({
|
|||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
window.leap = useLeapStore.getState;
|
window.appSearch = useAppSearchStore.getState;
|
||||||
|
|
||||||
export type MenuState =
|
export type MenuState =
|
||||||
| 'closed'
|
| 'closed'
|
||||||
@ -99,7 +101,7 @@ export const SystemPrefsLink = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to="/leap/system-preferences" className="relative flex-none">
|
<Link to="/system-preferences" className="relative flex-none">
|
||||||
<Avatar shipName={window.ship} size="nav" />
|
<Avatar shipName={window.ship} size="nav" />
|
||||||
{systemBlocked && (
|
{systemBlocked && (
|
||||||
<Bullet
|
<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 }) => {
|
export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
|
||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const navRef = useRef<HTMLDivElement>(null);
|
const navRef = useRef<HTMLDivElement>(null);
|
||||||
const dialogNavRef = useRef<HTMLDivElement>(null);
|
const dialogNavRef = useRef<HTMLDivElement>(null);
|
||||||
const systemMenuOpen = useRouteMatch('/leap/system-preferences');
|
|
||||||
const { systemBlocked } = useSystemUpdate();
|
const { systemBlocked } = useSystemUpdate();
|
||||||
const [dialogContentOpen, setDialogContentOpen] = useState(false);
|
const [dialogContentOpen, setDialogContentOpen] = useState(false);
|
||||||
const select = useLeapStore((state) => state.select);
|
const select = useAppSearchStore((state) => state.select);
|
||||||
|
|
||||||
const menuState = menu || 'closed';
|
const menuState = menu || 'closed';
|
||||||
const isOpen = menuState !== 'upgrading' && menuState !== 'closed';
|
const isOpen = menuState !== 'upgrading' && menuState !== 'closed';
|
||||||
@ -172,13 +187,16 @@ export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
|
|||||||
navOpen={isOpen}
|
navOpen={isOpen}
|
||||||
notificationsOpen={menu === 'notifications'}
|
notificationsOpen={menu === 'notifications'}
|
||||||
/>
|
/>
|
||||||
<Leap
|
{menuState === 'search' ? (
|
||||||
|
<AppSearch
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
menu={menuState}
|
menu={menuState}
|
||||||
dropdown="leap-items"
|
dropdown="leap-items"
|
||||||
navOpen={isOpen}
|
navOpen={isOpen}
|
||||||
systemMenuOpen={!!systemMenuOpen}
|
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<GetAppsLink menuState={menuState} />
|
||||||
|
)}
|
||||||
</Portal.Root>
|
</Portal.Root>
|
||||||
<div
|
<div
|
||||||
ref={navRef}
|
ref={navRef}
|
||||||
@ -213,13 +231,11 @@ export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
|
|||||||
role="listbox"
|
role="listbox"
|
||||||
>
|
>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/leap/notifications" component={Notifications} />
|
<Route path="/notifications" component={Notifications} />
|
||||||
<Route
|
<Route path="/system-preferences" component={SystemPreferences} />
|
||||||
path="/leap/system-preferences"
|
<Route path="/help-and-support" component={Help} />
|
||||||
component={SystemPreferences}
|
<Route path="/get-apps" component={GetApps} />
|
||||||
/>
|
<Route path={['/search']} component={Search} />
|
||||||
<Route path="/leap/help-and-support" component={Help} />
|
|
||||||
<Route path={['/leap/search', '/leap']} component={Search} />
|
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
@ -9,7 +9,7 @@ import { usePike } from '../state/kiln';
|
|||||||
import { disableDefault, handleDropdownLink } from '../state/util';
|
import { disableDefault, handleDropdownLink } from '../state/util';
|
||||||
import { useMedia } from '../logic/useMedia';
|
import { useMedia } from '../logic/useMedia';
|
||||||
import { Cross } from '../components/icons/Cross';
|
import { Cross } from '../components/icons/Cross';
|
||||||
import { useLeapStore } from './Nav';
|
import { useAppSearchStore } from './Nav';
|
||||||
|
|
||||||
type SystemMenuProps = HTMLAttributes<HTMLButtonElement> & {
|
type SystemMenuProps = HTMLAttributes<HTMLButtonElement> & {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -33,7 +33,7 @@ export const SystemMenu = ({
|
|||||||
const garden = usePike(window.desk);
|
const garden = usePike(window.desk);
|
||||||
const hash = garden ? getHash(garden) : null;
|
const hash = garden ? getHash(garden) : null;
|
||||||
const isMobile = useMedia('(max-width: 639px)');
|
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 clearSelection = useCallback(() => select(null), [select]);
|
||||||
|
|
||||||
const copyHash = useCallback(
|
const copyHash = useCallback(
|
||||||
|
@ -2,7 +2,7 @@ import classNames from 'classnames';
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { Link, LinkProps } from 'react-router-dom';
|
import { Link, LinkProps } from 'react-router-dom';
|
||||||
import { Cross } from '../../components/icons/Cross';
|
import { Cross } from '../../components/icons/Cross';
|
||||||
import { useLeapStore } from '../Nav';
|
import { useAppSearchStore } from '../Nav';
|
||||||
import { SettingsState, useSettingsState } from '../../state/settings';
|
import { SettingsState, useSettingsState } from '../../state/settings';
|
||||||
import BellIcon from '../../components/icons/BellIcon';
|
import BellIcon from '../../components/icons/BellIcon';
|
||||||
import { useNotifications } from './useNotifications';
|
import { useNotifications } from './useNotifications';
|
||||||
@ -32,12 +32,12 @@ export const NotificationsLink = ({ navOpen, notificationsOpen }: NotificationsL
|
|||||||
const dnd = useSettingsState(selDnd);
|
const dnd = useSettingsState(selDnd);
|
||||||
const { count } = useNotifications();
|
const { count } = useNotifications();
|
||||||
const state = getNotificationsState(notificationsOpen, count, dnd);
|
const state = getNotificationsState(notificationsOpen, count, dnd);
|
||||||
const select = useLeapStore((s) => s.select);
|
const select = useAppSearchStore((s) => s.select);
|
||||||
const clearSelection = useCallback(() => select(null), [select]);
|
const clearSelection = useCallback(() => select(null), [select]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
to={state === 'open' ? '/' : '/leap/notifications'}
|
to={state === 'open' ? '/' : '/notifications'}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'relative z-50 flex-none circle-button h4 default-ring',
|
'relative z-50 flex-none circle-button h4 default-ring',
|
||||||
navOpen && 'text-opacity-60',
|
navOpen && 'text-opacity-60',
|
||||||
|
@ -4,7 +4,7 @@ import fuzzy from 'fuzzy';
|
|||||||
import { Treaty } from '@urbit/api';
|
import { Treaty } from '@urbit/api';
|
||||||
import { ShipName } from '../../components/ShipName';
|
import { ShipName } from '../../components/ShipName';
|
||||||
import { useAllyTreaties } from '../../state/docket';
|
import { useAllyTreaties } from '../../state/docket';
|
||||||
import { useLeapStore } from '../Nav';
|
import { useAppSearchStore } from '../Nav';
|
||||||
import { AppList } from '../../components/AppList';
|
import { AppList } from '../../components/AppList';
|
||||||
import { addRecentDev } from './Home';
|
import { addRecentDev } from './Home';
|
||||||
import { Spinner } from '../../components/Spinner';
|
import { Spinner } from '../../components/Spinner';
|
||||||
@ -12,7 +12,7 @@ import { Spinner } from '../../components/Spinner';
|
|||||||
type AppsProps = RouteComponentProps<{ ship: string }>;
|
type AppsProps = RouteComponentProps<{ ship: string }>;
|
||||||
|
|
||||||
export const Apps = ({ match }: AppsProps) => {
|
export const Apps = ({ match }: AppsProps) => {
|
||||||
const { searchInput, selectedMatch, select } = useLeapStore((state) => ({
|
const { searchInput, selectedMatch, select } = useAppSearchStore((state) => ({
|
||||||
searchInput: state.searchInput,
|
searchInput: state.searchInput,
|
||||||
select: state.select,
|
select: state.select,
|
||||||
selectedMatch: state.selectedMatch
|
selectedMatch: state.selectedMatch
|
||||||
@ -61,7 +61,7 @@ export const Apps = ({ match }: AppsProps) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (results) {
|
if (results) {
|
||||||
useLeapStore.setState({
|
useAppSearchStore.setState({
|
||||||
matches: results.map((r) => ({
|
matches: results.map((r) => ({
|
||||||
url: getAppPath(r),
|
url: getAppPath(r),
|
||||||
openInNewTab: false,
|
openInNewTab: false,
|
||||||
|
@ -3,7 +3,7 @@ import create from 'zustand';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
import { MatchItem, useLeapStore } from '../Nav';
|
import { MatchItem, useAppSearchStore } from '../Nav';
|
||||||
import { providerMatch } from './Providers';
|
import { providerMatch } from './Providers';
|
||||||
import { AppList } from '../../components/AppList';
|
import { AppList } from '../../components/AppList';
|
||||||
import { ProviderList } from '../../components/ProviderList';
|
import { ProviderList } from '../../components/ProviderList';
|
||||||
@ -88,7 +88,7 @@ function getApps(desks: string[], charges: ChargesWithDesks) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Home = () => {
|
export const Home = () => {
|
||||||
const selectedMatch = useLeapStore((state) => state.selectedMatch);
|
const selectedMatch = useAppSearchStore((state) => state.selectedMatch);
|
||||||
const { recentApps, recentDevs } = useRecentsStore();
|
const { recentApps, recentDevs } = useRecentsStore();
|
||||||
const charges = useCharges();
|
const charges = useCharges();
|
||||||
const groups = charges?.landscape;
|
const groups = charges?.landscape;
|
||||||
@ -108,7 +108,7 @@ export const Home = () => {
|
|||||||
}));
|
}));
|
||||||
const devs = recentDevs.map(providerMatch);
|
const devs = recentDevs.map(providerMatch);
|
||||||
|
|
||||||
useLeapStore.setState({
|
useAppSearchStore.setState({
|
||||||
matches: ([] as MatchItem[]).concat(appMatches, devs)
|
matches: ([] as MatchItem[]).concat(appMatches, devs)
|
||||||
});
|
});
|
||||||
}, [recentApps, recentDevs]);
|
}, [recentApps, recentDevs]);
|
||||||
|
@ -3,7 +3,7 @@ import { RouteComponentProps } from 'react-router-dom';
|
|||||||
import fuzzy from 'fuzzy';
|
import fuzzy from 'fuzzy';
|
||||||
import { Provider, deSig } from '@urbit/api';
|
import { Provider, deSig } from '@urbit/api';
|
||||||
import * as ob from 'urbit-ob';
|
import * as ob from 'urbit-ob';
|
||||||
import { MatchItem, useLeapStore } from '../Nav';
|
import { MatchItem, useAppSearchStore } from '../Nav';
|
||||||
import { useAllies, useCharges } from '../../state/docket';
|
import { useAllies, useCharges } from '../../state/docket';
|
||||||
import { ProviderList } from '../../components/ProviderList';
|
import { ProviderList } from '../../components/ProviderList';
|
||||||
import useContactState from '../../state/contact';
|
import useContactState from '../../state/contact';
|
||||||
@ -19,7 +19,7 @@ export function providerMatch(provider: Provider | string): MatchItem {
|
|||||||
return {
|
return {
|
||||||
value,
|
value,
|
||||||
display,
|
display,
|
||||||
url: `/leap/search/${value}/apps`,
|
url: `/search/${value}/apps`,
|
||||||
openInNewTab: false
|
openInNewTab: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -34,7 +34,7 @@ function fuzzySort(search: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Providers = ({ match }: ProvidersProps) => {
|
export const Providers = ({ match }: ProvidersProps) => {
|
||||||
const selectedMatch = useLeapStore((state) => state.selectedMatch);
|
const selectedMatch = useAppSearchStore((state) => state.selectedMatch);
|
||||||
const provider = match?.params.ship;
|
const provider = match?.params.ship;
|
||||||
const contacts = useContactState((s) => s.contacts);
|
const contacts = useContactState((s) => s.contacts);
|
||||||
const charges = useCharges();
|
const charges = useCharges();
|
||||||
@ -87,7 +87,7 @@ export const Providers = ({ match }: ProvidersProps) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (search) {
|
if (search) {
|
||||||
useLeapStore.setState({ rawInput: search });
|
useAppSearchStore.setState({ rawInput: search });
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ export const Providers = ({ match }: ProvidersProps) => {
|
|||||||
const newProviderMatches = isValidPatp
|
const newProviderMatches = isValidPatp
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
url: `/leap/search/${patp}/apps`,
|
url: `/search/${patp}/apps`,
|
||||||
value: patp,
|
value: patp,
|
||||||
display: patp,
|
display: patp,
|
||||||
openInNewTab: false
|
openInNewTab: false
|
||||||
@ -114,7 +114,7 @@ export const Providers = ({ match }: ProvidersProps) => {
|
|||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
useLeapStore.setState({
|
useAppSearchStore.setState({
|
||||||
matches: ([] as MatchItem[]).concat(appMatches, providerMatches, newProviderMatches)
|
matches: ([] as MatchItem[]).concat(appMatches, providerMatches, newProviderMatches)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,10 @@ import { Spinner } from '../../components/Spinner';
|
|||||||
import useDocketState, { useCharge, useTreaty } from '../../state/docket';
|
import useDocketState, { useCharge, useTreaty } from '../../state/docket';
|
||||||
import { usePike } from '../../state/kiln';
|
import { usePike } from '../../state/kiln';
|
||||||
import { getAppName } from '../../state/util';
|
import { getAppName } from '../../state/util';
|
||||||
import { useLeapStore } from '../Nav';
|
import { useAppSearchStore } from '../Nav';
|
||||||
|
|
||||||
export const TreatyInfo = () => {
|
export const TreatyInfo = () => {
|
||||||
const select = useLeapStore((state) => state.select);
|
const select = useAppSearchStore((state) => state.select);
|
||||||
const { host, desk } = useParams<{ host: string; desk: string }>();
|
const { host, desk } = useParams<{ host: string; desk: string }>();
|
||||||
const treaty = useTreaty(host, desk);
|
const treaty = useTreaty(host, desk);
|
||||||
const pike = usePike(desk);
|
const pike = usePike(desk);
|
||||||
@ -23,7 +23,7 @@ export const TreatyInfo = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
select(<>{name}</>);
|
select(<>{name}</>);
|
||||||
useLeapStore.setState({ matches: [] });
|
useAppSearchStore.setState({ matches: [] });
|
||||||
}, [name]);
|
}, [name]);
|
||||||
|
|
||||||
if (!treaty) {
|
if (!treaty) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useLeapStore } from './nav/Nav';
|
import { useAppSearchStore } from './nav/Nav';
|
||||||
import { useRecentsStore } from './nav/search/Home';
|
import { useRecentsStore } from './nav/search/Home';
|
||||||
import useDocketState from './state/docket';
|
import useDocketState from './state/docket';
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ declare global {
|
|||||||
desk: string;
|
desk: string;
|
||||||
recents: typeof useRecentsStore.getState;
|
recents: typeof useRecentsStore.getState;
|
||||||
docket: typeof useDocketState.getState;
|
docket: typeof useDocketState.getState;
|
||||||
leap: typeof useLeapStore.getState;
|
appSearch: typeof useAppSearchStore.getState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +66,7 @@ const base = new Theme().addColors({
|
|||||||
},
|
},
|
||||||
blue: {
|
blue: {
|
||||||
DEFAULT: '#008EFF',
|
DEFAULT: '#008EFF',
|
||||||
|
soft: '#E5F4FF',
|
||||||
50: '#EFF9FF',
|
50: '#EFF9FF',
|
||||||
100: '#C8EDFF',
|
100: '#C8EDFF',
|
||||||
200: '#A0E1FF',
|
200: '#A0E1FF',
|
||||||
|
Loading…
Reference in New Issue
Block a user