Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
"@emotion/react": "^11.7.1",
"@emotion/server": "^11.4.0",
"@emotion/styled": "^11.6.0",
"@glazed/types": "^0.2.0",
"@mui/icons-material": "^5.3.1",
"@mui/lab": "^5.0.0-alpha.67",
"@mui/material": "^5.4.0",
"@mui/x-data-grid": "^5.4.0",
"@reduxjs/toolkit": "^1.7.1",
"@self.id/framework": "^0.3.2",
"@sentry/nextjs": "^6.17.4",
"@superfluid-finance/sdk-core": "0.4.2",
"@superfluid-finance/sdk-redux": "0.3.0",
Expand All @@ -28,6 +30,7 @@
"graphiql-explorer": "^0.9.0",
"graphql": "^16.3.0",
"graphql-ws": "^5.8.2",
"key-did-provider-ed25519": "^2.0.0",
"lodash": "^4.17.21",
"next": "^12.1.7-canary.6",
"next-redux-cookie-wrapper": "^2.1.2",
Expand All @@ -39,7 +42,7 @@
"redux": "^4.1.2",
"redux-persist": "^6.0.0"
},
"devDependencies": {
"devDependencies": {
"@types/lodash": "^4.14.178",
"@types/node": "^17.0.14",
"@types/react": "^18.0.0",
Expand Down
Binary file added src/.DS_Store
Binary file not shown.
43 changes: 28 additions & 15 deletions src/components/AddressBook.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
Box,
Button,
Card,
Dialog,
DialogActions,
DialogContent,
Expand All @@ -11,19 +12,22 @@ import {
SvgIconProps,
TextField,
Tooltip,
Typography
} from "@mui/material";
import { FC, useEffect, useState } from "react";
import StarIcon from "@mui/icons-material/Star";
import StarBorderIcon from "@mui/icons-material/StarBorder";
import { useAppDispatch, useAppSelector } from "../redux/hooks";
import { useViewerConnection, useViewerRecord } from '@self.id/framework'
import { useAppSelector } from "../redux/hooks";
import {
addressBookSelectors,
addressBookSlice,
createEntryId,
getEntryId,
} from "../redux/slices/addressBook.slice";
import { Network } from "../redux/networks";
import { ethers } from "ethers";
import useAddressBook from "../hooks/useAddressBook";


export const AddressBookButton: FC<{
network: Network;
Expand Down Expand Up @@ -64,14 +68,17 @@ export const AddressBookDialog: FC<{
open: boolean;
handleClose: () => void;
}> = ({ network, address, open, handleClose }) => {
const dispatch = useAppDispatch();
const existingEntry = useAppSelector((state) =>
addressBookSelectors.selectById(state, createEntryId(network, address))
);

const getInitialNameTag = () => existingEntry?.nameTag ?? "";
const [nameTag, setNameTag] = useState<string>(getInitialNameTag());

const [connection, connect, disconnect] = useViewerConnection()
const { addAddressBookEntry, removeAddressBookEntry } = useAddressBook();
const record = useViewerRecord('myAddressBook');

// Fixes: https://github.com/superfluid-finance/superfluid-console/issues/21
useEffect(() => {
setNameTag(getInitialNameTag());
Expand All @@ -80,32 +87,36 @@ export const AddressBookDialog: FC<{

const handleRemove = () => {
if (existingEntry) {
dispatch(
addressBookSlice.actions.entryRemoved(getEntryId(existingEntry))
);
removeAddressBookEntry(existingEntry, getEntryId(existingEntry))
}
handleClose();
};

const handleSave = () => {
const handleSave = async () => {
const nameTagTrimmed = nameTag.trim();
// Only save non-empty names
if (nameTagTrimmed) {
dispatch(
addressBookSlice.actions.entryUpserted({
chainId: network.chainId,
address: ethers.utils.getAddress(address),
nameTag: nameTagTrimmed,
})
);
const entry = {
chainId: network.chainId,
address: ethers.utils.getAddress(address),
nameTag: nameTagTrimmed,
}
addAddressBookEntry( entry )
}
handleClose();
};

return (
<Dialog fullWidth maxWidth="xs" open={open} onClose={handleClose}>
<Box sx={{ pb: 1 }}>
<Box sx={{ display: "flex", justifyContent: "center" }}>
{connection.status !== 'connected' ?
(
<Typography sx={{ p: 1, textAlign: 'center' }} variant="subtitle2" component="h3">
Please Connect(top right) DID to Access Your Ceramic Address Book.
</Typography>)

:(<div>
<Box sx={{ display: "flex", justifyContent: "center" }}>
<DialogTitle>
{existingEntry ? "Edit entry" : "Add entry"}
</DialogTitle>
Expand Down Expand Up @@ -137,6 +148,8 @@ export const AddressBookDialog: FC<{
Save
</Button>
</DialogActions>
</div>)
}
</Box>
</Dialog>
);
Expand Down
58 changes: 58 additions & 0 deletions src/components/CeramicConnect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useAppDispatch } from "../redux/hooks";
import useAddressBook from "../hooks/useAddressBook";
import { EthereumAuthProvider, useViewerConnection } from '@self.id/framework'
import { addressBookSlice } from "../redux/slices/addressBook.slice";
import { Button, styled, Tooltip } from "@mui/material";



export function CeramicConnectButton() {

const [connection, connect, disconnect] = useViewerConnection()
const dispatch = useAppDispatch();
const { populateAddressBook } = useAddressBook();

const connectToCeramic = async () => {
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts',
})
await connect(new EthereumAuthProvider(window.ethereum, accounts[0]))
populateAddressBook()
}

const disconnectFromCeramic = async () => {
console.log('disconnect')
disconnect();
dispatch(addressBookSlice.actions.entryRemoveAll());

}

return connection.status === 'connected' ? (
<Button
sx={{
fontWeight: 500,
fontSize: "16px",
color: "white",
textTransform: "none",
}}
variant="contained"
onClick={disconnectFromCeramic}>
{connection.selfID.id.substr(6,7)}...
</Button>
) : typeof window !== undefined ? (
<Button
variant="contained"
disabled={connection.status === 'connecting'}
onClick={connectToCeramic}>
Connect
</Button>
) : (
<p>
An injected Ethereum provider such as{' '}
<a href="https://metamask.io/">MetaMask</a> is needed to authenticate.
</p>
)
}



11 changes: 9 additions & 2 deletions src/components/SearchDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ import { useAppSelector } from "../redux/hooks";
import { addressBookSelectors } from "../redux/slices/addressBook.slice";
import { searchBarPlaceholderText } from "./SearchBar";
import { useSearch } from "../hooks/useSearch";
import { useViewerConnection } from "@self.id/framework";

//Change search dialog
const SearchDialog: FC<{ open: boolean; close: () => void }> = ({
open,
close,
Expand Down Expand Up @@ -62,7 +64,8 @@ const SearchDialog: FC<{ open: boolean; close: () => void }> = ({
};

const networkSearchResults = useSearch(searchTermDebounced);

console.log('network: ' + JSON.stringify(networkSearchResults))
const [ connection ] = useViewerConnection();
useEffect(() => {
const handleRouteChange = () => {
handleClose();
Expand Down Expand Up @@ -115,7 +118,11 @@ const SearchDialog: FC<{ open: boolean; close: () => void }> = ({
}}
onChange={(e) => setSearchTerm(e.currentTarget.value)}
/>

{ connection.status !== 'connected' &&
(<Typography sx={{ p: 1, textAlign: 'center' }} variant="subtitle2" component="h3">
Please Connect(top right) DID to Access Your Ceramic Address Book.
</Typography>)
}
{networkSearchResults
.filter((x) => x.tokens.length || x.accounts.length)
.map((x) => (
Expand Down
2 changes: 2 additions & 0 deletions src/components/SfAppBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { FC, useState } from "react";
import SubgraphIcon from "./SubgraphIcon";
import SettingsIcon from "@mui/icons-material/SettingsOutlined";
import SettingsDrawer from "./SettingsDrawer";
import { CeramicConnectButton } from "./CeramicConnect";

export const SfAppBar = () => {
const [searchOpen, setSearchOpen] = useState(false);
Expand Down Expand Up @@ -112,6 +113,7 @@ export const SfAppBar = () => {
Subgraph
</Typography>
</AppLink>
<CeramicConnectButton/>
</Stack>
<Tooltip title="Settings" enterDelay={100}>
<IconButton
Expand Down
128 changes: 128 additions & 0 deletions src/hooks/useAddressBook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { useCallback } from "react";
import { useAppDispatch, useAppSelector } from "../redux/hooks";
import { useViewerConnection } from '@self.id/framework'
import { useViewerRecord } from "@self.id/react";
import {
AddressBookEntry,
addressBookSelectors,
addressBookSlice,
} from "../redux/slices/addressBook.slice";


type Contact = {
name: string;
wallets: Wallet[];
};

type Wallet = {
network: string;
walletAddress: string;
};

type AddressBook = {
total_cnt: number,
contacts: Contact[]
}



const useAddressBook = () => {

const emptyAddressBook: AddressBook = {"total_cnt": 0,"contacts": []}
const dispatch = useAppDispatch();
const [connection] = useViewerConnection();
const addressBook = useViewerRecord('myAddressBook');
let localAddressBook = useAppSelector((state) =>
addressBookSelectors.selectAll(state)
);


const addAddressBookEntry = (entry: Contact) => {
let contacts: Contact[] = transformToCeramicData(localAddressBook.concat(entry));
dispatch(addressBookSlice.actions.entryUpserted(entry))
updateAddressBook(addressBook, contacts);
}


const removeAddressBookEntry = async ( entry: Contact,id) => {
let contacts = transformToCeramicData(localAddressBook.filter(
(c) => c.chainId !== entry.chainId && c.address !== entry.address
))
await updateAddressBook(addressBook, contacts);
dispatch(addressBookSlice.actions.entryRemoved(id));
}


const populateAddressBook = useCallback(async () => {
if('content' in addressBook){
await initializeAddressBook(addressBook);
const contacts = transformFromCeramicData(addressBook.content.contacts)
dispatch(addressBookSlice.actions.entryUpsertMany(contacts));
}
}, [addressBook])


async function initializeAddressBook(addressBook: AddressBook){
if(addressBook.content == null){
await addressBook.set(emptyAddressBook)
}
}


async function updateAddressBook(addressBook: AddressBook, contacts: Contact[]){
await addressBook
.set({ total_cnt: contacts.length, contacts })
.then(() => console.log("Update Address Book"));

populateAddressBook();

}

return {
addAddressBookEntry,
removeAddressBookEntry,
populateAddressBook
}
}


function transformToCeramicData(entries: AddressBookEntry[]) {
return Object.values(
entries.reduce((contacts: any, c) => {
const name = c.nameTag;
const network = c.chainId.toString();
const walletAddress = c.address;

// Each stored name can have multiple wallets - one for each network + address
const id = `${network}_${walletAddress}`;

contacts[name] = contacts[name] || {};
contacts[name].name = name;
contacts[name].wallets = contacts[name].wallets || {};
contacts[name].wallets[id] = { network, walletAddress };

return contacts;
}, {}) as { name: string; wallets: [] }[]
).map(({ name, wallets }) => ({ name, wallets: Object.values(wallets) }));
}

function transformFromCeramicData(contacts: Contact[]) {
const contactsMap: { [key: string]: any } = {};
contacts.forEach((contact: Contact) => {
contact.wallets.forEach((wallet) => {
const network = wallet.network;
const address = wallet.walletAddress;
const id = `${network}_${address}`;

contactsMap[id] = {
chainId: Number(network),
address,
nameTag: contact.name,
};
});
});

return Object.values(contactsMap);
}

export default useAddressBook;
2 changes: 1 addition & 1 deletion src/hooks/useSearchAddressBook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const useSearchAddressBook = (searchTerm: string) => {
() => ethers.utils.isAddress(searchTerm),
[searchTerm]
);

//add ceramic Address search
return networks.map((network) => {
const addressBookEntries = useAppSelector((state) =>
searchTerm !== "" && !isSearchTermAddress
Expand Down
Loading