Merge pull request #5236 from urbit/hm/grid-qa-fixes

grid: qa fixes round 1
This commit is contained in:
Liam Fitzgerald 2021-09-21 10:10:24 +10:00 committed by GitHub
commit 8748b115a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 150 additions and 86 deletions

View File

@ -1,6 +1,6 @@
import { chadIsRunning, Treaty } from '@urbit/api';
import clipboardCopy from 'clipboard-copy';
import React, { FC } from 'react';
import React, { FC, useCallback, useState } from 'react';
import cn from 'classnames';
import { Vat } from '@urbit/api/hood';
import { Button, PillButton } from './Button';
@ -50,6 +50,7 @@ function getRemoteDesk(docket: App, vat?: Vat) {
export const AppInfo: FC<AppInfoProps> = ({ docket, vat, className }) => {
const installStatus = getInstallStatus(docket);
const [ship, desk] = getRemoteDesk(docket, vat);
const [copied, setCopied] = useState(false);
const installApp = async () => {
if (installStatus === 'installed') {
@ -57,15 +58,21 @@ export const AppInfo: FC<AppInfoProps> = ({ docket, vat, className }) => {
}
await useDocketState.getState().installDocket(ship, desk);
};
const copyApp = () => {
clipboardCopy(`web+urbitgraph://app/${ship}/${desk}`);
};
const copyApp = useCallback(() => {
setCopied(true);
clipboardCopy(`web+urbitgraph://${ship}/${desk}`);
setTimeout(() => {
setCopied(false);
}, 1250);
}, []);
if (!docket) {
// TODO: maybe replace spinner with skeletons
return (
<div className="dialog-inner-container text-black">
<span>Loading...</span>
<div className="dialog-inner-container flex justify-center text-black">
<Spinner className="w-10 h-10" />
</div>
);
}
@ -115,7 +122,8 @@ export const AppInfo: FC<AppInfoProps> = ({ docket, vat, className }) => {
</Dialog>
)}
<PillButton variant="alt-secondary" onClick={copyApp}>
Copy App Link
{!copied && 'Copy App Link'}
{copied && 'copied!'}
</PillButton>
</div>
</DocketHeader>

View File

@ -242,6 +242,7 @@ export const Leap = React.forwardRef(
onFocus={onFocus}
onChange={onChange}
onKeyDown={onKeyDown}
autoComplete="off"
aria-autocomplete="both"
aria-controls={dropdown}
aria-activedescendant={selectedMatch?.display || selectedMatch?.value}

View File

@ -1,20 +1,16 @@
import React, { useEffect } from 'react';
import { Link, NavLink, Route, Switch } from 'react-router-dom';
import { useLeapStore } from './Nav';
import { Button } from '../components/Button';
import { useHarkStore } from '../state/hark';
import { Inbox } from './notifications/Inbox';
export const Notifications = () => {
const select = useLeapStore((s) => s.select);
const markAllAsRead = () => {
const { archiveAll } = useHarkStore.getState();
archiveAll();
};
useEffect(() => {
select('Notifications');
function visibilitychange() {
if (document.visibilityState === 'hidden') {
useHarkStore.getState().opened();

View File

@ -12,7 +12,10 @@ type SearchProps = RouteComponentProps<{
export const Search = ({ match }: SearchProps) => {
return (
<Switch>
<Route path={`${match.path}/:ship/apps/:host/:desk`} component={TreatyInfo} />
<Route
path={[`${match.path}/direct/apps/:host/:desk`, `${match.path}/:ship/apps/:host/:desk`]}
component={TreatyInfo}
/>
<Route path={`${match.path}/:ship/apps`} component={Apps} />
<Route path={`${match.path}/:ship`} component={Providers} />
<Route path={`${match.path}`} component={Home} />

View File

@ -28,16 +28,22 @@ export const SystemMenu = ({ className, open, subMenuOpen, shouldDim }: SystemMe
const hash = garden ? getHash(garden) : null;
const isMobile = useMedia('(max-width: 639px)');
const copyHash = useCallback((event: Event) => {
event.preventDefault();
const copyHash = useCallback(
(event: Event) => {
event.preventDefault();
if (!hash) {
return;
}
setCopied(true);
clipboardCopy('fjuhl');
setCopied(true);
clipboardCopy(hash);
setTimeout(() => {
setCopied(false);
}, 1250);
}, []);
setTimeout(() => {
setCopied(false);
}, 1250);
},
[hash]
);
const preventFlash = useCallback((e) => {
const target = e.target as HTMLElement;

View File

@ -5,10 +5,9 @@ import { map, take } from 'lodash';
import { useCharge } from '../../state/docket';
import { Elbow } from '../../components/icons/Elbow';
import { ShipName } from '../../components/ShipName';
import { getAppHref } from '../../state/util';
import { Link } from 'react-router-dom';
import { DeskLink } from '../../components/DeskLink';
import {useHarkStore} from '../../state/hark';
import { useHarkStore } from '../../state/hark';
import { DocketImage } from '../../components/DocketImage';
interface BasicNotificationProps {
notification: Notification;
@ -43,7 +42,6 @@ export const BasicNotification = ({ notification, lid }: BasicNotificationProps)
const contents = map(notification.body, 'content').filter((c) => c.length > 0);
const large = contents.length === 0;
const archive = () => {
const { bin } = notification;
useHarkStore.getState().archiveNote(notification.bin, lid);
};
@ -53,17 +51,14 @@ export const BasicNotification = ({ notification, lid }: BasicNotificationProps)
to={`?grid-note=${encodeURIComponent(first.link)}`}
desk={desk}
className={cn(
'text-black rounded',
'text-black rounded-xl',
'unseen' in lid ? 'bg-blue-100' : 'bg-gray-100',
large ? 'note-grid-no-content' : 'note-grid-content'
)}
aria-labelledby={id}
>
<header id={id} className="contents">
<div
className="note-grid-icon rounded w-full h-full"
style={{ backgroundColor: charge?.color ?? '#ee5432' }}
/>
<DocketImage {...charge} size={!large ? 'xs' : 'default'} className="note-grid-icon" />
<div className="note-grid-title font-semibold">{charge?.title || desk}</div>
{!large ? <Elbow className="note-grid-arrow w-6 h-6 text-gray-300" /> : null}
<h2 id={`${id}-title`} className="note-grid-head font-semibold text-gray-600">

View File

@ -1,47 +1,57 @@
import React from 'react';
import cn from 'classnames';
import { Link } from 'react-router-dom';
import { Button } from '../../components/Button';
import { useCurrentTheme } from '../../state/local';
import { getDarkColor } from '../../state/util';
const cards: OnboardingCardProps[] = [
{
title: 'Terminal',
body: "Install a web terminal to access your Urbit's command line",
button: 'Install',
color: '#9CA4B1'
color: '#9CA4B1',
href: '/leap/search/direct/apps/~zod/webterm'
},
{
title: 'Groups',
body: 'Install Groups, a suite of social software to communicate with other urbit users',
button: 'Install',
color: '#D1DDD3'
color: '#D1DDD3',
href: '/leap/search/direct/apps/~zod/landscape'
},
{
title: 'Bitcoin',
body: 'Install a bitcoin wallet. You can send bitcoin to any btc address, or even another ship',
button: 'Install',
color: '#F6EBDB'
},
{
title: 'Debug',
body: "Install a debugger. You can inspect your ship's internals using this interface",
button: 'Install'
},
{
title: 'Build an app',
body: 'You can instantly get started building new things on Urbit. Just right click your Landscape and select “New App”',
button: 'Learn more',
color: '#82A6CA'
color: '#F6EBDB',
href: '/leap/search/direct/apps/~zod/bitcoin'
}
// Commenting out until we have something real
// {
// title: 'Debug',
// body: "Install a debugger. You can inspect your ship's internals using this interface",
// button: 'Install',
// color: '#E5E5E5',
// href: '/leap/search/direct/apps/~zod/debug'
// }
// {
// title: 'Build an app',
// body: 'You can instantly get started building new things on Urbit. Just right click your Landscape and select “New App”',
// button: 'Learn more',
// color: '#82A6CA'
// }
];
interface OnboardingCardProps {
title: string;
button: string;
href: string;
body: string;
color?: string;
color: string;
}
const OnboardingCard = ({ title, button, body, color }: OnboardingCardProps) => (
const OnboardingCard = ({ title, button, href, body, color }: OnboardingCardProps) => (
<div
className="p-4 flex flex-col space-y-2 text-black bg-gray-100 justify-between rounded-xl"
style={color ? { backgroundColor: color } : {}}
@ -50,7 +60,7 @@ const OnboardingCard = ({ title, button, body, color }: OnboardingCardProps) =>
<h4 className="font-semibold text-black">{title}</h4>
<p>{body}</p>
</div>
<Button variant="primary" className="bg-gray-500">
<Button as={Link} to={href} variant="primary" className="bg-gray-500">
{button}
</Button>
</div>
@ -60,27 +70,35 @@ interface OnboardingNotificationProps {
unread: boolean;
}
export const OnboardingNotification = ({ unread }: OnboardingNotificationProps) => (
<section
className={cn('notification space-y-2 text-black', unread ? 'bg-blue-100' : 'bg-gray-100')}
aria-labelledby=""
>
<header id="system-updates-blocked" className="relative space-y-2">
<div className="flex space-x-2">
<span className="inline-block w-6 h-6 bg-orange-500 rounded-full" />
<span className="font-medium">Grid</span>
export const OnboardingNotification = ({ unread }: OnboardingNotificationProps) => {
const theme = useCurrentTheme();
return (
<section
className={cn('notification space-y-2 text-black', unread ? 'bg-blue-100' : 'bg-gray-100')}
aria-labelledby=""
>
<header id="system-updates-blocked" className="relative space-y-2">
<div className="flex space-x-2">
<span className="inline-block w-6 h-6 bg-orange-500 rounded-full" />
<span className="font-medium">Grid</span>
</div>
<div className="flex space-x-2">
<h2 id="runtime-lag">Hello there, welcome to Grid!</h2>
</div>
</header>
<div className="grid grid-cols-3 grid-rows-2 gap-4">
{
/* eslint-disable-next-line react/no-array-index-key */
cards.map((card, i) => (
<OnboardingCard
key={i}
{...card}
color={theme === 'dark' ? getDarkColor(card.color) : card.color}
/>
))
}
</div>
<div className="flex space-x-2">
<h2 id="runtime-lag">Hello there, welcome to Grid!</h2>
</div>
</header>
<div className="grid grid-cols-3 grid-rows-2 gap-4">
{
/* eslint-disable-next-line react/no-array-index-key */
cards.map((card, i) => (
<OnboardingCard key={i} {...card} />
))
}
</div>
</section>
);
</section>
);
};

View File

@ -78,7 +78,8 @@ export const BaseBlockedNotification = () => {
<DialogTrigger as={NotificationButton}>Dismiss</DialogTrigger>
<DialogContent
showClose={false}
className="max-w-[400px] space-y-6 text-base tracking-tight"
className="space-y-6 text-base tracking-tight"
containerClass="w-full max-w-md"
>
<h2 className="h4">Skip System Update</h2>
<p>
@ -108,7 +109,8 @@ export const BaseBlockedNotification = () => {
<DialogContent
showClose={false}
onOpenAutoFocus={disableDefault}
className="max-w-[400px] space-y-6 text-base tracking-tight"
className="space-y-6 text-base tracking-tight"
containerClass="w-full max-w-md"
>
<h2 className="h4">Archive ({count}) Apps and Apply System Update</h2>
<p>

View File

@ -53,7 +53,8 @@ export const useRecentsStore = create<RecentsStore>(
}),
{
whitelist: ['recentApps', 'recentDevs'],
name: 'recents-store'
name: 'recents-store',
version: import.meta.env.VITE_SHORTHASH as any
}
)
);

View File

@ -1,6 +1,7 @@
import React, { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { AppInfo } from '../../components/AppInfo';
import { Spinner } from '../../components/Spinner';
import useDocketState, { useCharge, useTreaty } from '../../state/docket';
import { useVat } from '../../state/kiln';
import { useLeapStore } from '../Nav';
@ -25,8 +26,8 @@ export const TreatyInfo = () => {
if (!treaty) {
// TODO: maybe replace spinner with skeletons
return (
<div className="dialog-inner-container text-black">
<span>Loading...</span>
<div className="dialog-inner-container flex justify-center text-black">
<Spinner className="w-10 h-10" />
</div>
);
}

View File

@ -149,8 +149,8 @@ export const createState = <T extends Record<string, unknown>>(
}),
{
blacklist,
name: stateStorageKey(name)
// version: process.env.LANDSCAPE_SHORTHASH as any
name: stateStorageKey(name),
version: import.meta.env.VITE_SHORTHASH as any
}
)
);

View File

@ -17,7 +17,7 @@ interface KilnState {
toggleOTAs: (desk: string, on: boolean) => Promise<void>;
set: (s: KilnState) => void;
}
const useKilnState = create<KilnState>((set) => ({
const useKilnState = create<KilnState>((set, get) => ({
vats: useMockData ? mockVats : {},
lag: !!useMockData,
loaded: false,
@ -66,6 +66,10 @@ const useKilnState = create<KilnState>((set) => ({
}
await api.poke(on ? kilnResume(desk) : kilnPause(desk));
if (!on) {
get().fetchVats(); // refresh vat state
}
},
set: produce(set)
}));

View File

@ -1,4 +1,5 @@
import { DocketHref } from '@urbit/api/docket';
import { hsla, parseToHsla } from 'color2k';
export const useMockData = import.meta.env.MODE === 'mock';
@ -33,3 +34,8 @@ export function deSig(ship: string): string {
}
return ship.replace('~', '');
}
export function getDarkColor(color: string): string {
const hslaColor = parseToHsla(color);
return hsla(hslaColor[0], hslaColor[1], 1 - hslaColor[2], 1);
}

View File

@ -1,3 +1,26 @@
body {
scrollbar-width: thin;
scrollbar-color: #e5e5e5 transparent;
}
body::-webkit-scrollbar-thumb {
background-color: #e5e5e5;
}
body::-webkit-scrollbar-track {
background: transparent;
}
@media (prefers-color-scheme: dark) {
body {
scrollbar-color: #333333 transparent;
}
body::-webkit-scrollbar-thumb {
background-color: #333333;
}
}
.h1 {
@apply text-2xl sm:text-3xl font-bold leading-relaxed tracking-tighter;
}

View File

@ -7,11 +7,11 @@
}
.dialog-container {
@apply fixed z-40 top-1/2 left-1/2 min-w-80 transform -translate-x-1/2 -translate-y-1/2;
@apply fixed z-40 top-1/2 left-1/2 p-4 transform -translate-x-1/2 -translate-y-1/2;
}
.dialog {
@apply relative p-5 sm:p-8 bg-white rounded-3xl;
@apply relative max-h-[calc(100vh-2rem)] max-w-[calc(100vw-2rem)] p-5 sm:p-8 bg-white rounded-3xl overflow-auto;
}
.dialog-inner-container {

View File

@ -18,7 +18,7 @@ export const RemoveApp = () => {
return (
<Dialog open onOpenChange={(open) => !open && history.push('/')}>
<DialogContent showClose={false} className="max-w-[400px] space-y-6">
<DialogContent showClose={false} className="space-y-6" containerClass="w-full max-w-md">
<h1 className="h4">Remove &ldquo;{docket?.title || ''}&rdquo;?</h1>
<p className="text-base tracking-tight pr-6">
This will remove the software&apos;s tile from your home screen.

View File

@ -21,7 +21,7 @@ export const SuspendApp = () => {
return (
<Dialog open onOpenChange={(open) => !open && history.push('/')}>
<DialogContent showClose={false} className="max-w-[400px] space-y-6">
<DialogContent showClose={false} className="space-y-6" containerClass="w-full max-w-md">
<h1 className="h4">Suspend &ldquo;{charge?.title || ''}&rdquo;</h1>
<p className="text-base tracking-tight pr-6">
Suspending an app will freeze its current state and render it unable

View File

@ -1,10 +1,6 @@
import { darken, hsla, lighten, parseToHsla, readableColorIsBlack } from 'color2k';
import { useCurrentTheme } from '../state/local';
function getDarkColor(color: string): string {
const hslaColor = parseToHsla(color);
return hsla(hslaColor[0], hslaColor[1], 1 - hslaColor[2], 1);
}
import { getDarkColor } from '../state/util';
function bgAdjustedColor(color: string, darkBg: boolean): string {
return darkBg ? lighten(color, 0.1) : darken(color, 0.1);

View File

@ -3,11 +3,15 @@ import analyze from 'rollup-plugin-analyzer';
import { visualizer } from 'rollup-plugin-visualizer';
import reactRefresh from '@vitejs/plugin-react-refresh';
import htmlPlugin from 'vite-plugin-html-config';
import { execSync } from 'child_process';
const htmlPluginOpt = {
headScripts: [{ src: '/apps/grid/desk.js' }, { src: '/session.js' }]
};
const GIT_DESC = execSync('git describe --always', { encoding: 'utf8' }).trim();
process.env.VITE_SHORTHASH = GIT_DESC;
// https://vitejs.dev/config/
export default ({ mode }) => {
Object.assign(process.env, loadEnv(mode, process.cwd()));