mirror of
https://github.com/urbit/shrub.git
synced 2025-01-03 01:54:43 +03:00
dialogs: updates to design and mobile tweaks
This commit is contained in:
parent
952d5c0c38
commit
1c421963d1
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import type * as Polymorphic from '@radix-ui/react-polymorphic';
|
||||
import classNames from 'classnames';
|
||||
|
||||
type ButtonVariant = 'primary' | 'secondary' | 'destructive';
|
||||
type ButtonVariant = 'primary' | 'secondary' | 'destructive' | 'alt-primary' | 'alt-secondary';
|
||||
|
||||
type PolymorphicButton = Polymorphic.ForwardRefComponent<
|
||||
'button',
|
||||
@ -12,9 +12,11 @@ type PolymorphicButton = Polymorphic.ForwardRefComponent<
|
||||
>;
|
||||
|
||||
const variants: Record<ButtonVariant, string> = {
|
||||
primary: 'text-white bg-blue-400',
|
||||
secondary: 'text-blue-400 bg-blue-100',
|
||||
destructive: 'text-white bg-red-400'
|
||||
primary: 'text-white bg-black',
|
||||
secondary: 'text-black bg-gray-100',
|
||||
destructive: 'text-white bg-red-400',
|
||||
'alt-primary': 'text-white bg-blue-400',
|
||||
'alt-secondary': 'text-blue-400 bg-blue-100'
|
||||
};
|
||||
|
||||
export const Button = React.forwardRef(
|
||||
|
@ -163,13 +163,13 @@ export const Nav: FunctionComponent<NavProps> = ({ menu }) => {
|
||||
<Dialog open={isOpen} onOpenChange={onDialogClose}>
|
||||
<DialogContent
|
||||
onOpenAutoFocus={onOpen}
|
||||
className="fixed top-0 left-[calc(50%)] w-[calc(100%-15px)] max-w-3xl px-4 text-gray-400 -translate-x-1/2 outline-none"
|
||||
className="fixed bottom-0 sm:top-0 left-[calc(50%-7.5px)] flex flex-col w-[calc(100%-15px)] max-w-3xl px-4 text-gray-400 -translate-x-1/2 outline-none"
|
||||
role="combobox"
|
||||
aria-controls="leap-items"
|
||||
aria-owns="leap-items"
|
||||
aria-expanded={isOpen}
|
||||
>
|
||||
<header ref={dialogNavRef} className="my-6" />
|
||||
<header ref={dialogNavRef} className="my-6 order-last sm:order-none" />
|
||||
<div
|
||||
id="leap-items"
|
||||
className="grid grid-rows-[fit-content(calc(100vh-7.5rem))] bg-white rounded-3xl overflow-hidden default-ring"
|
||||
|
@ -2,7 +2,8 @@ import { chadIsRunning } from '@urbit/api/docket';
|
||||
import clipboardCopy from 'clipboard-copy';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { PillButton } from '../../components/Button';
|
||||
import { Button, PillButton } from '../../components/Button';
|
||||
import { Dialog, DialogClose, DialogContent, DialogTrigger } from '../../components/Dialog';
|
||||
import { DocketHeader } from '../../components/DocketHeader';
|
||||
import { ShipName } from '../../components/ShipName';
|
||||
import { Spinner } from '../../components/Spinner';
|
||||
@ -49,23 +50,45 @@ export const AppInfo = () => {
|
||||
<DocketHeader docket={treaty}>
|
||||
<div className="col-span-2 md:col-span-1 flex items-center space-x-4">
|
||||
{installed && (
|
||||
<PillButton as="a" href={getAppHref(treaty.href)} target={treaty.title || '_blank'}>
|
||||
<PillButton
|
||||
variant="alt-primary"
|
||||
as="a"
|
||||
href={getAppHref(treaty.href)}
|
||||
target={treaty.title || '_blank'}
|
||||
>
|
||||
Open App
|
||||
</PillButton>
|
||||
)}
|
||||
{!installed && (
|
||||
<PillButton onClick={installApp}>
|
||||
{installing ? (
|
||||
<>
|
||||
<Spinner />
|
||||
<span className="sr-only">Installing...</span>
|
||||
</>
|
||||
) : (
|
||||
'Get App'
|
||||
)}
|
||||
</PillButton>
|
||||
<Dialog>
|
||||
<DialogTrigger as={PillButton} variant="alt-primary">
|
||||
{installing ? (
|
||||
<>
|
||||
<Spinner />
|
||||
<span className="sr-only">Installing...</span>
|
||||
</>
|
||||
) : (
|
||||
'Get App'
|
||||
)}
|
||||
</DialogTrigger>
|
||||
<DialogContent showClose={false} className="max-w-[400px] space-y-6">
|
||||
<h2 className="h4">Install “{treaty.title}”</h2>
|
||||
<p className="text-base tracking-tight pr-6">
|
||||
This application will be able to view and interact with the contents of your
|
||||
Urbit. Only install if you trust the developer.
|
||||
</p>
|
||||
<div className="flex space-x-6">
|
||||
<DialogClose as={Button} variant="secondary">
|
||||
Cancel
|
||||
</DialogClose>
|
||||
<DialogClose as={Button} onClick={installApp}>
|
||||
Get “{treaty.title}”
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
<PillButton variant="secondary" onClick={copyApp}>
|
||||
<PillButton variant="alt-secondary" onClick={copyApp}>
|
||||
Copy App Link
|
||||
</PillButton>
|
||||
</div>
|
||||
|
@ -26,14 +26,14 @@ export const Grid: FunctionComponent<GridProps> = ({ match }) => {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<header className="sticky top-0 left-0 z-30 flex justify-center w-full bg-white">
|
||||
<header className="fixed sm:sticky bottom-0 sm:bottom-auto sm:top-0 left-0 z-30 flex justify-center w-full bg-white">
|
||||
<Nav menu={match.params.menu} />
|
||||
</header>
|
||||
|
||||
<main className="h-full w-full flex justify-center pt-24 pb-32 relative z-0">
|
||||
<main className="h-full w-full flex justify-center pt-4 md:pt-16 pb-32 relative z-0">
|
||||
{!chargesLoaded && <span>Loading...</span>}
|
||||
{chargesLoaded && (
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-6 px-4 md:px-8 w-full max-w-6xl">
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-4 px-4 md:px-8 w-full max-w-6xl">
|
||||
{charges &&
|
||||
map(omit(charges, 'grid'), (charge, desk) => (
|
||||
<Tile key={desk} charge={charge} desk={desk} />
|
||||
|
@ -1,9 +1,7 @@
|
||||
import create from 'zustand';
|
||||
import produce from 'immer';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { omit } from 'lodash-es';
|
||||
import api from './api';
|
||||
import { mockAllies, mockCharges, mockTreaties } from './mock-data';
|
||||
import { mapValues, omit, pick } from 'lodash-es';
|
||||
import {
|
||||
Allies,
|
||||
Charge,
|
||||
@ -21,8 +19,9 @@ import {
|
||||
docketInstall,
|
||||
ChargeUpdate
|
||||
} from '@urbit/api/docket';
|
||||
import _ from 'lodash';
|
||||
import {kilnRevive, kilnSuspend} from '@urbit/api/hood';
|
||||
import { kilnRevive, kilnSuspend } from '@urbit/api/hood';
|
||||
import api from './api';
|
||||
import { mockAllies, mockCharges, mockTreaties } from './mock-data';
|
||||
|
||||
const useMockData = import.meta.env.MODE === 'mock';
|
||||
|
||||
@ -67,8 +66,10 @@ const useDocketState = create<DocketState>((set, get) => ({
|
||||
return allies;
|
||||
},
|
||||
fetchAllyTreaties: async (ally: string) => {
|
||||
let treaties = useMockData ? mockTreaties : (await api.scry<TreatyUpdateIni>(scryAllyTreaties(ally))).ini;
|
||||
treaties = _.mapValues(treaties, normalizeDocket);
|
||||
let treaties = useMockData
|
||||
? mockTreaties
|
||||
: (await api.scry<TreatyUpdateIni>(scryAllyTreaties(ally))).ini;
|
||||
treaties = mapValues(treaties, normalizeDocket);
|
||||
set((s) => ({ treaties: { ...s.treaties, ...treaties } }));
|
||||
return treaties;
|
||||
},
|
||||
@ -97,10 +98,9 @@ const useDocketState = create<DocketState>((set, get) => ({
|
||||
throw new Error('Bad install');
|
||||
}
|
||||
if (useMockData) {
|
||||
|
||||
set((state) => addCharge(state, desk, {...treaty, chad: { install: null }}));
|
||||
set((state) => addCharge(state, desk, { ...treaty, chad: { install: null } }));
|
||||
await new Promise<void>((res) => setTimeout(() => res(), 5000));
|
||||
set((state) => addCharge(state, desk, {...treaty, chad: { glob: null }}));
|
||||
set((state) => addCharge(state, desk, { ...treaty, chad: { glob: null } }));
|
||||
}
|
||||
|
||||
return api.poke(docketInstall(ship, desk));
|
||||
@ -117,7 +117,7 @@ const useDocketState = create<DocketState>((set, get) => ({
|
||||
});
|
||||
},
|
||||
toggleDocket: async (desk: string) => {
|
||||
if(useMockData) {
|
||||
if (useMockData) {
|
||||
set(
|
||||
produce((draft) => {
|
||||
const charge = draft.charges[desk];
|
||||
@ -127,11 +127,11 @@ const useDocketState = create<DocketState>((set, get) => ({
|
||||
}
|
||||
const { charges } = get();
|
||||
const charge = charges[desk];
|
||||
if(!charge) {
|
||||
if (!charge) {
|
||||
return;
|
||||
}
|
||||
const suspended = 'suspend' in charge.chad;
|
||||
if(suspended) {
|
||||
if (suspended) {
|
||||
await api.poke(kilnRevive(desk));
|
||||
} else {
|
||||
await api.poke(kilnSuspend(desk));
|
||||
@ -175,15 +175,14 @@ api.subscribe({
|
||||
path: '/charges',
|
||||
event: (data: ChargeUpdate) => {
|
||||
useDocketState.setState((state) => {
|
||||
|
||||
if ('add-charge' in data) {
|
||||
const { desk, charge } = data['add-charge']
|
||||
return addCharge(state, desk, charge)
|
||||
const { desk, charge } = data['add-charge'];
|
||||
return addCharge(state, desk, charge);
|
||||
}
|
||||
|
||||
if ('del-charge' in data) {
|
||||
const desk = data['del-charge'];
|
||||
return delCharge(state, desk)
|
||||
return delCharge(state, desk);
|
||||
}
|
||||
|
||||
return { charges: state.charges };
|
||||
@ -222,7 +221,7 @@ export function useAllyTreaties(ship: string) {
|
||||
useCallback(
|
||||
(s) => {
|
||||
const charter = s.allies[ship];
|
||||
return _.pick(s.treaties, ...(charter || []));
|
||||
return pick(s.treaties, ...(charter || []));
|
||||
},
|
||||
[ship]
|
||||
)
|
||||
@ -242,6 +241,6 @@ export function useTreaty(host: string, desk: string) {
|
||||
}
|
||||
|
||||
// xx useful for debugging
|
||||
//window.docket = useDocketState.getState;
|
||||
// window.docket = useDocketState.getState;
|
||||
|
||||
export default useDocketState;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import systemUrl from '../assets/system.png';
|
||||
import _ from 'lodash';
|
||||
import _ from 'lodash-es';
|
||||
import { Allies, Charges, DocketHrefGlob, Treaties, Treaty } from '@urbit/api/docket';
|
||||
import systemUrl from '../assets/system.png';
|
||||
|
||||
export const appMetaData: Pick<Treaty, 'cass' | 'hash' | 'website' | 'license' | 'version'> = {
|
||||
cass: '~2021.8.11..05.11.10..b721',
|
||||
@ -127,9 +127,8 @@ export const mockCharges: Charges = _.reduce(
|
||||
mockTreaties,
|
||||
(acc, val, key) => {
|
||||
const [, desk] = key.split('/');
|
||||
const chad = desk === 'uniswap'
|
||||
? { install: null } : { glob : null };
|
||||
if(desk === 'inbox') {
|
||||
const chad = { glob: null };
|
||||
if (desk === 'inbox') {
|
||||
return acc;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { Button } from '../components/Button';
|
||||
import { Dialog, DialogContent } from '../components/Dialog';
|
||||
import { Dialog, DialogClose, DialogContent } from '../components/Dialog';
|
||||
import useDocketState, { useCharges } from '../state/docket';
|
||||
|
||||
export const RemoveApp = () => {
|
||||
@ -14,19 +14,23 @@ export const RemoveApp = () => {
|
||||
// TODO: add optimistic updates
|
||||
const handleRemoveApp = useCallback(() => {
|
||||
uninstallDocket(desk);
|
||||
history.push('/');
|
||||
}, []);
|
||||
}, [desk]);
|
||||
|
||||
return (
|
||||
<Dialog open onOpenChange={(open) => !open && history.push('/')}>
|
||||
<DialogContent>
|
||||
<h1 className="h4 mb-9">Remove “{docket?.title || ''}”</h1>
|
||||
<p className="text-base tracking-tight mb-4 pr-6">
|
||||
Explanatory writing about what data will be kept.
|
||||
<DialogContent showClose={false} className="max-w-[400px] space-y-6">
|
||||
<h1 className="h4">Remove “{docket?.title || ''}”?</h1>
|
||||
<p className="text-base tracking-tight pr-6">
|
||||
This will remove the software's tile from your home screen.
|
||||
</p>
|
||||
<Button variant="destructive" onClick={handleRemoveApp}>
|
||||
Remove
|
||||
</Button>
|
||||
<div className="flex space-x-6">
|
||||
<DialogClose as={Button} variant="secondary">
|
||||
Cancel
|
||||
</DialogClose>
|
||||
<DialogClose as={Button} onClick={handleRemoveApp}>
|
||||
Remove “{docket?.title}”
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { Redirect, useHistory, useParams } from 'react-router-dom';
|
||||
import { Button } from '../components/Button';
|
||||
import { Dialog, DialogContent } from '../components/Dialog';
|
||||
import { Dialog, DialogClose, DialogContent } from '../components/Dialog';
|
||||
import useDocketState, { useCharges } from '../state/docket';
|
||||
|
||||
export const SuspendApp = () => {
|
||||
@ -13,7 +13,6 @@ export const SuspendApp = () => {
|
||||
// TODO: add optimistic updates
|
||||
const handleSuspendApp = useCallback(() => {
|
||||
useDocketState.getState().toggleDocket(desk);
|
||||
history.push('/');
|
||||
}, [desk]);
|
||||
|
||||
if ('suspend' in charge.chad) {
|
||||
@ -22,14 +21,19 @@ export const SuspendApp = () => {
|
||||
|
||||
return (
|
||||
<Dialog open onOpenChange={(open) => !open && history.push('/')}>
|
||||
<DialogContent>
|
||||
<h1 className="h4 mb-9">Suspend “{charge?.title || ''}”</h1>
|
||||
<p className="text-base tracking-tight mb-4 pr-6">
|
||||
<DialogContent showClose={false} className="max-w-[400px] space-y-6">
|
||||
<h1 className="h4">Suspend “{charge?.title || ''}”</h1>
|
||||
<p className="text-base tracking-tight pr-6">
|
||||
Suspending an app will freeze its current state, and render it unable
|
||||
</p>
|
||||
<Button variant="destructive" onClick={handleSuspendApp}>
|
||||
Suspend
|
||||
</Button>
|
||||
<div className="flex space-x-6">
|
||||
<DialogClose as={Button} variant="secondary">
|
||||
Cancel
|
||||
</DialogClose>
|
||||
<DialogClose as={Button} onClick={handleSuspendApp}>
|
||||
Suspend “{charge?.title}”
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
@ -38,7 +38,7 @@ export const Tile: FunctionComponent<TileProps> = ({ charge, desk }) => {
|
||||
href={active ? link : undefined}
|
||||
target={desk}
|
||||
className={classNames(
|
||||
'group relative font-semibold aspect-w-1 aspect-h-1 rounded-xl default-ring',
|
||||
'group relative font-semibold aspect-w-1 aspect-h-1 rounded-3xl default-ring',
|
||||
!active && 'cursor-default'
|
||||
)}
|
||||
style={{ backgroundColor: active ? color || 'purple' : suspendColor }}
|
||||
|
Loading…
Reference in New Issue
Block a user