Skip to content
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

feat: new OnchainName component #49

Merged
merged 19 commits into from
Feb 1, 2024
Merged

Conversation

alvaroraminelli
Copy link
Contributor

@alvaroraminelli alvaroraminelli commented Jan 31, 2024

What changed? Why?

Adding the first OnchainName component of our Identity Kit 👤

The OnchainName component primarily focuses on showcasing ENS names for given Ethereum addresses, and defaults to displaying a sliced version of the address when an ENS name isn't available.

import { OnchainName } from '@coinbase/onchainkit';

<OnchainName address="0x1234567890abcdef1234567890abcdef12345678" sliced={false} />;

Notes to reviewers

How has it been tested?

  • Unit tests only.

README.md Outdated
@@ -213,6 +213,29 @@ type FrameMetadataResponse = {
<br />
<br />

## OnchainName 📛
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new section will be called Identity Kit, which will have a callection of components and utilities around Onchain Identity.

@alvaroraminelli alvaroraminelli marked this pull request as ready for review February 1, 2024 00:29
README.md Outdated Show resolved Hide resolved
<OnchainName address="0x1234567890abcdef1234567890abcdef12345678" sliced={false} />;
```

**@Param**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I think we can use types going forward to better describe what's going on. Check the latest README.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

soon we might have autogen docs that will be based on type correct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe... There is a difference between type information, and how to teach how to use something.

Vitepress will automate docs creation in some capacity, but the ART of teaching how to use a function is still a manual work.

README.md Show resolved Hide resolved
import type { Address } from 'viem';

type OnchainNameProps = {
address?: Address;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the address should be mandatory

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If OnchainName accepts undefined address

import { useAccount } from 'wagmi';
import { OnchainName } from '@/components/OnchainName';

function App() {
  // may be undefined
  const { address } = useAccount();

  // Will return empty span if address is undefined
  return <OnchainName address={address} />
}

If OnchainName rejects undefined address

import { useAccount } from 'wagmi';
import { OnchainName } from '@/components/OnchainName';

function App() {
  // may be undefined
  const { address } = useAccount();

  if (!address) {
    return null;
  }

  // Will cause type error if we do not check address first
  return <OnchainName address={address} />
}

Looking at likely example use case, it feels less cumbersome to allow address to be undefined. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed with @cnasc. vote to accept to make less cumbersome for consumers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You just made my point, you allow user to shot themself if the address is not there. And than they end up in a broken state.

vote to accept to make less cumbersome for consumers.

The consumer will see an empty name, that's not a good experience.

@cnasc @alvaroraminelli what am I missing? If you want an OnchainName, you need a an address. Simple. Why are we allowing folks to say, I have not an address today. It's a bit saying, I go to a coffee shop I ask for coffee, I don't have money, and still expect an empty cup of coffee because I have no money. Well if you don't have money I want someone to tell me, sorry no coffee until you have money.

Right?

export function OnchainName({ address, className, sliced = true, props }: OnchainNameProps) {
const { ensName } = useEnsName(address);

const normilizedAddress = useMemo(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment here why we have useMemo

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also should we avoid have CPU execution for the normilizedAddress if in case of ensName we will use that anyway?

Copy link
Contributor

@cnasc cnasc Feb 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const normilizedAddress = useMemo(() => {
const normalizedAddress = useMemo(() => {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I think the idea here is that the input address will display until ens name resolves. I don't think that's unreasonable, but maybe we'd prefer an explicit loading state?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great call. Yes, we need loading state here. Wondering how the UX would look like when you have a list of users in the UX, will things shift and change like crazy. User might opt-in to display loading state.

Need some UX thinking here.

import { createPublicClient, http } from 'viem';
import { mainnet } from 'viem/chains';

export const publicClient: ReturnType<typeof createPublicClient> = createPublicClient({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think client should go in src/network or more a location where we handle Data Layer stuff. cc @robpolak

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM


// InMemoryStorageService: A service to store data in JavaScript memory.
// It's a simple key-value storage using a Map. The data is stored as long as the page is not reloaded.
export class InMemoryStorageService implements StorageInterface {
Copy link
Contributor

@Zizzamia Zizzamia Feb 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's convert this to functional at some point. Let's keep OnchainKit class free.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we see this as a frontend-only use case? If so, it might be best to expose this as a context

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this evolving towards sharing in context as well. We might want build a light version of react-query for actions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this evolving towards sharing in context as well.

React Context are a slippery slop, where folks think everything should be a context. Library should have like 90% of their state being just variable shared between functions. And for very very small exential stuff you can have Context that optimizes the Hooks story.

So before we build any React Context, it's important to ask the question, on where is the benefit for the end application. If it's just a nice to have for us as developer, than we don't need that. But again, for Hooks Rendering optimization, that we can talk about it.

@Zizzamia Zizzamia requested review from robpolak and cnasc February 1, 2024 02:39
@Zizzamia
Copy link
Contributor

Zizzamia commented Feb 1, 2024

Before we merge, let's have also a quick review from @cnasc, @robpolak, @wespickett and @Sneh1999 on this epic first Identity component.

it('displays sliced address when ENS name is not available and sliced is true as default', () => {
(useEnsName as jest.Mock).mockReturnValue({ ensName: null });
(getSlicedAddress as jest.Mock).mockImplementation(
(addr) => addr.slice(0, 6) + '...' + addr.slice(-4),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be mocked? I notice that the implementation here is slightly different from the one in the real function. Is that significant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tried the approach below but it is not working. fallback to mocking the entire function.

import * as addressUtils from '../core/address';

beforeEach(() => {
    jest.clearAllMocks();
    jest.spyOn(addressUtils, 'getSlicedAddress');
  });

expect(addressUtils.getSlicedAddress).toHaveBeenCalledTimes(0);

@wespickett
Copy link
Contributor

lgtm. We ended up switching to extend the next/jest config in build-onchain-apps, but if tests run successfully as is, then it should be fine with the current config https://github.com/coinbase/build-onchain-apps/blob/main/template/jest.config.js#L1

}, [address, isLoading]);

if (isLoading) {
return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for later, this will cause a Layout Shift. Something to consider in follow up PR.

Copy link
Contributor

@Zizzamia Zizzamia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BRILLIANT

@Zizzamia Zizzamia merged commit 8af8505 into main Feb 1, 2024
5 checks passed
@Zizzamia Zizzamia deleted the alvaroraminelli/feat-onchain-name branch February 1, 2024 23:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants