mirror of
https://github.com/urbit/shrub.git
synced 2024-12-21 01:41:37 +03:00
Merge pull request #5362 from urbit/hm/fix-empty-title-handling
grid: fix empty title handling
This commit is contained in:
commit
1a784cdeb7
@ -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 “{docket.title}”</h2>
|
||||
<h2 className="h4">Install “{getAppName(docket)}”</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 “{docket.title}”
|
||||
Get “{getAppName(docket)}”
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
@ -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>
|
||||
</>
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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>
|
||||
);
|
||||
|
2
pkg/grid/src/env.d.ts
vendored
2
pkg/grid/src/env.d.ts
vendored
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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" />
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 “{docket?.title || ''}”?</h1>
|
||||
<h1 className="h4">Remove “{getAppName(docket)}”?</h1>
|
||||
<p className="text-base tracking-tight pr-6">
|
||||
This will remove the software's tile from your home screen.
|
||||
</p>
|
||||
@ -30,7 +31,7 @@ export const RemoveApp = () => {
|
||||
Cancel
|
||||
</DialogClose>
|
||||
<DialogClose as={Button} onClick={handleRemoveApp}>
|
||||
Remove “{docket?.title}”
|
||||
Remove “{getAppName(docket)}”
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
@ -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 “{charge?.title || ''}”</h1>
|
||||
<h1 className="h4">Suspend “{getAppName(charge)}”</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 “{charge?.title}”
|
||||
Suspend “{getAppName(charge)}”
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user