-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Settings: Garden permissions #860
base: staging
Are you sure you want to change the base?
Changes from all commits
aa8498b
261a10d
7b0ad3d
88f764c
41efc9d
0acb24b
1ec2222
e1cbe78
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,23 +11,48 @@ import { | |
useTheme, | ||
useViewport, | ||
} from '@1hive/1hive-ui' | ||
import styled from 'styled-components' | ||
import { Transition, animated } from 'react-spring/renderprops' | ||
import { useConnectedGarden } from '@providers/ConnectedGarden' | ||
import { useWallet } from '@/providers/Wallet' | ||
import { useEsc } from '../../../hooks/useKeyboardArrows' | ||
|
||
import AppsAddresses from './AppsAddresses' | ||
import EVMExecutor from './EVMExecutor' | ||
import Permissions from './Permissions' | ||
|
||
|
||
const LayoutWrapper = styled(Layout)` | ||
z-index: 2; | ||
height: 100%; | ||
` | ||
const ParentDiv = styled.div` | ||
display: flex; | ||
height: 100%; | ||
outline: 0; | ||
` | ||
|
||
const RootWrapper = styled(Root.Provider)` | ||
display: flex; | ||
flex-direction: column; | ||
height: 100%; | ||
` | ||
|
||
const HeaderWrapper = styled(Header)` | ||
padding-top: ${(props) => !props.compact ? 10 * GU : 0}px; | ||
` | ||
|
||
const SECTIONS = new Map([ | ||
['generalInfo', 'General Info'], | ||
['evmExecutor', 'EVM Executor'], | ||
['permissions', 'Permissions'] | ||
]) | ||
const PATHS = Array.from(SECTIONS.keys()) | ||
const VALUES = Array.from(SECTIONS.values()) | ||
|
||
const GENERAL_INFO_INDEX = 0 | ||
const EVM_EXECUTOR_INDEX = 1 | ||
const PERMISSIONS_INDEX = 2 | ||
|
||
const AnimatedDiv = animated.div | ||
|
||
|
@@ -65,17 +90,18 @@ function GlobalPreferences({ compact, onClose, onNavigation, sectionIndex }) { | |
getEvmCrispr() | ||
}, [account, connectedGarden, ethers, isSafari]) | ||
|
||
console.log('sectionIndex ', sectionIndex) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe remove the console log here |
||
|
||
return ( | ||
<div ref={container} tabIndex="0" css="outline: 0"> | ||
<Layout css="z-index: 2"> | ||
<ParentDiv ref={container} tabIndex="0"> | ||
<LayoutWrapper> | ||
<Close compact={compact} onClick={onClose} /> | ||
<Header | ||
primary="Global preferences" | ||
css={` | ||
padding-top: ${!compact ? 10 * GU : 0}px; | ||
`} | ||
/> | ||
<Root.Provider> | ||
<HeaderWrapper | ||
compact={compact} | ||
> | ||
<Header primary="Global preferences" /> | ||
</HeaderWrapper> | ||
<RootWrapper> | ||
<React.Fragment> | ||
<Tabs | ||
items={VALUES} | ||
|
@@ -87,10 +113,11 @@ function GlobalPreferences({ compact, onClose, onNavigation, sectionIndex }) { | |
{sectionIndex === EVM_EXECUTOR_INDEX && ( | ||
<EVMExecutor evmcrispr={evmcrispr} /> | ||
)} | ||
{sectionIndex === PERMISSIONS_INDEX && <Permissions />} | ||
</React.Fragment> | ||
</Root.Provider> | ||
</Layout> | ||
</div> | ||
</RootWrapper> | ||
</LayoutWrapper> | ||
</ParentDiv> | ||
) | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
/* eslint-disable react/jsx-key */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe the problem with names don't show up could be related to it. Same if it's not, I'll recommend remove it and set proper keys attributes to react render properly There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we really need this line also. |
||
import React from 'react' | ||
import { AppBadge, DataView, IdentityBadge, textStyle } from '@1hive/1hive-ui' | ||
import { useGardenState } from '@providers/GardenState' | ||
import { useRoles } from '@/hooks/useRoles' | ||
import Loader from '@components/Loader' | ||
|
||
const ENTRIES_PER_PAGE = 10 | ||
|
||
interface EntryProps { | ||
appAddress: string, | ||
appIcon: string, | ||
appName: string, | ||
description: string, | ||
manager: any, | ||
permissions: any | ||
} | ||
|
||
|
||
function Permissions() { | ||
const [appRoles, loading] = useRoles() | ||
const { installedApps } = useGardenState() | ||
|
||
const fields = [ | ||
'Action', | ||
'On app', | ||
{ label: 'Assigned to entity', childStart: true }, | ||
'Managed by', | ||
] | ||
|
||
return ( | ||
<div css={`height: 100%;`}> | ||
{loading ? | ||
( | ||
|
||
<Loader /> | ||
) : | ||
<DataView | ||
entriesPerPage={ENTRIES_PER_PAGE} | ||
fields={fields} | ||
entries={appRoles} | ||
renderEntry={(entry: EntryProps) => renderEntry(entry, installedApps)} | ||
renderEntryExpansion={(entry: EntryProps) => renderEntryExpansion(entry, installedApps)} | ||
/> | ||
} | ||
</div> | ||
) | ||
} | ||
|
||
interface EntryProps { | ||
appAddress: string, | ||
appIcon: string, | ||
appName: string, | ||
description: string, | ||
manager: any, | ||
permissions: any | ||
} | ||
|
||
function EntryEntities({ permissions, installedApps }: { permissions: any, installedApps: any }) { | ||
const allowedEntities = permissions.filter((permission: any) => permission.allowed === true) | ||
|
||
if (allowedEntities.length === 1) { | ||
return <EntityBadge address={permissions[0].granteeAddress} name={permissions[0].granteeName} icon={permissions[0].granteeIcon} installedApps={installedApps} /> | ||
} | ||
|
||
return <span | ||
css={` | ||
${textStyle('body2')} | ||
`} | ||
> | ||
{allowedEntities.length} entities | ||
</span> | ||
} | ||
|
||
function EntityBadge({ address, name, icon, installedApps }: { address: string, name: string, icon: string, installedApps: any }) { | ||
if (installedApps.find((app: any) => app.address === address)) { | ||
|
||
return <AppBadge appAddress={address} iconSrc={icon} label={name} entity={address} /> | ||
} | ||
|
||
return <IdentityBadge label={name} entity={address} /> | ||
} | ||
|
||
|
||
function renderEntry(entry: EntryProps, installedApps: any) { | ||
const { appAddress, appIcon, appName, description, manager, permissions } = entry | ||
|
||
const cells = [ | ||
<span | ||
css={` | ||
${textStyle('body2')} | ||
`} | ||
> | ||
{description} | ||
</span>, | ||
<AppBadge appAddress={appAddress} iconSrc={appIcon} label={appName} entity={appAddress} />, | ||
<EntryEntities permissions={permissions} installedApps={installedApps} />, | ||
<EntityBadge address={manager.address} name={manager.shortenedName} icon={manager.managerIcon} installedApps={installedApps} /> | ||
|
||
] | ||
return cells | ||
} | ||
|
||
function renderEntryExpansion(entry: EntryProps, installedApps: any) { | ||
const allowedEntities = entry.permissions.filter((permission: any) => permission.allowed === true) | ||
|
||
return allowedEntities.length < 2 | ||
? null | ||
: allowedEntities.map((entity: any) => ( | ||
<EntityBadge address={entity.granteeAddress} name={entity.granteeName} icon={entity.granteeIcon} installedApps={installedApps} /> | ||
)) | ||
} | ||
|
||
|
||
|
||
|
||
|
||
export default Permissions |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -226,4 +226,4 @@ export function useTokenBalances( | |
}, [account, balances, timeout, tokenContract, token]) | ||
|
||
return balances | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { useEffect, useMemo, useState } from 'react' | ||
import { useGardenState } from '@providers/GardenState' | ||
import usePromise from '@hooks/usePromise' | ||
import { getAppPresentationByAddress } from '@utils/app-utils' | ||
|
||
const ANY_ADDRESS = "0xffffffffffffffffffffffffffffffffffffffff" | ||
|
||
interface ManagerProps { | ||
address: string, | ||
shortenedName: string, | ||
} | ||
|
||
interface PermissionProps { | ||
allowed: boolean, | ||
appAddress: string, | ||
granteeAddress: string, | ||
granteeName: string | undefined, | ||
params: Array<any>, | ||
roleHash: string | ||
} | ||
|
||
interface RoleProps { | ||
appAddress: string, | ||
appName: string, | ||
appIcon: string, | ||
description: string | undefined, // TODO- check why this is happening proabably old ABI | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are still getting undefined here some times? |
||
hash: string, | ||
manager: Array<ManagerProps>, | ||
name: string, | ||
params: Array<any>, | ||
permissions: Array<PermissionProps> | ||
} | ||
|
||
export function useRoles() { | ||
const [loading, setLoading] = useState<boolean>(true) | ||
const gardenState = useGardenState() | ||
|
||
const appsWithPermissions = useMemo(() => { | ||
if (!gardenState?.installedApps) { | ||
return async () => { null } | ||
} | ||
return () => Promise.all(gardenState?.installedApps.map(async (app: any) => { | ||
return { | ||
...app, | ||
roles: await app.roles() | ||
} | ||
})) | ||
}, [gardenState]) | ||
|
||
const appsWithRolesResolved = usePromise(appsWithPermissions, [], []) | ||
|
||
function isAnyAddress(address: string) { | ||
return address === ANY_ADDRESS | ||
} | ||
|
||
const rolesWithEntitiesResolved: Array<RoleProps> = appsWithRolesResolved ? appsWithRolesResolved.map((app: any) => { | ||
return app.roles.map((role: any) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's possible we get the correct type here in instead any, it could help track changes in the code below |
||
const appPresentation = getAppPresentationByAddress(gardenState?.installedApps, app.address) | ||
|
||
return { | ||
...role, | ||
appName: appPresentation?.shortenedName || appPresentation?.humanName, | ||
appIcon: appPresentation?.iconSrc, | ||
manager: { | ||
address: role.manager, | ||
shortenedName: getAppPresentationByAddress(gardenState?.installedApps, role.manager)?.shortenedName, | ||
managerIcon: getAppPresentationByAddress(gardenState?.installedApps, role.manager)?.iconSrc | ||
}, | ||
permissions: role.permissions.map((permission: any) => { | ||
return { | ||
...permission, | ||
granteeName: isAnyAddress(permission.granteeAddress) ? 'Any account' : getAppPresentationByAddress(gardenState?.installedApps, permission.granteeAddress)?.shortenedName, | ||
granteeIcon: getAppPresentationByAddress(gardenState?.installedApps, permission.granteeAddress)?.iconSrc | ||
} | ||
}) | ||
} | ||
}) | ||
}) : null | ||
|
||
useEffect(() => { | ||
if (rolesWithEntitiesResolved) { | ||
setLoading(false) | ||
} | ||
}, [rolesWithEntitiesResolved]) | ||
|
||
|
||
|
||
return [rolesWithEntitiesResolved ? rolesWithEntitiesResolved.flat() : [], loading] | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice 🔥
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah loved that too