feat: UI integrate ens dropdown (#143)
* chore: get ens names from address * wip: ens validation * wip: combobox with option to add new items * chore: add trim words * chore: change order steps * chore: add comments * chore: change components test view * chore: remove unused file * chore: add alchemy-sdk as prod dependency * chore: pr comments
This commit is contained in:
parent
8e309ee04c
commit
e5d28251c4
|
|
@ -1,4 +1,11 @@
|
||||||
import { Address, Bytes, log, store, ethereum, BigInt } from '@graphprotocol/graph-ts';
|
import {
|
||||||
|
Address,
|
||||||
|
Bytes,
|
||||||
|
log,
|
||||||
|
store,
|
||||||
|
ethereum,
|
||||||
|
BigInt,
|
||||||
|
} from '@graphprotocol/graph-ts';
|
||||||
|
|
||||||
// Event Imports [based on the yaml config]
|
// Event Imports [based on the yaml config]
|
||||||
import {
|
import {
|
||||||
|
|
@ -35,11 +42,11 @@ import {
|
||||||
|
|
||||||
enum CollectionRoles {
|
enum CollectionRoles {
|
||||||
Owner,
|
Owner,
|
||||||
};
|
}
|
||||||
|
|
||||||
enum TokenRoles {
|
enum TokenRoles {
|
||||||
Controller,
|
Controller,
|
||||||
};
|
}
|
||||||
|
|
||||||
export function handleApproval(event: ApprovalEvent): void {
|
export function handleApproval(event: ApprovalEvent): void {
|
||||||
let entity = new Approval(
|
let entity = new Approval(
|
||||||
|
|
@ -263,22 +270,24 @@ export function handleMetadataUpdateWithIntValue(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleInitialized(event: InitializedEvent): void {
|
export function handleInitialized(event: InitializedEvent): void {
|
||||||
// This is the contract creation transaction.
|
// This is the contract creation transaction.
|
||||||
log.warning('This is the contract creation transaction.', []);
|
log.warning('This is the contract creation transaction.', []);
|
||||||
if (event.receipt) {
|
if (event.receipt) {
|
||||||
let receipt = event.receipt as ethereum.TransactionReceipt;
|
let receipt = event.receipt as ethereum.TransactionReceipt;
|
||||||
log.warning('Contract address is: {}', [
|
log.warning('Contract address is: {}', [
|
||||||
receipt.contractAddress.toHexString(),
|
receipt.contractAddress.toHexString(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// add owner
|
// add owner
|
||||||
let owner = new Owner(event.transaction.from);
|
let owner = new Owner(event.transaction.from);
|
||||||
owner.collection = true;
|
owner.collection = true;
|
||||||
owner.save();
|
owner.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleCollectionRoleChanged(event: CollectionRoleChangedEvent): void {
|
export function handleCollectionRoleChanged(
|
||||||
|
event: CollectionRoleChangedEvent
|
||||||
|
): void {
|
||||||
let toAddress = event.params.toAddress;
|
let toAddress = event.params.toAddress;
|
||||||
let byAddress = event.params.byAddress;
|
let byAddress = event.params.byAddress;
|
||||||
let role = event.params.role;
|
let role = event.params.role;
|
||||||
|
|
@ -298,14 +307,21 @@ export function handleCollectionRoleChanged(event: CollectionRoleChangedEvent):
|
||||||
// revoked
|
// revoked
|
||||||
let owner = Owner.load(toAddress);
|
let owner = Owner.load(toAddress);
|
||||||
if (!owner) {
|
if (!owner) {
|
||||||
log.error('Owner entity not found. Role: {}, byAddress: {}, toAddress: {}', [role.toString(), byAddress.toHexString(), toAddress.toHexString()]);
|
log.error(
|
||||||
|
'Owner entity not found. Role: {}, byAddress: {}, toAddress: {}',
|
||||||
|
[role.toString(), byAddress.toHexString(), toAddress.toHexString()]
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
owner.collection = false;
|
owner.collection = false;
|
||||||
owner.save();
|
owner.save();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.error('Role not supported. Role: {}, byAddress: {}, toAddress: {}', [role.toString(), byAddress.toHexString(), toAddress.toHexString()]);
|
log.error('Role not supported. Role: {}, byAddress: {}, toAddress: {}', [
|
||||||
|
role.toString(),
|
||||||
|
byAddress.toHexString(),
|
||||||
|
toAddress.toHexString(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -343,11 +359,17 @@ export function handleTokenRoleChanged(event: TokenRoleChangedEvent): void {
|
||||||
}
|
}
|
||||||
token.controllers = token_controllers;
|
token.controllers = token_controllers;
|
||||||
} else {
|
} else {
|
||||||
log.error('Role not supported. Role: {}, byAddress: {}, toAddress: {}', [role.toString(), byAddress.toHexString(), toAddress.toHexString()]);
|
log.error('Role not supported. Role: {}, byAddress: {}, toAddress: {}', [
|
||||||
|
role.toString(),
|
||||||
|
byAddress.toHexString(),
|
||||||
|
toAddress.toHexString(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleMetadataUpdateWithBooleanValue(event: MetadataUpdateEvent3): void {
|
export function handleMetadataUpdateWithBooleanValue(
|
||||||
|
event: MetadataUpdateEvent3
|
||||||
|
): void {
|
||||||
/**
|
/**
|
||||||
* accessPointAutoApproval
|
* accessPointAutoApproval
|
||||||
*/
|
*/
|
||||||
|
|
@ -364,7 +386,9 @@ export function handleMetadataUpdateWithBooleanValue(event: MetadataUpdateEvent3
|
||||||
|
|
||||||
entity.save();
|
entity.save();
|
||||||
|
|
||||||
let token = Token.load(Bytes.fromByteArray(Bytes.fromBigInt(event.params._tokenId)));
|
let token = Token.load(
|
||||||
|
Bytes.fromByteArray(Bytes.fromBigInt(event.params._tokenId))
|
||||||
|
);
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
if (event.params.key == 'accessPointAutoApproval') {
|
if (event.params.key == 'accessPointAutoApproval') {
|
||||||
|
|
@ -420,14 +444,13 @@ export function handleTransfer(event: TransferEvent): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This handler will create and load entities in the following order:
|
* This handler will create and load entities in the following order:
|
||||||
* - AccessPoint [create]
|
* - AccessPoint [create]
|
||||||
* - Owner [load / create]
|
* - Owner [load / create]
|
||||||
* Note to discuss later: Should a `NewAccessPoint` entity be also created and defined?
|
* Note to discuss later: Should a `NewAccessPoint` entity be also created and defined?
|
||||||
*/
|
*/
|
||||||
export function handleNewAccessPoint(event: NewAccessPointEvent): void {
|
export function handleNewAccessPoint(event: NewAccessPointEvent): void {
|
||||||
// Create an AccessPoint entity
|
// Create an AccessPoint entity
|
||||||
let accessPointEntity = new AccessPoint(event.params.apName);
|
let accessPointEntity = new AccessPoint(event.params.apName);
|
||||||
accessPointEntity.score = BigInt.fromU32(0);
|
accessPointEntity.score = BigInt.fromU32(0);
|
||||||
|
|
@ -435,7 +458,9 @@ export function handleTransfer(event: TransferEvent): void {
|
||||||
accessPointEntity.nameVerified = false;
|
accessPointEntity.nameVerified = false;
|
||||||
accessPointEntity.creationStatus = 'DRAFT'; // Since a `ChangeAccessPointCreationStatus` event is emitted instantly after `NewAccessPoint`, the status will be updated in that handler.
|
accessPointEntity.creationStatus = 'DRAFT'; // Since a `ChangeAccessPointCreationStatus` event is emitted instantly after `NewAccessPoint`, the status will be updated in that handler.
|
||||||
accessPointEntity.owner = event.params.owner;
|
accessPointEntity.owner = event.params.owner;
|
||||||
accessPointEntity.token = Bytes.fromByteArray(Bytes.fromBigInt(event.params.tokenId));
|
accessPointEntity.token = Bytes.fromByteArray(
|
||||||
|
Bytes.fromBigInt(event.params.tokenId)
|
||||||
|
);
|
||||||
|
|
||||||
// Load / Create an Owner entity
|
// Load / Create an Owner entity
|
||||||
let ownerEntity = Owner.load(event.params.owner);
|
let ownerEntity = Owner.load(event.params.owner);
|
||||||
|
|
@ -453,7 +478,9 @@ export function handleTransfer(event: TransferEvent): void {
|
||||||
/**
|
/**
|
||||||
* This handler will update the status of an access point entity.
|
* This handler will update the status of an access point entity.
|
||||||
*/
|
*/
|
||||||
export function handleChangeAccessPointCreationStatus(event: ChangeAccessPointCreationStatusEvent): void {
|
export function handleChangeAccessPointCreationStatus(
|
||||||
|
event: ChangeAccessPointCreationStatusEvent
|
||||||
|
): void {
|
||||||
// Load the AccessPoint entity
|
// Load the AccessPoint entity
|
||||||
let accessPointEntity = AccessPoint.load(event.params.apName);
|
let accessPointEntity = AccessPoint.load(event.params.apName);
|
||||||
let status = event.params.status;
|
let status = event.params.status;
|
||||||
|
|
@ -474,20 +501,28 @@ export function handleChangeAccessPointCreationStatus(event: ChangeAccessPointCr
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Unknown status
|
// Unknown status
|
||||||
log.error('Unable to handle ChangeAccessPointCreationStatus. Unknown status. Status: {}, AccessPoint: {}', [status.toString(), event.params.apName]);
|
log.error(
|
||||||
|
'Unable to handle ChangeAccessPointCreationStatus. Unknown status. Status: {}, AccessPoint: {}',
|
||||||
|
[status.toString(), event.params.apName]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
accessPointEntity.save();
|
accessPointEntity.save();
|
||||||
} else {
|
} else {
|
||||||
// Unknown access point
|
// Unknown access point
|
||||||
log.error('Unable to handle ChangeAccessPointCreationStatus. Unknown access point. Status: {}, AccessPoint: {}', [status.toString(), event.params.apName]);
|
log.error(
|
||||||
|
'Unable to handle ChangeAccessPointCreationStatus. Unknown access point. Status: {}, AccessPoint: {}',
|
||||||
|
[status.toString(), event.params.apName]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This handler will update the score of an access point entity.
|
* This handler will update the score of an access point entity.
|
||||||
*/
|
*/
|
||||||
export function handleChangeAccessPointScore(event: ChangeAccessPointCreationScoreEvent): void {
|
export function handleChangeAccessPointScore(
|
||||||
|
event: ChangeAccessPointCreationScoreEvent
|
||||||
|
): void {
|
||||||
// Load the AccessPoint entity
|
// Load the AccessPoint entity
|
||||||
let accessPointEntity = AccessPoint.load(event.params.apName);
|
let accessPointEntity = AccessPoint.load(event.params.apName);
|
||||||
|
|
||||||
|
|
@ -496,14 +531,19 @@ export function handleChangeAccessPointCreationStatus(event: ChangeAccessPointCr
|
||||||
accessPointEntity.save();
|
accessPointEntity.save();
|
||||||
} else {
|
} else {
|
||||||
// Unknown access point
|
// Unknown access point
|
||||||
log.error('Unable to handle ChangeAccessPointScore. Unknown access point. Score: {}, AccessPoint: {}', [event.params.score.toString(), event.params.apName]);
|
log.error(
|
||||||
|
'Unable to handle ChangeAccessPointScore. Unknown access point. Score: {}, AccessPoint: {}',
|
||||||
|
[event.params.score.toString(), event.params.apName]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This handler will update the nameVerified field of an access point entity.
|
* This handler will update the nameVerified field of an access point entity.
|
||||||
*/
|
*/
|
||||||
export function handleChangeAccessPointNameVerify(event: ChangeAccessPointNameVerifyEvent): void {
|
export function handleChangeAccessPointNameVerify(
|
||||||
|
event: ChangeAccessPointNameVerifyEvent
|
||||||
|
): void {
|
||||||
// Load the AccessPoint entity
|
// Load the AccessPoint entity
|
||||||
let accessPointEntity = AccessPoint.load(event.params.apName);
|
let accessPointEntity = AccessPoint.load(event.params.apName);
|
||||||
|
|
||||||
|
|
@ -512,14 +552,19 @@ export function handleChangeAccessPointCreationStatus(event: ChangeAccessPointCr
|
||||||
accessPointEntity.save();
|
accessPointEntity.save();
|
||||||
} else {
|
} else {
|
||||||
// Unknown access point
|
// Unknown access point
|
||||||
log.error('Unable to handle ChangeAccessPointNameVerify. Unknown access point. Verified: {}, AccessPoint: {}', [event.params.verified.toString(), event.params.apName]);
|
log.error(
|
||||||
|
'Unable to handle ChangeAccessPointNameVerify. Unknown access point. Verified: {}, AccessPoint: {}',
|
||||||
|
[event.params.verified.toString(), event.params.apName]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This handler will update the contentVerified field of an access point entity.
|
* This handler will update the contentVerified field of an access point entity.
|
||||||
*/
|
*/
|
||||||
export function handleChangeAccessPointContentVerify(event: ChangeAccessPointContentVerifyEvent): void {
|
export function handleChangeAccessPointContentVerify(
|
||||||
|
event: ChangeAccessPointContentVerifyEvent
|
||||||
|
): void {
|
||||||
// Load the AccessPoint entity
|
// Load the AccessPoint entity
|
||||||
let accessPointEntity = AccessPoint.load(event.params.apName);
|
let accessPointEntity = AccessPoint.load(event.params.apName);
|
||||||
|
|
||||||
|
|
@ -528,6 +573,9 @@ export function handleChangeAccessPointCreationStatus(event: ChangeAccessPointCr
|
||||||
accessPointEntity.save();
|
accessPointEntity.save();
|
||||||
} else {
|
} else {
|
||||||
// Unknown access point
|
// Unknown access point
|
||||||
log.error('Unable to handle ChangeAccessPointContentVerify. Unknown access point. Verified: {}, AccessPoint: {}', [event.params.verified.toString(), event.params.apName]);
|
log.error(
|
||||||
|
'Unable to handle ChangeAccessPointContentVerify. Unknown access point. Verified: {}, AccessPoint: {}',
|
||||||
|
[event.params.verified.toString(), event.params.apName]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
"@reduxjs/toolkit": "^1.9.1",
|
"@reduxjs/toolkit": "^1.9.1",
|
||||||
"@stitches/react": "^1.2.8",
|
"@stitches/react": "^1.2.8",
|
||||||
"abitype": "^0.5.0",
|
"abitype": "^0.5.0",
|
||||||
|
"alchemy-sdk": "^2.5.0",
|
||||||
"colorthief": "^2.3.2",
|
"colorthief": "^2.3.2",
|
||||||
"connectkit": "^1.1.3",
|
"connectkit": "^1.1.3",
|
||||||
"firebase": "^9.17.1",
|
"firebase": "^9.17.1",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { BrowserRouter, Route, Routes, Navigate } from 'react-router-dom';
|
import { BrowserRouter, Route, Routes, Navigate } from 'react-router-dom';
|
||||||
import { themeGlobals } from '@/theme/globals';
|
import { themeGlobals } from '@/theme/globals';
|
||||||
import { Home, Mint } from './views';
|
import { ComponentsTest, Home, Mint } from './views';
|
||||||
import { SVGTestScreen } from './views/svg-test'; // TODO: remove when done
|
import { SVGTestScreen } from './views/svg-test'; // TODO: remove when done
|
||||||
import { ConnectKitButton } from 'connectkit';
|
import { ConnectKitButton } from 'connectkit';
|
||||||
import { MintTest } from './views/mint-test';
|
import { MintTest } from './views/mint-test';
|
||||||
|
|
@ -18,6 +18,8 @@ export const App = () => {
|
||||||
<Route path="/home" element={<Home />} />
|
<Route path="/home" element={<Home />} />
|
||||||
<Route path="/mint" element={<Mint />} />
|
<Route path="/mint" element={<Mint />} />
|
||||||
<Route path="/svg" element={<SVGTestScreen />} />
|
<Route path="/svg" element={<SVGTestScreen />} />
|
||||||
|
{/** TODO remove for release */}
|
||||||
|
<Route path="/components-test" element={<ComponentsTest />} />
|
||||||
<Route path="/mint-test" element={<MintTest />} />
|
<Route path="/mint-test" element={<MintTest />} />
|
||||||
<Route path="*" element={<Navigate to="/home" />} />
|
<Route path="*" element={<Navigate to="/home" />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,60 @@
|
||||||
import React, { Fragment, useRef, useState } from 'react';
|
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
||||||
import { Combobox as ComboboxLib, Transition } from '@headlessui/react';
|
import { Combobox as ComboboxLib, Transition } from '@headlessui/react';
|
||||||
import { Icon } from '@/components/core/icon';
|
import { Icon, IconName } from '@/components/core/icon';
|
||||||
import { Flex } from '@/components/layout';
|
import { Flex } from '@/components/layout';
|
||||||
|
import { useDebounce } from '@/hooks/use-debounce';
|
||||||
|
import { Separator } from '../separator.styles';
|
||||||
|
import { cleanString } from './combobox.utils';
|
||||||
|
|
||||||
type ComboboxInputProps = {
|
type ComboboxInputProps = {
|
||||||
|
/**
|
||||||
|
* If it's true, the list of options will be displayed
|
||||||
|
*/
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
/**
|
||||||
|
* Name of the left icon to display in the input
|
||||||
|
*/
|
||||||
|
leftIcon: IconName;
|
||||||
|
/**
|
||||||
|
* Function to handle the input change
|
||||||
|
*/
|
||||||
handleInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
handleInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
/**
|
||||||
|
* Function to handle the input click. When the user clicks on the input, the list of options will be displayed
|
||||||
|
*/
|
||||||
handleInputClick: () => void;
|
handleInputClick: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ComboboxInput = ({
|
const ComboboxInput = ({
|
||||||
open,
|
open,
|
||||||
|
leftIcon,
|
||||||
handleInputChange,
|
handleInputChange,
|
||||||
handleInputClick,
|
handleInputClick,
|
||||||
}: ComboboxInputProps) => (
|
}: ComboboxInputProps) => (
|
||||||
<div className="relative w-full cursor-default ">
|
<div className="relative w-full cursor-default ">
|
||||||
<Icon
|
<Icon
|
||||||
name="search"
|
name={leftIcon}
|
||||||
size="sm"
|
size="sm"
|
||||||
css={{
|
css={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: '$3',
|
left: '$3',
|
||||||
top: '0.9375rem',
|
top: '$3',
|
||||||
|
fontSize: '$xl',
|
||||||
color: 'slate8',
|
color: 'slate8',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ComboboxLib.Input
|
<ComboboxLib.Input
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
className={`w-full border-solid border border-slate7 h-11 py-3 pl-8 pr-10 text-sm bg-transparent leading-5 text-slate11 outline-none ${
|
className={`w-full border-solid border border-slate7 h-11 py-3 pl-10 pr-10 text-sm bg-transparent leading-5 text-slate11 outline-none ${
|
||||||
open ? 'border-b-0 rounded-t-xl bg-black border-slate6' : 'rounded-xl'
|
open ? 'border-b-0 rounded-t-xl bg-black border-slate6' : 'rounded-xl'
|
||||||
}`}
|
}`}
|
||||||
displayValue={(selectedValue: ComboboxItem) => selectedValue.label}
|
displayValue={(selectedValue: ComboboxItem) => selectedValue.label}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
onClick={handleInputClick}
|
onClick={handleInputClick}
|
||||||
/>
|
/>
|
||||||
|
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-4">
|
||||||
|
<Icon name="chevron-down" css={{ fontSize: '$xs' }} />
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -52,9 +73,13 @@ const ComboboxOption = ({ option }: ComboboxOptionProps) => (
|
||||||
>
|
>
|
||||||
{({ selected, active }) => (
|
{({ selected, active }) => (
|
||||||
<Flex css={{ justifyContent: 'space-between' }}>
|
<Flex css={{ justifyContent: 'space-between' }}>
|
||||||
<Flex css={{ flexDirection: 'row' }}>
|
<Flex css={{ flexDirection: 'row', maxWidth: '95%' }}>
|
||||||
{option.icon}
|
{option.icon}
|
||||||
<span className={`${active ? 'text-slate12' : 'text-slate11'}`}>
|
<span
|
||||||
|
className={`${active ? 'text-slate12' : 'text-slate11'} ${
|
||||||
|
option.icon ? 'max-w-70' : 'max-w-full'
|
||||||
|
} whitespace-nowrap text-ellipsis overflow-hidden`}
|
||||||
|
>
|
||||||
{option.label}
|
{option.label}
|
||||||
</span>
|
</span>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
@ -73,38 +98,97 @@ export const NoResults = ({ css }: { css?: string }) => (
|
||||||
);
|
);
|
||||||
|
|
||||||
export type ComboboxItem = {
|
export type ComboboxItem = {
|
||||||
|
/**
|
||||||
|
* The key of the item.
|
||||||
|
*/
|
||||||
value: string;
|
value: string;
|
||||||
|
/**
|
||||||
|
* The label to display of the item.
|
||||||
|
*/
|
||||||
label: string;
|
label: string;
|
||||||
|
/**
|
||||||
|
* Optional icon to display on the left of the item.
|
||||||
|
*/
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ComboboxProps = {
|
export type ComboboxProps = {
|
||||||
|
/**
|
||||||
|
* List of items to be displayed in the combobox.
|
||||||
|
*/
|
||||||
items: ComboboxItem[];
|
items: ComboboxItem[];
|
||||||
|
/**
|
||||||
|
* The selected value of the combobox.
|
||||||
|
*/
|
||||||
selectedValue: ComboboxItem | undefined;
|
selectedValue: ComboboxItem | undefined;
|
||||||
|
/**
|
||||||
|
* If true, the combobox will add the input if it doesn't exist in the list of items.
|
||||||
|
*/
|
||||||
|
withAutocomplete?: boolean;
|
||||||
|
/**
|
||||||
|
* Name of the left icon to display in the input. Defualt is "search".
|
||||||
|
*/
|
||||||
|
leftIcon?: IconName;
|
||||||
|
/**
|
||||||
|
* Callback when the selected value changes.
|
||||||
|
*/
|
||||||
onChange(option: ComboboxItem): void;
|
onChange(option: ComboboxItem): void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Combobox: React.FC<ComboboxProps> = ({
|
export const Combobox: React.FC<ComboboxProps> = ({
|
||||||
items,
|
items,
|
||||||
selectedValue = { value: '', label: '' },
|
selectedValue = { value: '', label: '' },
|
||||||
|
withAutocomplete = false,
|
||||||
|
leftIcon = 'search',
|
||||||
onChange,
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
const [query, setQuery] = useState('');
|
const [filteredItems, setFilteredItems] = useState<ComboboxItem[]>([]);
|
||||||
|
const [autocompleteItems, setAutocompleteItems] = useState<ComboboxItem[]>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// If the selected value doesn't exist in the list of items, we add it
|
||||||
|
if (
|
||||||
|
items.filter((item) => item === selectedValue).length === 0 &&
|
||||||
|
selectedValue.value !== undefined &&
|
||||||
|
autocompleteItems.length === 0 &&
|
||||||
|
withAutocomplete
|
||||||
|
) {
|
||||||
|
setAutocompleteItems([selectedValue]);
|
||||||
|
}
|
||||||
|
}, [selectedValue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFilteredItems(items);
|
||||||
|
}, [items]);
|
||||||
|
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
const filteredItems =
|
const handleSearch = useDebounce((searchValue: string) => {
|
||||||
query === ''
|
if (searchValue === '') {
|
||||||
? items
|
setFilteredItems(items);
|
||||||
: items.filter((person) =>
|
|
||||||
person.label
|
if (withAutocomplete) {
|
||||||
.toLowerCase()
|
setAutocompleteItems([]);
|
||||||
.replace(/\s+/g, '')
|
handleComboboxChange({} as ComboboxItem);
|
||||||
.includes(query.toLowerCase().replace(/\s+/g, ''))
|
}
|
||||||
);
|
} else {
|
||||||
|
const filteredValues = items.filter((item) =>
|
||||||
|
cleanString(item.label).startsWith(cleanString(searchValue))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (withAutocomplete && filteredValues.length === 0) {
|
||||||
|
// If the search value doesn't exist in the list of items, we add it
|
||||||
|
setAutocompleteItems([{ value: searchValue, label: searchValue }]);
|
||||||
|
}
|
||||||
|
setFilteredItems(filteredValues);
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
|
||||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setQuery(event.target.value);
|
event.stopPropagation();
|
||||||
|
handleSearch(event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleInputClick = () => {
|
const handleInputClick = () => {
|
||||||
|
|
@ -116,7 +200,11 @@ export const Combobox: React.FC<ComboboxProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLeaveTransition = () => {
|
const handleLeaveTransition = () => {
|
||||||
setQuery('');
|
setFilteredItems(items);
|
||||||
|
if (selectedValue.value === undefined && withAutocomplete) {
|
||||||
|
setAutocompleteItems([]);
|
||||||
|
handleComboboxChange({} as ComboboxItem);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -131,6 +219,7 @@ export const Combobox: React.FC<ComboboxProps> = ({
|
||||||
handleInputChange={handleInputChange}
|
handleInputChange={handleInputChange}
|
||||||
handleInputClick={handleInputClick}
|
handleInputClick={handleInputClick}
|
||||||
open={open}
|
open={open}
|
||||||
|
leftIcon={leftIcon}
|
||||||
/>
|
/>
|
||||||
<ComboboxLib.Button ref={buttonRef} className="hidden" />
|
<ComboboxLib.Button ref={buttonRef} className="hidden" />
|
||||||
|
|
||||||
|
|
@ -144,12 +233,25 @@ export const Combobox: React.FC<ComboboxProps> = ({
|
||||||
afterLeave={handleLeaveTransition}
|
afterLeave={handleLeaveTransition}
|
||||||
>
|
>
|
||||||
<ComboboxLib.Options className="absolute max-h-60 w-full z-10 overflow-auto rounded-b-xl border-solid border-slate6 border bg-black pt-2 px-3 text-base focus:outline-none sm:text-sm">
|
<ComboboxLib.Options className="absolute max-h-60 w-full z-10 overflow-auto rounded-b-xl border-solid border-slate6 border bg-black pt-2 px-3 text-base focus:outline-none sm:text-sm">
|
||||||
{filteredItems.length === 0 && query !== '' ? (
|
{[...autocompleteItems, ...filteredItems].length === 0 ||
|
||||||
|
filteredItems === undefined ? (
|
||||||
<NoResults />
|
<NoResults />
|
||||||
) : (
|
) : (
|
||||||
filteredItems.map((option: ComboboxItem) => {
|
<>
|
||||||
return <ComboboxOption key={option.value} option={option} />;
|
{autocompleteItems.length > 0 && <span>Create new</span>}
|
||||||
})
|
{autocompleteItems.map((autocompleteOption: ComboboxItem) => (
|
||||||
|
<ComboboxOption
|
||||||
|
key={autocompleteOption.value}
|
||||||
|
option={autocompleteOption}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{autocompleteItems.length > 0 && filteredItems.length > 0 && (
|
||||||
|
<Separator css={{ mb: '$2' }} />
|
||||||
|
)}
|
||||||
|
{filteredItems.map((option: ComboboxItem) => (
|
||||||
|
<ComboboxOption key={option.value} option={option} />
|
||||||
|
))}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</ComboboxLib.Options>
|
</ComboboxLib.Options>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const cleanString = (str: string) =>
|
||||||
|
str.toLowerCase().replace(/\s+/g, '');
|
||||||
|
|
@ -3,6 +3,10 @@ export const env = Object.freeze({
|
||||||
id: import.meta.env.VITE_ALCHEMY_API_KEY || '',
|
id: import.meta.env.VITE_ALCHEMY_API_KEY || '',
|
||||||
appName: import.meta.env.VITE_ALCHEMY_APP_NAME || '',
|
appName: import.meta.env.VITE_ALCHEMY_APP_NAME || '',
|
||||||
},
|
},
|
||||||
|
ens: {
|
||||||
|
contractAddress: import.meta.env.VITE_ENS_ADDRESS || '',
|
||||||
|
validationEnsURL: import.meta.env.VITE_ENS_VALIDATION_URL || '',
|
||||||
|
},
|
||||||
firebase: {
|
firebase: {
|
||||||
apiKey: import.meta.env.VITE_FIREBASE_API_KEY || '',
|
apiKey: import.meta.env.VITE_FIREBASE_API_KEY || '',
|
||||||
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN || '',
|
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN || '',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { useRef } from 'react';
|
||||||
|
|
||||||
|
export const useDebounce = <A extends any[], F extends (...args: A) => void>(
|
||||||
|
f: F,
|
||||||
|
t = 500
|
||||||
|
) => {
|
||||||
|
const timeOutRef = useRef<NodeJS.Timeout>();
|
||||||
|
|
||||||
|
return (...args: A) => {
|
||||||
|
timeOutRef.current && clearTimeout(timeOutRef.current);
|
||||||
|
timeOutRef.current = setTimeout(() => {
|
||||||
|
f(...args);
|
||||||
|
}, t);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -1,8 +1,18 @@
|
||||||
import { JsonRpcProvider, Networkish } from '@ethersproject/providers';
|
import { JsonRpcProvider, Networkish } from '@ethersproject/providers';
|
||||||
import { ethers } from 'ethers';
|
import { ethers } from 'ethers';
|
||||||
import * as Contracts from './contracts';
|
import * as Contracts from './contracts';
|
||||||
|
import { env } from '@/constants';
|
||||||
|
import { Alchemy, Network } from 'alchemy-sdk';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
apiKey: env.alchemy.id,
|
||||||
|
network: Network.ETH_MAINNET,
|
||||||
|
};
|
||||||
|
|
||||||
|
const alchemy = new Alchemy(config);
|
||||||
|
|
||||||
export const Ethereum: Ethereum.Core = {
|
export const Ethereum: Ethereum.Core = {
|
||||||
|
//TODO remove
|
||||||
defaultNetwork: 'https://rpc-mumbai.maticvigil.com', // TODO: make it environment variable
|
defaultNetwork: 'https://rpc-mumbai.maticvigil.com', // TODO: make it environment variable
|
||||||
|
|
||||||
provider: {
|
provider: {
|
||||||
|
|
@ -20,6 +30,23 @@ export const Ethereum: Ethereum.Core = {
|
||||||
|
|
||||||
return new ethers.Contract(contract.address, contract.abi, provider);
|
return new ethers.Contract(contract.address, contract.abi, provider);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async getEnsName(address) {
|
||||||
|
const ensAddresses = await alchemy.nft.getNftsForOwner(address, {
|
||||||
|
contractAddresses: [env.ens.contractAddress],
|
||||||
|
});
|
||||||
|
|
||||||
|
return ensAddresses.ownedNfts.map((nft) => nft.title);
|
||||||
|
},
|
||||||
|
|
||||||
|
//TODO remove if we're not gonna validate ens on the client side
|
||||||
|
async validateEnsName(name) {
|
||||||
|
const provider = new ethers.providers.JsonRpcProvider(
|
||||||
|
env.ens.validationEnsURL
|
||||||
|
);
|
||||||
|
|
||||||
|
return Boolean(await provider.resolveName(name));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export namespace Ethereum {
|
export namespace Ethereum {
|
||||||
|
|
@ -36,5 +63,9 @@ export namespace Ethereum {
|
||||||
contractName: keyof typeof Contracts,
|
contractName: keyof typeof Contracts,
|
||||||
providerName?: Providers
|
providerName?: Providers
|
||||||
) => ethers.Contract;
|
) => ethers.Contract;
|
||||||
|
|
||||||
|
getEnsName: (address: string) => Promise<string[]>;
|
||||||
|
|
||||||
|
validateEnsName: (name: string) => Promise<boolean>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { Ethereum } from '@/integrations';
|
||||||
|
import { RootState } from '@/store';
|
||||||
|
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||||
|
import { ensActions } from '../ens-slice';
|
||||||
|
|
||||||
|
export const fetchEnsNamesThunk = createAsyncThunk<void, `0x${string}`>(
|
||||||
|
'ens/fetchEnsNames',
|
||||||
|
async (address, { dispatch, getState }) => {
|
||||||
|
const { state } = (getState() as RootState).ens;
|
||||||
|
if (state === 'loading') return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
dispatch(ensActions.setState('loading'));
|
||||||
|
|
||||||
|
//fetch ens names for received addresses
|
||||||
|
const ensList = await Ethereum.getEnsName(address);
|
||||||
|
|
||||||
|
dispatch(ensActions.setEnsNames(ensList));
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
dispatch(ensActions.setState('failed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './fetch-ens-names';
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { RootState, useAppSelector } from '@/store';
|
||||||
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
import * as asyncThunk from './async-thunk';
|
||||||
|
|
||||||
|
export namespace EnsState {
|
||||||
|
export type State = 'idle' | 'loading' | 'success' | 'failed';
|
||||||
|
|
||||||
|
export type EnsNames = string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnsState {
|
||||||
|
state: EnsState.State;
|
||||||
|
ensNames: EnsState.EnsNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: EnsState = {
|
||||||
|
state: 'idle',
|
||||||
|
ensNames: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ensSlice = createSlice({
|
||||||
|
name: 'ens',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setEnsNames: (state, action: PayloadAction<EnsState.EnsNames>) => {
|
||||||
|
state.ensNames = action.payload;
|
||||||
|
state.state = 'success';
|
||||||
|
},
|
||||||
|
setState: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<Exclude<EnsState.State, 'success'>>
|
||||||
|
) => {
|
||||||
|
state.state = action.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ensActions = {
|
||||||
|
...ensSlice.actions,
|
||||||
|
...asyncThunk,
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectEnsState = (state: RootState): EnsState => state.ens;
|
||||||
|
|
||||||
|
export const useEnsStore = (): EnsState => useAppSelector(selectEnsState);
|
||||||
|
|
||||||
|
export default ensSlice.reducer;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './ens-slice';
|
||||||
|
|
@ -16,8 +16,6 @@ export const fetchRepositoriesThunk = createAsyncThunk(
|
||||||
|
|
||||||
const repositories = await githubClient.fetchRepos(url);
|
const repositories = await githubClient.fetchRepos(url);
|
||||||
|
|
||||||
console.log(repositories);
|
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
githubActions.setRepositories(
|
githubActions.setRepositories(
|
||||||
repositories.map((repo: any) => ({
|
repositories.map((repo: any) => ({
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
export * from './github';
|
export * from './github';
|
||||||
|
export * from './ens';
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import { configureStore } from '@reduxjs/toolkit';
|
import { configureStore } from '@reduxjs/toolkit';
|
||||||
import githubReducer from './features/github/github-slice';
|
import githubReducer from './features/github/github-slice';
|
||||||
|
import ensReducer from './features/ens/ens-slice';
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
github: githubReducer,
|
github: githubReducer,
|
||||||
|
ens: ensReducer,
|
||||||
},
|
},
|
||||||
middleware: (getDefaultMiddleware) =>
|
middleware: (getDefaultMiddleware) =>
|
||||||
getDefaultMiddleware({
|
getDefaultMiddleware({
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { Combobox, ComboboxItem, Flex } from '@/components';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
const itemsCombobox = [
|
||||||
|
{ label: 'Item 1', value: 'item-1' },
|
||||||
|
{ label: 'Item 2', value: 'item-2' },
|
||||||
|
{ label: 'Item 3', value: 'item-3' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const ComponentsTest = () => {
|
||||||
|
const [selectedValue, setSelectedValue] = useState({} as ComboboxItem);
|
||||||
|
const [selectedValueAutocomplete, setSelectedValueAutocomplete] = useState(
|
||||||
|
{} as ComboboxItem
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleComboboxChange = (item: ComboboxItem) => {
|
||||||
|
setSelectedValue(item);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleComboboxChangeAutocomplete = (item: ComboboxItem) => {
|
||||||
|
setSelectedValueAutocomplete(item);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
css={{
|
||||||
|
flexDirection: 'column',
|
||||||
|
margin: '100px',
|
||||||
|
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: '10px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h1>Components Test</h1>
|
||||||
|
<Flex css={{ width: '400px', gap: '$2' }}>
|
||||||
|
<Combobox
|
||||||
|
items={itemsCombobox}
|
||||||
|
selectedValue={selectedValue}
|
||||||
|
onChange={handleComboboxChange}
|
||||||
|
/>
|
||||||
|
<Combobox
|
||||||
|
items={itemsCombobox}
|
||||||
|
selectedValue={selectedValueAutocomplete}
|
||||||
|
onChange={handleComboboxChangeAutocomplete}
|
||||||
|
withAutocomplete
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './components-test';
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
export * from './home';
|
export * from './home';
|
||||||
export * from './mint';
|
export * from './mint';
|
||||||
export * from './svg-test';
|
export * from './svg-test';
|
||||||
|
export * from './components-test';
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Ethereum } from '@/integrations';
|
||||||
|
|
||||||
|
//TODO remove if we're not gonna validate ens on the client side
|
||||||
|
export const validateEnsField = async (
|
||||||
|
ensName: string,
|
||||||
|
setError: (message: string) => void
|
||||||
|
) => {
|
||||||
|
const isValid = await Ethereum.validateEnsName(ensName);
|
||||||
|
if (!isValid) setError('Invalid ENS name');
|
||||||
|
else setError('');
|
||||||
|
return isValid;
|
||||||
|
};
|
||||||
|
|
@ -1,30 +1,25 @@
|
||||||
import { Card, Grid, Icon, IconButton } from '@/components';
|
import { Card, Grid, Stepper } from '@/components';
|
||||||
|
import { MintCardHeader } from '@/views/mint/mint-card';
|
||||||
import { GithubButton } from './github-button';
|
import { GithubButton } from './github-button';
|
||||||
|
|
||||||
export const GithubConnect: React.FC = () => (
|
export const GithubConnect: React.FC = () => {
|
||||||
<Card.Container>
|
const { prevStep } = Stepper.useContext();
|
||||||
<Card.Heading
|
|
||||||
title="Connect GitHub"
|
return (
|
||||||
rightIcon={
|
<Card.Container>
|
||||||
<IconButton
|
<MintCardHeader title="Connect GitHub" onClickBack={prevStep} />
|
||||||
aria-label="Add"
|
<Card.Body>
|
||||||
colorScheme="gray"
|
<Grid css={{ rowGap: '$6' }}>
|
||||||
variant="link"
|
<GithubButton />
|
||||||
icon={<Icon name="info" />}
|
<Card.Text
|
||||||
/>
|
css={{ height: '$46h', width: '$95', fontSize: '$md', px: '$12' }}
|
||||||
}
|
>
|
||||||
/>
|
<span>
|
||||||
<Card.Body>
|
After connecting your GitHub, your repositories will show here.
|
||||||
<Grid css={{ rowGap: '$6' }}>
|
</span>
|
||||||
<GithubButton />
|
</Card.Text>
|
||||||
<Card.Text
|
</Grid>
|
||||||
css={{ height: '$46h', width: '$95', fontSize: '$md', px: '$12' }}
|
</Card.Body>
|
||||||
>
|
</Card.Container>
|
||||||
<span>
|
);
|
||||||
After connecting your GitHub, your repositories will show here.
|
};
|
||||||
</span>
|
|
||||||
</Card.Text>
|
|
||||||
</Grid>
|
|
||||||
</Card.Body>
|
|
||||||
</Card.Container>
|
|
||||||
);
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { Card, ComboboxItem, Flex, Grid, Icon, Spinner } from '@/components';
|
import { Card, ComboboxItem, Flex, Grid, Icon, Spinner } from '@/components';
|
||||||
import { Input } from '@/components/core/input';
|
import { Input } from '@/components/core/input';
|
||||||
|
import { useDebounce } from '@/hooks/use-debounce';
|
||||||
import { useGithubStore } from '@/store';
|
import { useGithubStore } from '@/store';
|
||||||
import { MintCardHeader } from '@/views/mint/mint-card';
|
import { MintCardHeader } from '@/views/mint/mint-card';
|
||||||
import { Mint } from '@/views/mint/mint.context';
|
import { Mint } from '@/views/mint/mint.context';
|
||||||
import React, { forwardRef, useRef, useState } from 'react';
|
import React, { forwardRef, useState } from 'react';
|
||||||
import { RepositoriesList } from './repositories-list';
|
import { RepositoriesList } from './repositories-list';
|
||||||
import { UserOrgsCombobox } from './users-orgs-combobox';
|
import { UserOrgsCombobox } from './users-orgs-combobox';
|
||||||
|
|
||||||
|
|
@ -46,14 +47,15 @@ export const GithubRepositoryConnection: React.FC = () => {
|
||||||
|
|
||||||
const { setGithubStep, setSelectedUserOrg } = Mint.useContext();
|
const { setGithubStep, setSelectedUserOrg } = Mint.useContext();
|
||||||
|
|
||||||
const timeOutRef = useRef<NodeJS.Timeout>();
|
const setSearchValueDebounced = useDebounce(
|
||||||
|
(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
setSearchValue(event.target.value),
|
||||||
|
500
|
||||||
|
);
|
||||||
|
|
||||||
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
timeOutRef.current && clearTimeout(timeOutRef.current);
|
setSearchValueDebounced(event);
|
||||||
timeOutRef.current = setTimeout(() => {
|
|
||||||
setSearchValue(event.target.value);
|
|
||||||
}, 500);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePrevStepClick = () => {
|
const handlePrevStepClick = () => {
|
||||||
|
|
@ -73,7 +75,7 @@ export const GithubRepositoryConnection: React.FC = () => {
|
||||||
<UserOrgsCombobox />
|
<UserOrgsCombobox />
|
||||||
<Input
|
<Input
|
||||||
leftIcon="search"
|
leftIcon="search"
|
||||||
placeholder="Search"
|
placeholder="Search repo"
|
||||||
onChange={handleSearchChange}
|
onChange={handleSearchChange}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ export const UserOrgsCombobox = () => {
|
||||||
)}
|
)}
|
||||||
selectedValue={selectedUserOrg}
|
selectedValue={selectedUserOrg}
|
||||||
onChange={handleUserOrgChange}
|
onChange={handleUserOrgChange}
|
||||||
|
leftIcon="github"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,14 @@ export const MintStepper = () => {
|
||||||
<Stepper.Root initialStep={1}>
|
<Stepper.Root initialStep={1}>
|
||||||
<Stepper.Container>
|
<Stepper.Container>
|
||||||
<Stepper.Step>
|
<Stepper.Step>
|
||||||
<MintStep header="Connect GitHub and select repository">
|
<MintStep header="Connect your Ethereum Wallet to mint an NFA">
|
||||||
<GithubStep />
|
<WalletStep />
|
||||||
</MintStep>
|
</MintStep>
|
||||||
</Stepper.Step>
|
</Stepper.Step>
|
||||||
|
|
||||||
<Stepper.Step>
|
<Stepper.Step>
|
||||||
<MintStep header="Connect your Ethereum Wallet to mint an NFA">
|
<MintStep header="Connect GitHub and select repository">
|
||||||
<WalletStep />
|
<GithubStep />
|
||||||
</MintStep>
|
</MintStep>
|
||||||
</Stepper.Step>
|
</Stepper.Step>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,10 @@ export type MintContext = {
|
||||||
appDescription: string;
|
appDescription: string;
|
||||||
appLogo: string;
|
appLogo: string;
|
||||||
logoColor: string;
|
logoColor: string;
|
||||||
ens: DropdownItem;
|
ens: ComboboxItem;
|
||||||
domain: string;
|
domain: string;
|
||||||
verifyNFA: boolean;
|
verifyNFA: boolean;
|
||||||
|
ensError: string;
|
||||||
setGithubStep: (step: number) => void;
|
setGithubStep: (step: number) => void;
|
||||||
setNfaStep: (step: number) => void;
|
setNfaStep: (step: number) => void;
|
||||||
setSelectedUserOrg: (userOrg: ComboboxItem) => void;
|
setSelectedUserOrg: (userOrg: ComboboxItem) => void;
|
||||||
|
|
@ -28,9 +29,10 @@ export type MintContext = {
|
||||||
setAppDescription: (description: string) => void;
|
setAppDescription: (description: string) => void;
|
||||||
setAppLogo: (logo: string) => void;
|
setAppLogo: (logo: string) => void;
|
||||||
setLogoColor: (color: string) => void;
|
setLogoColor: (color: string) => void;
|
||||||
setEns: (ens: DropdownItem) => void;
|
setEns: (ens: ComboboxItem) => void;
|
||||||
setDomain: (domain: string) => void;
|
setDomain: (domain: string) => void;
|
||||||
setVerifyNFA: (verify: boolean) => void;
|
setVerifyNFA: (verify: boolean) => void;
|
||||||
|
setEnsError: (error: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const [MintProvider, useContext] = createContext<MintContext>({
|
const [MintProvider, useContext] = createContext<MintContext>({
|
||||||
|
|
@ -62,10 +64,13 @@ export abstract class Mint {
|
||||||
const [appDescription, setAppDescription] = useState('');
|
const [appDescription, setAppDescription] = useState('');
|
||||||
const [appLogo, setAppLogo] = useState('');
|
const [appLogo, setAppLogo] = useState('');
|
||||||
const [logoColor, setLogoColor] = useState('');
|
const [logoColor, setLogoColor] = useState('');
|
||||||
const [ens, setEns] = useState({} as DropdownItem);
|
const [ens, setEns] = useState({} as ComboboxItem);
|
||||||
const [domain, setDomain] = useState('');
|
const [domain, setDomain] = useState('');
|
||||||
const [verifyNFA, setVerifyNFA] = useState(true);
|
const [verifyNFA, setVerifyNFA] = useState(true);
|
||||||
|
|
||||||
|
//Field validations
|
||||||
|
const [ensError, setEnsError] = useState<string>('');
|
||||||
|
|
||||||
const setGithubStep = (step: number): void => {
|
const setGithubStep = (step: number): void => {
|
||||||
if (step > 0 && step <= 3) {
|
if (step > 0 && step <= 3) {
|
||||||
setGithubStepContext(step);
|
setGithubStepContext(step);
|
||||||
|
|
@ -88,6 +93,7 @@ export abstract class Mint {
|
||||||
ens,
|
ens,
|
||||||
domain,
|
domain,
|
||||||
verifyNFA,
|
verifyNFA,
|
||||||
|
ensError,
|
||||||
setSelectedUserOrg,
|
setSelectedUserOrg,
|
||||||
setGithubStep,
|
setGithubStep,
|
||||||
setNfaStep,
|
setNfaStep,
|
||||||
|
|
@ -101,6 +107,7 @@ export abstract class Mint {
|
||||||
setEns,
|
setEns,
|
||||||
setDomain,
|
setDomain,
|
||||||
setVerifyNFA,
|
setVerifyNFA,
|
||||||
|
setEnsError,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TransactionProvider
|
<TransactionProvider
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,35 @@
|
||||||
import { Dropdown, DropdownItem, Form } from '@/components';
|
import { Combobox, ComboboxItem, Form } from '@/components';
|
||||||
|
import { ensActions, useAppDispatch, useEnsStore } from '@/store';
|
||||||
import { Mint } from '@/views/mint/mint.context';
|
import { Mint } from '@/views/mint/mint.context';
|
||||||
|
import { useAccount } from 'wagmi';
|
||||||
// TODO remove after integration with wallet
|
|
||||||
const ensList: DropdownItem[] = [
|
|
||||||
{
|
|
||||||
value: 'fleek.eth',
|
|
||||||
label: 'fleek.eth',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'ens.eth',
|
|
||||||
label: 'ens.eth',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'cami.eth',
|
|
||||||
label: 'cami.eth',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const EnsField = () => {
|
export const EnsField = () => {
|
||||||
const { ens, setEns } = Mint.useContext();
|
const { ens, ensError, setEns } = Mint.useContext();
|
||||||
|
const { state, ensNames } = useEnsStore();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { address } = useAccount();
|
||||||
|
|
||||||
const handleEnsChange = (item: DropdownItem) => {
|
if (state === 'idle' && address) {
|
||||||
|
dispatch(ensActions.fetchEnsNamesThunk(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEnsChange = (item: ComboboxItem) => {
|
||||||
setEns(item);
|
setEns(item);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form.Field css={{ flex: 1 }}>
|
<Form.Field css={{ flex: 1 }}>
|
||||||
<Form.Label>ENS</Form.Label>
|
<Form.Label>ENS</Form.Label>
|
||||||
<Dropdown
|
<Combobox
|
||||||
items={ensList}
|
items={ensNames.map((ens) => ({
|
||||||
|
label: ens,
|
||||||
|
value: ens,
|
||||||
|
}))}
|
||||||
selectedValue={ens}
|
selectedValue={ens}
|
||||||
onChange={handleEnsChange}
|
onChange={handleEnsChange}
|
||||||
|
withAutocomplete
|
||||||
/>
|
/>
|
||||||
|
{ensError && <Form.Error>{ensError}</Form.Error>}
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,20 @@
|
||||||
import { Button, Card, Grid, Icon, Stepper } from '@/components';
|
import { Card, Grid, Icon, IconButton } from '@/components';
|
||||||
import { MintCardHeader } from '../mint-card';
|
|
||||||
import { ConnectWalletButton } from './connect-wallet-button';
|
import { ConnectWalletButton } from './connect-wallet-button';
|
||||||
|
|
||||||
export const WalletStep = () => {
|
export const WalletStep = () => {
|
||||||
const { prevStep } = Stepper.useContext();
|
|
||||||
return (
|
return (
|
||||||
<Card.Container>
|
<Card.Container>
|
||||||
<MintCardHeader title="Connect Wallet" onClickBack={prevStep} />
|
<Card.Heading
|
||||||
|
title="Connect Wallet"
|
||||||
|
rightIcon={
|
||||||
|
<IconButton
|
||||||
|
aria-label="Add"
|
||||||
|
colorScheme="gray"
|
||||||
|
variant="link"
|
||||||
|
icon={<Icon name="info" />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
<Grid css={{ rowGap: '$6' }}>
|
<Grid css={{ rowGap: '$6' }}>
|
||||||
<ConnectWalletButton />
|
<ConnectWalletButton />
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ module.exports = {
|
||||||
borderRadius: {
|
borderRadius: {
|
||||||
xhl: '1.25rem',
|
xhl: '1.25rem',
|
||||||
},
|
},
|
||||||
|
maxWidth: {
|
||||||
|
70: '70%',
|
||||||
|
},
|
||||||
space: {
|
space: {
|
||||||
'1h': '0.375rem',
|
'1h': '0.375rem',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
115
ui/yarn.lock
115
ui/yarn.lock
|
|
@ -1415,7 +1415,7 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ethersproject/bignumber" "^5.7.0"
|
"@ethersproject/bignumber" "^5.7.0"
|
||||||
|
|
||||||
"@ethersproject/contracts@5.7.0":
|
"@ethersproject/contracts@5.7.0", "@ethersproject/contracts@^5.7.0":
|
||||||
version "5.7.0"
|
version "5.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e"
|
resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e"
|
||||||
integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==
|
integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==
|
||||||
|
|
@ -1518,7 +1518,7 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ethersproject/logger" "^5.7.0"
|
"@ethersproject/logger" "^5.7.0"
|
||||||
|
|
||||||
"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.7.2":
|
"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.7.0", "@ethersproject/providers@^5.7.2":
|
||||||
version "5.7.2"
|
version "5.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb"
|
resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb"
|
||||||
integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==
|
integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==
|
||||||
|
|
@ -1617,7 +1617,7 @@
|
||||||
"@ethersproject/rlp" "^5.7.0"
|
"@ethersproject/rlp" "^5.7.0"
|
||||||
"@ethersproject/signing-key" "^5.7.0"
|
"@ethersproject/signing-key" "^5.7.0"
|
||||||
|
|
||||||
"@ethersproject/units@5.7.0":
|
"@ethersproject/units@5.7.0", "@ethersproject/units@^5.7.0":
|
||||||
version "5.7.0"
|
version "5.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1"
|
resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1"
|
||||||
integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==
|
integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==
|
||||||
|
|
@ -1626,7 +1626,7 @@
|
||||||
"@ethersproject/constants" "^5.7.0"
|
"@ethersproject/constants" "^5.7.0"
|
||||||
"@ethersproject/logger" "^5.7.0"
|
"@ethersproject/logger" "^5.7.0"
|
||||||
|
|
||||||
"@ethersproject/wallet@5.7.0":
|
"@ethersproject/wallet@5.7.0", "@ethersproject/wallet@^5.7.0":
|
||||||
version "5.7.0"
|
version "5.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d"
|
resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d"
|
||||||
integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==
|
integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==
|
||||||
|
|
@ -5373,6 +5373,26 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv
|
||||||
json-schema-traverse "^0.4.1"
|
json-schema-traverse "^0.4.1"
|
||||||
uri-js "^4.2.2"
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
|
alchemy-sdk@^2.5.0:
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/alchemy-sdk/-/alchemy-sdk-2.5.0.tgz#20813b02eded16e9cbc56c5fc8259684dc324eb9"
|
||||||
|
integrity sha512-pgwyPiAmUp4uaBkkpdnlHxjB7yjeO88VhIIFo5aCiuPAxgpB5nKRqBQw2XXqiZEUF4xU1ybZ2s1ESQ6k/hc2MQ==
|
||||||
|
dependencies:
|
||||||
|
"@ethersproject/abi" "^5.7.0"
|
||||||
|
"@ethersproject/abstract-provider" "^5.7.0"
|
||||||
|
"@ethersproject/bignumber" "^5.7.0"
|
||||||
|
"@ethersproject/bytes" "^5.7.0"
|
||||||
|
"@ethersproject/contracts" "^5.7.0"
|
||||||
|
"@ethersproject/hash" "^5.7.0"
|
||||||
|
"@ethersproject/networks" "^5.7.0"
|
||||||
|
"@ethersproject/providers" "^5.7.0"
|
||||||
|
"@ethersproject/units" "^5.7.0"
|
||||||
|
"@ethersproject/wallet" "^5.7.0"
|
||||||
|
"@ethersproject/web" "^5.7.0"
|
||||||
|
axios "^0.26.1"
|
||||||
|
sturdy-websocket "^0.2.1"
|
||||||
|
websocket "^1.0.34"
|
||||||
|
|
||||||
ansi-align@^3.0.0:
|
ansi-align@^3.0.0:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59"
|
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59"
|
||||||
|
|
@ -5756,6 +5776,13 @@ axios@^0.21.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects "^1.14.0"
|
follow-redirects "^1.14.0"
|
||||||
|
|
||||||
|
axios@^0.26.1:
|
||||||
|
version "0.26.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9"
|
||||||
|
integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.14.8"
|
||||||
|
|
||||||
babel-loader@^8.0.0, babel-loader@^8.3.0:
|
babel-loader@^8.0.0, babel-loader@^8.3.0:
|
||||||
version "8.3.0"
|
version "8.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.3.0.tgz#124936e841ba4fe8176786d6ff28add1f134d6a8"
|
resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.3.0.tgz#124936e841ba4fe8176786d6ff28add1f134d6a8"
|
||||||
|
|
@ -7086,6 +7113,14 @@ cyclist@^1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
|
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
|
||||||
integrity sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==
|
integrity sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==
|
||||||
|
|
||||||
|
d@1, d@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
|
||||||
|
integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
|
||||||
|
dependencies:
|
||||||
|
es5-ext "^0.10.50"
|
||||||
|
type "^1.0.1"
|
||||||
|
|
||||||
dashdash@^1.12.0:
|
dashdash@^1.12.0:
|
||||||
version "1.14.1"
|
version "1.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
|
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
|
||||||
|
|
@ -7716,11 +7751,29 @@ es-to-primitive@^1.2.1:
|
||||||
is-date-object "^1.0.1"
|
is-date-object "^1.0.1"
|
||||||
is-symbol "^1.0.2"
|
is-symbol "^1.0.2"
|
||||||
|
|
||||||
|
es5-ext@^0.10.35, es5-ext@^0.10.50:
|
||||||
|
version "0.10.62"
|
||||||
|
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5"
|
||||||
|
integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==
|
||||||
|
dependencies:
|
||||||
|
es6-iterator "^2.0.3"
|
||||||
|
es6-symbol "^3.1.3"
|
||||||
|
next-tick "^1.1.0"
|
||||||
|
|
||||||
es5-shim@^4.5.13:
|
es5-shim@^4.5.13:
|
||||||
version "4.6.7"
|
version "4.6.7"
|
||||||
resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.6.7.tgz#bc67ae0fc3dd520636e0a1601cc73b450ad3e955"
|
resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.6.7.tgz#bc67ae0fc3dd520636e0a1601cc73b450ad3e955"
|
||||||
integrity sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ==
|
integrity sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ==
|
||||||
|
|
||||||
|
es6-iterator@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
|
||||||
|
integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==
|
||||||
|
dependencies:
|
||||||
|
d "1"
|
||||||
|
es5-ext "^0.10.35"
|
||||||
|
es6-symbol "^3.1.1"
|
||||||
|
|
||||||
es6-promise@^4.0.3:
|
es6-promise@^4.0.3:
|
||||||
version "4.2.8"
|
version "4.2.8"
|
||||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
|
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
|
||||||
|
|
@ -7738,6 +7791,14 @@ es6-shim@^0.35.5:
|
||||||
resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.7.tgz#db00f1cbb7d4de70b50dcafa45b157e9ba28f5d2"
|
resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.7.tgz#db00f1cbb7d4de70b50dcafa45b157e9ba28f5d2"
|
||||||
integrity sha512-baZkUfTDSx7X69+NA8imbvGrsPfqH0MX7ADdIDjqwsI8lkTgLIiD2QWrUCSGsUQ0YMnSCA/4pNgSyXdnLHWf3A==
|
integrity sha512-baZkUfTDSx7X69+NA8imbvGrsPfqH0MX7ADdIDjqwsI8lkTgLIiD2QWrUCSGsUQ0YMnSCA/4pNgSyXdnLHWf3A==
|
||||||
|
|
||||||
|
es6-symbol@^3.1.1, es6-symbol@^3.1.3:
|
||||||
|
version "3.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
|
||||||
|
integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
|
||||||
|
dependencies:
|
||||||
|
d "^1.0.1"
|
||||||
|
ext "^1.1.2"
|
||||||
|
|
||||||
esbuild-android-64@0.15.18:
|
esbuild-android-64@0.15.18:
|
||||||
version "0.15.18"
|
version "0.15.18"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz#20a7ae1416c8eaade917fb2453c1259302c637a5"
|
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz#20a7ae1416c8eaade917fb2453c1259302c637a5"
|
||||||
|
|
@ -8347,6 +8408,13 @@ express@^4.17.1:
|
||||||
utils-merge "1.0.1"
|
utils-merge "1.0.1"
|
||||||
vary "~1.1.2"
|
vary "~1.1.2"
|
||||||
|
|
||||||
|
ext@^1.1.2:
|
||||||
|
version "1.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f"
|
||||||
|
integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==
|
||||||
|
dependencies:
|
||||||
|
type "^2.7.2"
|
||||||
|
|
||||||
extend-shallow@^2.0.1:
|
extend-shallow@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
|
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
|
||||||
|
|
@ -8667,7 +8735,7 @@ focus-lock@^0.8.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
follow-redirects@^1.14.0:
|
follow-redirects@^1.14.0, follow-redirects@^1.14.8:
|
||||||
version "1.15.2"
|
version "1.15.2"
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
|
||||||
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
|
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
|
||||||
|
|
@ -11288,6 +11356,11 @@ nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5"
|
resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5"
|
||||||
integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==
|
integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==
|
||||||
|
|
||||||
|
next-tick@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
|
||||||
|
integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
|
||||||
|
|
||||||
nice-try@^1.0.4:
|
nice-try@^1.0.4:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||||
|
|
@ -13951,6 +14024,11 @@ strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.
|
||||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||||
|
|
||||||
|
sturdy-websocket@^0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/sturdy-websocket/-/sturdy-websocket-0.2.1.tgz#20a58fd53372ef96eaa08f3c61c91a10b07c7c05"
|
||||||
|
integrity sha512-NnzSOEKyv4I83qbuKw9ROtJrrT6Z/Xt7I0HiP/e6H6GnpeTDvzwGIGeJ8slai+VwODSHQDooW2CAilJwT9SpRg==
|
||||||
|
|
||||||
style-loader@^1.3.0:
|
style-loader@^1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.3.0.tgz#828b4a3b3b7e7aa5847ce7bae9e874512114249e"
|
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.3.0.tgz#828b4a3b3b7e7aa5847ce7bae9e874512114249e"
|
||||||
|
|
@ -14466,6 +14544,16 @@ type-is@~1.6.18:
|
||||||
media-typer "0.3.0"
|
media-typer "0.3.0"
|
||||||
mime-types "~2.1.24"
|
mime-types "~2.1.24"
|
||||||
|
|
||||||
|
type@^1.0.1:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
|
||||||
|
integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
|
||||||
|
|
||||||
|
type@^2.7.2:
|
||||||
|
version "2.7.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0"
|
||||||
|
integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==
|
||||||
|
|
||||||
typed-array-length@^1.0.4:
|
typed-array-length@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb"
|
resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb"
|
||||||
|
|
@ -15111,6 +15199,18 @@ websocket-extensions@>=0.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
|
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
|
||||||
integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
|
integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
|
||||||
|
|
||||||
|
websocket@^1.0.34:
|
||||||
|
version "1.0.34"
|
||||||
|
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111"
|
||||||
|
integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==
|
||||||
|
dependencies:
|
||||||
|
bufferutil "^4.0.1"
|
||||||
|
debug "^2.2.0"
|
||||||
|
es5-ext "^0.10.50"
|
||||||
|
typedarray-to-buffer "^3.1.5"
|
||||||
|
utf-8-validate "^5.0.2"
|
||||||
|
yaeti "^0.0.6"
|
||||||
|
|
||||||
whatwg-url@^5.0.0:
|
whatwg-url@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||||
|
|
@ -15303,6 +15403,11 @@ y18n@^5.0.5:
|
||||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||||
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
||||||
|
|
||||||
|
yaeti@^0.0.6:
|
||||||
|
version "0.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577"
|
||||||
|
integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==
|
||||||
|
|
||||||
yallist@^3.0.2:
|
yallist@^3.0.2:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue