mirror of
https://github.com/tloncorp/landscape.git
synced 2025-01-07 17:08:53 +03:00
Merge pull request #47 from urbit/ja/tile-fixes
garden: various maladies
This commit is contained in:
commit
d8e5c8583f
@ -9,44 +9,33 @@ export const SecurityPrefs = () => {
|
|||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<div className="inner-section space-y-8">
|
||||||
open
|
<h2 className="h4">Log Out</h2>
|
||||||
onOpenChange={(open) => !open && push('/leap/system-preferences')}
|
<div className="flex flex-1 flex-col justify-center space-y-6">
|
||||||
>
|
<div className="flex flex-col space-y-3">
|
||||||
<DialogContent containerClass="w-1/3" showClose={false}>
|
<p className="leading-5">
|
||||||
<h3 className="h4 mb-6 flex items-center">Log Out</h3>
|
Logging out of Landscape will additionally log you out of any
|
||||||
<div className="flex flex-1 flex-col justify-center space-y-6">
|
applications installed on your urbit.
|
||||||
<div className="flex flex-col space-y-3">
|
</p>
|
||||||
<p className="leading-5">
|
<p className="leading-5">
|
||||||
Logging out of Landscape will additionally log you out of any
|
You'll need to log into your urbit again in order to access its
|
||||||
applications installed on your urbit.
|
apps.
|
||||||
</p>
|
</p>
|
||||||
<p className="leading-5">
|
|
||||||
You'll need to log into your urbit again in order to access
|
|
||||||
its apps.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Checkbox
|
|
||||||
defaultChecked={false}
|
|
||||||
checked={allSessions}
|
|
||||||
onCheckedChange={() => setAllSessions((prev) => !prev)}
|
|
||||||
>
|
|
||||||
Log out of all connected sessions.
|
|
||||||
</Checkbox>
|
|
||||||
<div className="flex justify-end space-x-2">
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
onClick={() => push('/leap/system-preferences')}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<form method="post" action="/~/logout">
|
|
||||||
{allSessions && <input type="hidden" name="all" />}
|
|
||||||
<Button>Logout</Button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
<Checkbox
|
||||||
</Dialog>
|
defaultChecked={false}
|
||||||
|
checked={allSessions}
|
||||||
|
onCheckedChange={() => setAllSessions((prev) => !prev)}
|
||||||
|
>
|
||||||
|
Log out of all connected sessions
|
||||||
|
</Checkbox>
|
||||||
|
<div className="flex justify-end space-x-2">
|
||||||
|
<form method="post" action="/~/logout">
|
||||||
|
{allSessions && <input type="hidden" name="all" />}
|
||||||
|
<Button>Log Out</Button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -107,7 +107,7 @@ export const SystemPreferences = (
|
|||||||
>
|
>
|
||||||
<div className="system-preferences-grid bg-gray-50">
|
<div className="system-preferences-grid bg-gray-50">
|
||||||
<Route exact={isMobile} path={match.url}>
|
<Route exact={isMobile} path={match.url}>
|
||||||
<aside className="system-preferences-aside min-h-fit max-h-[calc(100vh-6.25rem)] w-full min-w-60 flex flex-col border-r-2 border-gray-50 bg-white py-4 font-semibold text-black sm:w-auto sm:py-8 sm:text-gray-600">
|
<aside className="system-preferences-aside min-h-fit flex max-h-[calc(100vh-6.25rem)] w-full min-w-60 flex-col border-r-2 border-gray-50 bg-white py-4 font-semibold text-black sm:w-auto sm:py-8 sm:text-gray-600">
|
||||||
<nav className="flex flex-col px-2 sm:px-6">
|
<nav className="flex flex-col px-2 sm:px-6">
|
||||||
<SearchSystemPreferences subUrl={subUrl} />
|
<SearchSystemPreferences subUrl={subUrl} />
|
||||||
<span className="pt-1 pl-2 pb-3 text-sm font-semibold text-gray-400">
|
<span className="pt-1 pl-2 pb-3 text-sm font-semibold text-gray-400">
|
||||||
@ -236,6 +236,7 @@ export const SystemPreferences = (
|
|||||||
component={AttentionAndPrivacy}
|
component={AttentionAndPrivacy}
|
||||||
/>
|
/>
|
||||||
<Route path={[`${match.url}/storage`]} component={StoragePrefs} />
|
<Route path={[`${match.url}/storage`]} component={StoragePrefs} />
|
||||||
|
<Route path={`${match.url}/security`} component={SecurityPrefs} />
|
||||||
<Route
|
<Route
|
||||||
path={[`${match.url}/system-updates`, match.url]}
|
path={[`${match.url}/system-updates`, match.url]}
|
||||||
component={AboutSystem}
|
component={AboutSystem}
|
||||||
@ -249,7 +250,6 @@ export const SystemPreferences = (
|
|||||||
</Link>
|
</Link>
|
||||||
</section>
|
</section>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${match.url}/security`} component={SecurityPrefs} />
|
|
||||||
</div>
|
</div>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
|
@ -46,7 +46,8 @@ export const Tile: FunctionComponent<TileProps> = ({ charge, desk, disabled = fa
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'group absolute font-semibold w-full h-full rounded-3xl default-ring focus-visible:ring-4 overflow-hidden',
|
'default-ring group absolute h-full w-full overflow-hidden rounded-3xl font-semibold focus-visible:ring-4',
|
||||||
|
suspended && 'opacity-50 grayscale',
|
||||||
isDragging && 'opacity-0',
|
isDragging && 'opacity-0',
|
||||||
lightText && active && !loading ? 'text-gray-200' : 'text-gray-800',
|
lightText && active && !loading ? 'text-gray-200' : 'text-gray-800',
|
||||||
!active && 'cursor-default'
|
!active && 'cursor-default'
|
||||||
@ -56,15 +57,21 @@ export const Tile: FunctionComponent<TileProps> = ({ charge, desk, disabled = fa
|
|||||||
onAuxClick={() => addRecentApp(desk)}
|
onAuxClick={() => addRecentApp(desk)}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div className="absolute z-10 top-4 left-4 sm:top-6 sm:left-6 flex items-center">
|
<div className="absolute top-4 left-4 z-10 flex items-center sm:top-6 sm:left-6">
|
||||||
{pike?.zest === 'held' && !disabled && (
|
{pike?.zest === 'held' && !disabled && (
|
||||||
<Bullet className="w-4 h-4 text-orange-500 dark:text-black" />
|
<Bullet className="h-4 w-4 text-orange-500 dark:text-black" />
|
||||||
)}
|
)}
|
||||||
{!active && (
|
{!active && (
|
||||||
<>
|
<>
|
||||||
{loading && <Spinner className="h-6 w-6 mr-2" />}
|
{loading && <Spinner className="mr-2 h-6 w-6" />}
|
||||||
<span className="text-gray-500">
|
<span className="text-gray-500">
|
||||||
{suspended ? 'Suspended' : loading ? 'Installing' : hung ? 'Errored' : null}
|
{suspended
|
||||||
|
? 'Suspended'
|
||||||
|
: loading
|
||||||
|
? 'Installing'
|
||||||
|
: hung
|
||||||
|
? 'Errored'
|
||||||
|
: null}
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -74,18 +81,22 @@ export const Tile: FunctionComponent<TileProps> = ({ charge, desk, disabled = fa
|
|||||||
chad={chad}
|
chad={chad}
|
||||||
menuColor={active ? menuColor : suspendMenuColor}
|
menuColor={active ? menuColor : suspendMenuColor}
|
||||||
lightText={lightText}
|
lightText={lightText}
|
||||||
className="absolute z-10 top-3 right-3 sm:top-5 sm:right-5 opacity-0 pointer-coarse:opacity-100 hover-none:opacity-100 focus:opacity-100 group-hover:opacity-100"
|
className="absolute top-3 right-3 z-10 opacity-0 focus:opacity-100 group-hover:opacity-100 pointer-coarse:opacity-100 hover-none:opacity-100 sm:top-5 sm:right-5"
|
||||||
/>
|
/>
|
||||||
{title && (
|
{title && (
|
||||||
<div
|
<div
|
||||||
className="h4 absolute z-10 bottom-[8%] left-[5%] sm:bottom-7 sm:left-5 py-1 px-3 rounded-lg"
|
className="h4 absolute bottom-[8%] left-[5%] z-10 rounded-lg py-1 px-3 sm:bottom-7 sm:left-5"
|
||||||
style={{ backgroundColor }}
|
style={{ backgroundColor }}
|
||||||
>
|
>
|
||||||
<h3 className="mix-blend-hard-light">{title}</h3>
|
<h3 className="mix-blend-hard-light">{title}</h3>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{image && !loading && (
|
{image && !loading && (
|
||||||
<img className="absolute top-0 left-0 h-full w-full object-cover" src={image} alt="" />
|
<img
|
||||||
|
className="absolute top-0 left-0 h-full w-full object-cover"
|
||||||
|
src={image}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
@ -6,6 +6,7 @@ import { Link } from 'react-router-dom';
|
|||||||
import { Chad, chadIsRunning } from '@urbit/api';
|
import { Chad, chadIsRunning } from '@urbit/api';
|
||||||
import useDocketState from '../state/docket';
|
import useDocketState from '../state/docket';
|
||||||
import { disableDefault, handleDropdownLink } from '../state/util';
|
import { disableDefault, handleDropdownLink } from '../state/util';
|
||||||
|
import { useMedia } from '../logic/useMedia';
|
||||||
|
|
||||||
export interface TileMenuProps {
|
export interface TileMenuProps {
|
||||||
desk: string;
|
desk: string;
|
||||||
@ -32,25 +33,32 @@ const Item = React.forwardRef(({ children, ...props }, ref) => (
|
|||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...props}
|
{...props}
|
||||||
className="block w-full px-4 py-3 leading-none rounded mix-blend-hard-light select-none default-ring ring-gray-600"
|
className="default-ring block w-full select-none rounded px-4 py-3 leading-none mix-blend-hard-light ring-gray-600"
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
)) as ItemComponent;
|
)) as ItemComponent;
|
||||||
|
|
||||||
export const TileMenu = ({ desk, chad, menuColor, lightText, className }: TileMenuProps) => {
|
export const TileMenu = ({
|
||||||
|
desk,
|
||||||
|
chad,
|
||||||
|
menuColor,
|
||||||
|
lightText,
|
||||||
|
className,
|
||||||
|
}: TileMenuProps) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const toggleDocket = useDocketState((s) => s.toggleDocket);
|
const toggleDocket = useDocketState((s) => s.toggleDocket);
|
||||||
const menuBg = { backgroundColor: menuColor };
|
const menuBg = { backgroundColor: menuColor };
|
||||||
const linkOnSelect = useCallback(handleDropdownLink(setOpen), []);
|
const linkOnSelect = useCallback(handleDropdownLink(setOpen), []);
|
||||||
const active = chadIsRunning(chad);
|
const active = chadIsRunning(chad);
|
||||||
const suspended = 'suspend' in chad;
|
const suspended = 'suspend' in chad;
|
||||||
|
const isMobile = useMedia('(any-pointer: coarse)');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu.Root open={open} onOpenChange={(isOpen) => setOpen(isOpen)}>
|
<DropdownMenu.Root open={open} onOpenChange={(isOpen) => setOpen(isOpen)}>
|
||||||
<DropdownMenu.Trigger
|
<DropdownMenu.Trigger
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'flex items-center justify-center w-8 h-8 rounded-full transition-opacity duration-75 default-ring',
|
'default-ring flex h-8 w-8 items-center justify-center rounded-full transition-opacity duration-75',
|
||||||
open && 'opacity-100',
|
open && 'opacity-100',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
@ -58,7 +66,10 @@ export const TileMenu = ({ desk, chad, menuColor, lightText, className }: TileMe
|
|||||||
// onMouseOver={() => queryClient.setQueryData(['apps', name], app)}
|
// onMouseOver={() => queryClient.setQueryData(['apps', name], app)}
|
||||||
>
|
>
|
||||||
<MenuIcon
|
<MenuIcon
|
||||||
className={classNames('w-4 h-4 mix-blend-hard-light', lightText && 'text-gray-100')}
|
className={classNames(
|
||||||
|
'h-4 w-4 mix-blend-hard-light',
|
||||||
|
lightText && 'text-gray-100'
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
<span className="sr-only">Menu</span>
|
<span className="sr-only">Menu</span>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
@ -72,23 +83,40 @@ export const TileMenu = ({ desk, chad, menuColor, lightText, className }: TileMe
|
|||||||
style={menuBg}
|
style={menuBg}
|
||||||
>
|
>
|
||||||
<DropdownMenu.Group>
|
<DropdownMenu.Group>
|
||||||
<Item as={Link} to={`/app/${desk}`} onSelect={linkOnSelect}>
|
<Item
|
||||||
|
as={Link}
|
||||||
|
to={`/app/${desk}`}
|
||||||
|
onSelect={isMobile ? (e) => e.preventDefault() : linkOnSelect}
|
||||||
|
>
|
||||||
App Info
|
App Info
|
||||||
</Item>
|
</Item>
|
||||||
</DropdownMenu.Group>
|
</DropdownMenu.Group>
|
||||||
<DropdownMenu.Separator className="-mx-4 my-2 border-t-2 border-solid border-gray-600 mix-blend-soft-light" />
|
<DropdownMenu.Separator className="-mx-4 my-2 border-t-2 border-solid border-gray-600 mix-blend-soft-light" />
|
||||||
<DropdownMenu.Group>
|
<DropdownMenu.Group>
|
||||||
{active && (
|
{active && (
|
||||||
<Item as={Link} to={`/app/${desk}/suspend`} onSelect={linkOnSelect}>
|
<Item
|
||||||
|
as={Link}
|
||||||
|
to={`/app/${desk}/suspend`}
|
||||||
|
onSelect={isMobile ? (e) => e.preventDefault() : linkOnSelect}
|
||||||
|
>
|
||||||
Suspend App
|
Suspend App
|
||||||
</Item>
|
</Item>
|
||||||
)}
|
)}
|
||||||
{suspended && <Item onSelect={() => toggleDocket(desk)}>Resume App</Item>}
|
{suspended && (
|
||||||
<Item as={Link} to={`/app/${desk}/remove`} onSelect={linkOnSelect}>
|
<Item onSelect={() => toggleDocket(desk)}>Resume App</Item>
|
||||||
|
)}
|
||||||
|
<Item
|
||||||
|
as={Link}
|
||||||
|
to={`/app/${desk}/remove`}
|
||||||
|
onSelect={isMobile ? (e) => e.preventDefault() : linkOnSelect}
|
||||||
|
>
|
||||||
Uninstall App
|
Uninstall App
|
||||||
</Item>
|
</Item>
|
||||||
</DropdownMenu.Group>
|
</DropdownMenu.Group>
|
||||||
<DropdownMenu.Arrow className="w-4 h-[10px] fill-current" style={{ color: menuColor }} />
|
<DropdownMenu.Arrow
|
||||||
|
className="h-[10px] w-4 fill-current"
|
||||||
|
style={{ color: menuColor }}
|
||||||
|
/>
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user