Skip to content

Commit

Permalink
perms: update passport request
Browse files Browse the repository at this point in the history
also, render permission details using epxandable summary rows
  • Loading branch information
tomholford committed Jun 17, 2023
1 parent cfedddf commit f2d8c20
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 103 deletions.
9 changes: 6 additions & 3 deletions ui/src/gear/permissions/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ export function denyPerms(desk: string, perms: PokePerm[]): Poke<DenyPermsPoke>
};
}

export async function sealToPassport(seal: Seal) {
return await api.thread<Passport, Seal>({
export async function sealToPassport(desk: string, seal: Seal) {
return await api.thread<Passport, { desk: string, seal: Seal }>({
inputMark: "json",
outputMark: "json",
threadName: "get-passport",
body: seal
body: {
desk,
seal
}
});
}
4 changes: 4 additions & 0 deletions ui/src/logic/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,7 @@ export function randomElement<T>(a: T[]) {
export function randomIntInRange(min: number, max: number) {
return Math.round(Math.random() * (max - min) + min);
}

export function capFirst(string: string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
10 changes: 6 additions & 4 deletions ui/src/permissions/PermissionsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export function PermissionsDialogInner({
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const showWarning = passport.sys.length > 0;

const passportPerms = [...passport.sys, ...passport.any, ...passport.new, ...passport.rad, ...passport.app];

return (
<div className="space-y-6">
<section className='flex justify-between items-center'>
Expand Down Expand Up @@ -55,10 +57,10 @@ export function PermissionsDialogInner({
viewMode === 'Summary' ? (
<div className="space-y-5">
{
[...passport.sys, ...passport.any, ...passport.new, ...passport.rad].map(p => {
return p.kind.pes.map((pe, i) => {
return <SummaryRow key={i} summary={pe} />
})
passportPerms.map((p, i) => {
return (
<SummaryRow key={i} perm={p} />
)
})
}
</div>
Expand Down
125 changes: 91 additions & 34 deletions ui/src/permissions/SummaryRow.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,113 @@
import cn from 'classnames';
import { Adjust } from '@/components/icons/Adjust';
import BellIcon from '@/components/icons/BellIcon';
import { ChevronDown16Icon } from '@/components/icons/ChevronDown16Icon';
import GlobeIcon from '@/components/icons/GlobeIcon';
import KeyIcon from '@/components/icons/KeyIcon';
import SigIcon from '@/components/icons/SigIcon';
import ZapIcon from '@/components/icons/ZapIcon';
import { PermSummary } from '@/gear';
import React from 'react';
import { AppPerm, PassportPerm } from '@/gear';
import { capFirst } from '@/logic/utils';
import React, { useCallback, useMemo, useState } from 'react';
import exp from 'constants';

interface SummaryRowProps {
/**
* A permission summary.
* A permission bucket.
*/
summary: PermSummary;
perm: PassportPerm | AppPerm;
}

// TODO: figured out mapping from permission to icon from Dan & Mark
const iconFromPerm = (perm: PermSummary) => {
if(perm.pers.some(p => p.vane && ['ames', 'eyre', 'gall'].includes(p.vane))) {
return <GlobeIcon />;
/**
* A row summarizing a requested permission.
* It has the description of the permission and an optional warning icon.
*/
export default function SummaryRow({ perm }: SummaryRowProps) {
const [expanded, setExpanded] = useState(false);

// TODO: need icon identifier from perm; requested from tinnus
// for now, return random icon placeholder
const iconSeed = useMemo(() => Math.random(), []);
const iconFromPerm = (perm: PassportPerm) => {

if (iconSeed < 0.2) {
return <GlobeIcon />;
} else if (iconSeed < 0.4) {
return <KeyIcon />;
} else if (iconSeed < 0.6) {
return <BellIcon />;
} else if (iconSeed < 0.8) {
return <SigIcon />;
} else {
return <Adjust />;
}
}

// TODO: handle node perms
if ('node' in perm) {
return null;
}

if(perm.pers.some(p => p.vane && p.vane === 'jael')) {
return <KeyIcon />;
// TODO: handle app perms
if ('app' in perm) {
return null;
}

// TODO:
// <BellIcon />
// <SigIcon />
const { nom, pes } = perm.kind;
const hasWarning = pes.some(pe => pe.warn !== null);
const expandable = pes.length > 0;

return <Adjust />;
}

/**
* A row summarizing a requested permission.
* It has the description of the permission and an optional warning icon.
*/
export default function SummaryRow({ summary }: SummaryRowProps) {
const { desc, warn } = summary;
const toggleExpanded = useCallback(() => {
if (!expandable) return;
setExpanded(!expanded);
}, [expanded]);

return (
<div className='w-full flex justify-between content-center p-2 space-x-4'>
<div className='flex space-x-1'>
<div className='h-8 w-8 bg-gray-50 p-1.5'>
{iconFromPerm(summary)}
<section>
<div
onClick={toggleExpanded}
className={cn('w-full flex justify-between content-center p-2 space-x-4 rounded-t-lg',
expandable && 'cursor-pointer',
expanded && 'bg-gray-50',
)}>
<div className='flex space-x-1'>
<div className='h-8 w-8 bg-gray-50 p-1.5 bg-blend-multiply'>
{iconFromPerm(perm)}
</div>
<div className='flex flex-col justify-center text-sm font-medium text-gray-900'>
{capFirst(nom)}
</div>
</div>
<div className='flex flex-col justify-center text-sm font-medium text-gray-900'>
{desc}
<div className='flex flex-row space-x-3'>
{hasWarning ? (
<div className='flex flex-col justify-center'>
<ZapIcon color='#FF6240' />
</div>
) : null}
{
expandable ? (
<div className='flex flex-col justify-center'>
<ChevronDown16Icon className={`transform ${expanded ? 'rotate-180' : ''}`} />
</div>
) : null
}
</div>
</div>
{warn ? (
<div className='flex flex-col justify-center'>
<ZapIcon color='#FF6240' />
</div>
) : null}
</div>
{
expanded ? (
<div className='flex flex-col space-y-2 rounded-b-lg bg-gray-50 px-2 py-3'>
{
pes.map(pe => (
<div className='flex flex-row'>
<div className='text-sm font-semibold leading-4'>
{capFirst(pe.desc)}
</div>
</div>
))
}
</div>
) : null
}
</section>
)
}
49 changes: 16 additions & 33 deletions ui/src/permissions/temp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,65 +5,48 @@ const fakePassport: Passport = {
sys: [
{
kind: {
nom: 'write',
nom: 'Access your network keys',
pes: [
{
desc: 'Access network keys or passwords',
have: "nil",
warn: "This app can impersonate you on the network",
pers: [
{
name: 'write',
vane: 'jael',
tail: null,
}
]
desc: 'Read or change your network keys',
warn: 'This permission is required for the app to work properly',
pers: [],
have: 'nil',
}
]
}
},
],
any: [
{
kind: {
nom: 'write',
nom: 'Send Notifications',
pes: [
{
desc: 'Manage system utilities and data',
desc: 'Receive notifications via Hark',
have: "nil",
warn: "This app can execute commands on your computer",
pers: [
{
name: 'write',
vane: 'clay',
tail: null,
}
]
warn: null,
pers: []
}
]
}
}
],
any: [
new: [
{
kind: {
nom: 'write',
nom: 'Manage system utilities and data',
pes: [
{
desc: 'Send notifications',
desc: 'Update your Landscape and Urbit settings',
have: "nil",
warn: null,
pers: [
{
name: 'write',
vane: null,
tail: null,
}
]
pers: []
}
]
}
}
},
],
new: [],
app: [],
};

Expand Down
14 changes: 8 additions & 6 deletions ui/src/permissions/usePassport.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { Passport } from "@/gear";
import { Passport, Seal, sealToPassport } from "@/gear";
import { useEffect, useState } from "react";
import { fakePassport } from './temp';

export default function usePassport() {
export default function usePassport({ desk, seal }: { desk: string, seal: Seal }) {
const [passport, setPassport] = useState<Passport | null>(null);

const fetchPassport = async () => {
const response = await sealToPassport(desk, seal);
setPassport(response);
};

useEffect(() => {
setTimeout(() => {
setPassport(fakePassport);
} , 2000);
fetchPassport();
}, []);

return { passport };
Expand Down
2 changes: 1 addition & 1 deletion ui/src/permissions/usePermissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export default function usePermissions() {
const treaty = useTreaty(host, desk);
const docket = charge || treaty;
const appName = getAppName(docket);
const { passport } = usePassport(); // TODO: pass in desk
const [ship,] = useRemoteDesk(docket, pike, treaty?.ship);
const { passport } = usePassport({ desk, seal: treaty?.seal });
const installStatus = useInstallStatus(docket);
const [presentableSeal, setPresentableSeal] = useState<string[] | null>(null);

Expand Down
45 changes: 23 additions & 22 deletions ui/src/stories/SummaryRow.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,40 @@ type Story = StoryObj<typeof SummaryRow>;

export const HasWarning: Story = {
args: {
summary: {
desc: 'Access your network keys',
warn: 'This permission is required for the app to work properly',
pers: [],
have: 'nil',
perm: {
kind: {
nom: 'Access your network keys',
pes: []
}
},
},
};

export const NoWarning: Story = {
args: {
summary: {
desc: 'Send notifications',
warn: null,
pers: [],
have: 'nil',
perm: {
kind: {
nom: 'Access your network keys',
pes: []
}
},
},
};

export const Eyre: Story = {
export const Expandable: Story = {
args: {
summary: {
desc: 'Communicate with ships or clearweb sites',
warn: null,
pers: [
{
name: 'write',
vane: 'eyre',
tail: null,
}
],
have: 'nil',
perm: {
kind: {
nom: 'Access your network keys',
pes: [
{
desc: 'Read or change your network keys',
warn: 'This permission is required for the app to work properly',
pers: [],
have: 'nil',
}
]
}
},
},
};

0 comments on commit f2d8c20

Please sign in to comment.