Merge pull request #5362 from urbit/hm/fix-empty-title-handling

grid: fix empty title handling
This commit is contained in:
Hunter Miller 2021-10-25 13:52:58 -05:00 committed by GitHub
commit 1a784cdeb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 51 additions and 25 deletions

View File

@ -9,7 +9,7 @@ import { DocketHeader } from './DocketHeader';
import { Spinner } from './Spinner';
import { VatMeta } from './VatMeta';
import useDocketState, { ChargeWithDesk } from '../state/docket';
import { getAppHref } from '../state/util';
import { getAppHref, getAppName } from '../state/util';
import { addRecentApp } from '../nav/search/Home';
import { TreatyMeta } from './TreatyMeta';
@ -113,7 +113,7 @@ export const AppInfo: FC<AppInfoProps> = ({ docket, vat, className }) => {
className="space-y-6"
containerClass="w-full max-w-md"
>
<h2 className="h4">Install &ldquo;{docket.title}&rdquo;</h2>
<h2 className="h4">Install &ldquo;{getAppName(docket)}&rdquo;</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.
@ -123,7 +123,7 @@ export const AppInfo: FC<AppInfoProps> = ({ docket, vat, className }) => {
Cancel
</DialogClose>
<DialogClose as={Button} onClick={installApp}>
Get &ldquo;{docket.title}&rdquo;
Get &ldquo;{getAppName(docket)}&rdquo;
</DialogClose>
</div>
</DialogContent>

View File

@ -2,7 +2,7 @@ import classNames from 'classnames';
import React, { HTMLProps, ReactNode } from 'react';
import { Link, LinkProps } from 'react-router-dom';
import { DocketWithDesk } from '../state/docket';
import { getAppHref } from '../state/util';
import { getAppHref, getAppName } from '../state/util';
import { DocketImage } from './DocketImage';
type Sizes = 'xs' | 'small' | 'default';
@ -57,7 +57,7 @@ export const AppLink = <T extends DocketWithDesk>({
<>
<DocketImage color={app.color} image={app.image} size={size} />
<div className="flex-1 text-black">
<p>{app.title}</p>
<p>{getAppName(app)}</p>
{app.info && size === 'default' && <p className="font-normal">{app.info}</p>}
</div>
</>

View File

@ -4,6 +4,7 @@ import { MatchItem } from '../nav/Nav';
import { useRecentsStore } from '../nav/search/Home';
import { AppLink, AppLinkProps } from './AppLink';
import { DocketWithDesk } from '../state/docket';
import { getAppName } from '../state/util';
type AppListProps<T extends DocketWithDesk> = {
apps: T[];
@ -45,7 +46,7 @@ export const AppList = <T extends DocketWithDesk>({
aria-labelledby={labelledBy}
>
{apps.map((app) => (
<li key={app.title} id={app.title} role="option" aria-selected={selected(app)}>
<li key={getAppName(app)} id={getAppName(app)} role="option" aria-selected={selected(app)}>
<AppLink
{...props}
app={app}

View File

@ -1,21 +1,22 @@
import React from 'react';
import { Docket } from '@urbit/api/docket';
import { DocketImage } from './DocketImage';
import { getAppName } from '../state/util';
import { DocketWithDesk } from '../state/docket';
interface DocketHeaderProps {
docket: Docket;
docket: DocketWithDesk;
children?: React.ReactNode;
}
export function DocketHeader(props: DocketHeaderProps) {
const { docket, children } = props;
const { info, title, image, color } = docket;
const { info, image, color } = docket;
return (
<header className="grid grid-cols-[5rem,1fr] md:grid-cols-[8rem,1fr] auto-rows-min grid-flow-row-dense mb-5 sm:mb-8 gap-x-6 gap-y-4">
<DocketImage color={color} image={image} className="row-span-1 md:row-span-2" />
<div className="col-start-2">
<h1 className="h2">{title}</h1>
<h1 className="h2">{getAppName(docket)}</h1>
{info && <p className="h4 mt-2 text-gray-500">{info}</p>}
</div>
{children}

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { Docket } from '@urbit/api/docket';
import cn from 'classnames';
import { useTileColor } from '../tiles/useTileColor';
@ -19,13 +19,20 @@ const sizeMap: Record<DocketImageSizes, string> = {
export function DocketImage({ color, image, className = '', size = 'full' }: DocketImageProps) {
const { tileColor } = useTileColor(color);
const [imageError, setImageError] = useState(false);
return (
<div
className={cn('flex-none relative bg-gray-200 overflow-hidden', sizeMap[size], className)}
style={{ backgroundColor: tileColor }}
>
{image && (
<img className="absolute top-0 left-0 h-full w-full object-cover" src={image} alt="" />
{image && !imageError && (
<img
className="absolute top-0 left-0 h-full w-full object-cover"
src={image}
alt=""
onError={() => setImageError(true)}
/>
)}
</div>
);

View File

@ -1,4 +1,4 @@
interface ImportMetaEnv extends Readonly<Record<string, string>> {
interface ImportMetaEnv extends Readonly<Record<string, string | boolean | undefined>> {
readonly VITE_LAST_WIPE: string;
readonly VITE_STORAGE_VERSION: string;
}

View File

@ -14,6 +14,7 @@ import { LeftArrow } from '../components/icons/LeftArrow';
import { System } from '../components/icons/System';
import { Interface } from '../components/icons/Interface';
import { Notifications } from '../components/icons/Notifications';
import { getAppName } from '../state/util';
interface SystemPreferencesSectionProps {
url: string;
@ -112,7 +113,7 @@ export const SystemPreferences = (props: RouteComponentProps<{ submenu: string }
active={matchSub('apps', charge.desk)}
>
<DocketImage size="small" className="mr-3" {...charge} />
{charge.title}
{getAppName(charge)}
</SystemPreferencesSection>
))}
</ul>

View File

@ -4,6 +4,7 @@ import { Setting } from '../../components/Setting';
import { ShipName } from '../../components/ShipName';
import { useCharge } from '../../state/docket';
import useKilnState, { useVat } from '../../state/kiln';
import { getAppName } from '../../state/util';
export const AppPrefs = ({ match }: RouteComponentProps<{ desk: string }>) => {
const { desk } = match.params;
@ -18,11 +19,11 @@ export const AppPrefs = ({ match }: RouteComponentProps<{ desk: string }>) => {
return (
<>
<h2 className="h3 mb-7">{charge?.title} Settings</h2>
<h2 className="h3 mb-7">{getAppName(charge)} Settings</h2>
<div className="space-y-3">
{tracking ? (
<Setting on={otasEnabled} toggle={toggleUpdates} name="Automatic Updates">
<p>Automatically download and apply updates to keep {charge?.title} up to date.</p>
<p>Automatically download and apply updates to keep {getAppName(charge)} up to date.</p>
{otaSource && (
<p>
OTA Source: <ShipName name={otaSource} className="font-semibold font-mono" />

View File

@ -4,6 +4,7 @@ import { AppInfo } from '../../components/AppInfo';
import { Spinner } from '../../components/Spinner';
import useDocketState, { useCharge, useTreaty } from '../../state/docket';
import { useVat } from '../../state/kiln';
import { getAppName } from '../../state/util';
import { useLeapStore } from '../Nav';
export const TreatyInfo = () => {
@ -12,6 +13,7 @@ export const TreatyInfo = () => {
const treaty = useTreaty(host, desk);
const vat = useVat(desk);
const charge = useCharge(desk);
const name = getAppName(treaty);
useEffect(() => {
if (!charge) {
@ -20,9 +22,9 @@ export const TreatyInfo = () => {
}, [host, desk]);
useEffect(() => {
select(<>{treaty?.title}</>);
select(<>{name}</>);
useLeapStore.setState({ matches: [] });
}, [treaty?.title]);
}, [name]);
if (!treaty) {
// TODO: maybe replace spinner with skeletons

View File

@ -1,4 +1,4 @@
import { DocketHref } from '@urbit/api/docket';
import { Docket, DocketHref, Treaty } from '@urbit/api/docket';
import { hsla, parseToHsla } from 'color2k';
import _ from 'lodash';
@ -16,6 +16,14 @@ export function getAppHref(href: DocketHref) {
return 'site' in href ? href.site : `/apps/${href.glob.base}/`;
}
export function getAppName(app: (Docket & { desk: string }) | Treaty | undefined): string {
if (!app) {
return '';
}
return app.title || app.desk;
}
export function disableDefault<T extends Event>(e: T): void {
e.preventDefault();
}

View File

@ -4,6 +4,7 @@ import { Button } from '../components/Button';
import { Dialog, DialogClose, DialogContent } from '../components/Dialog';
import { useRecentsStore } from '../nav/search/Home';
import useDocketState, { useCharges } from '../state/docket';
import { getAppName } from '../state/util';
export const RemoveApp = () => {
const history = useHistory();
@ -21,7 +22,7 @@ export const RemoveApp = () => {
return (
<Dialog open onOpenChange={(open) => !open && history.push('/')}>
<DialogContent showClose={false} className="space-y-6" containerClass="w-full max-w-md">
<h1 className="h4">Remove &ldquo;{docket?.title || ''}&rdquo;?</h1>
<h1 className="h4">Remove &ldquo;{getAppName(docket)}&rdquo;?</h1>
<p className="text-base tracking-tight pr-6">
This will remove the software&apos;s tile from your home screen.
</p>
@ -30,7 +31,7 @@ export const RemoveApp = () => {
Cancel
</DialogClose>
<DialogClose as={Button} onClick={handleRemoveApp}>
Remove &ldquo;{docket?.title}&rdquo;
Remove &ldquo;{getAppName(docket)}&rdquo;
</DialogClose>
</div>
</DialogContent>

View File

@ -4,6 +4,7 @@ import { Button } from '../components/Button';
import { Dialog, DialogClose, DialogContent } from '../components/Dialog';
import { useRecentsStore } from '../nav/search/Home';
import useDocketState, { useCharges } from '../state/docket';
import { getAppName } from '../state/util';
export const SuspendApp = () => {
const history = useHistory();
@ -24,7 +25,7 @@ export const SuspendApp = () => {
return (
<Dialog open onOpenChange={(open) => !open && history.push('/')}>
<DialogContent showClose={false} className="space-y-6" containerClass="w-full max-w-md">
<h1 className="h4">Suspend &ldquo;{charge?.title || ''}&rdquo;</h1>
<h1 className="h4">Suspend &ldquo;{getAppName(charge)}&rdquo;</h1>
<p className="text-base tracking-tight pr-6">
Suspending an app will turn off automatic updates. You cannot use an app when it is
suspended, but you can resume it at any time.
@ -34,7 +35,7 @@ export const SuspendApp = () => {
Cancel
</DialogClose>
<DialogClose as={Button} onClick={handleSuspendApp}>
Suspend &ldquo;{charge?.title}&rdquo;
Suspend &ldquo;{getAppName(charge)}&rdquo;
</DialogClose>
</div>
</DialogContent>

View File

@ -199,6 +199,8 @@ const ClampedText = styled(Text)`
type AppTileProps = Treaty & BoxProps;
export function AppTile({ color, image, ...props }: AppTileProps) {
const [imageError, setImageError] = useState(false);
return (
<Box
position="relative"
@ -210,7 +212,7 @@ export function AppTile({ color, image, ...props }: AppTileProps) {
bg={color || 'washedGray'}
{...props}
>
{image && (
{image && !imageError && (
<Image
src={image}
position="absolute"
@ -218,6 +220,7 @@ export function AppTile({ color, image, ...props }: AppTileProps) {
left="0"
width="100%"
height="100%"
onError={() => setImageError(true)}
/>
)}
</Box>