Skip to content

Commit

Permalink
Merge pull request #35 from coinbase/alissa.crane/assets
Browse files Browse the repository at this point in the history
chore: style updates
  • Loading branch information
abcrane123 authored Nov 8, 2024
2 parents 20f43de + fd78a39 commit 281a493
Show file tree
Hide file tree
Showing 17 changed files with 187 additions and 180 deletions.
26 changes: 9 additions & 17 deletions app/components/Agent.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useCallback, useState } from 'react';
import type { Language } from '../types';
import { useState } from 'react';
import AgentAssets from './AgentAssets';
import AgentBalance from './AgentBalance';
import AgentProfile from './AgentProfile';
Expand All @@ -11,11 +10,6 @@ import Stream from './Stream';
export default function Agent() {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [isMobileChatOpen, setIsMobileChatOpen] = useState(false);
const [currentLanguage, setCurrentLanguage] = useState<Language>('en');

const handleLanguageChange = useCallback((lang: Language) => {
setCurrentLanguage(lang);
}, []);

return (
<div className="relative flex h-screen flex-col overflow-hidden bg-black font-mono text-[#5788FA]">
Expand All @@ -24,8 +18,6 @@ export default function Agent() {
setIsMobileMenuOpen={setIsMobileMenuOpen}
isMobileChatOpen={isMobileChatOpen}
setIsMobileChatOpen={setIsMobileChatOpen}
setCurrentLanguage={handleLanguageChange}
currentLanguage={currentLanguage}
/>

<div className="relative flex flex-grow overflow-hidden">
Expand All @@ -35,14 +27,15 @@ export default function Agent() {
isMobileMenuOpen ? 'translate-x-0' : '-translate-x-full'
} fixed z-20 flex h-full w-full flex-col overflow-y-auto bg-black transition-transform duration-300 lg:relative lg:z-0 lg:w-1/3 lg:translate-x-0 lg:border-[#5788FA]/50 lg:border-r `}
>
<AgentProfile currentLanguage={currentLanguage} />
<AgentProfile />
<AgentBalance />
<AgentAssets />
<Footer />
</div>

<div className="flex w-full lg:w-2/3">
<Stream currentLanguage={currentLanguage} />
<Chat currentLanguage={currentLanguage} className="hidden" />
<Chat />
<Stream className="hidden" />
</div>

<div
Expand All @@ -51,13 +44,12 @@ export default function Agent() {
isMobileChatOpen ? 'translate-y-0' : 'translate-x-full'
} fixed top-0 z-8 flex h-full w-full flex-col overflow-y-auto bg-black pt-[100px] transition-transform duration-300 md:hidden`}
>
<Chat
currentLanguage={currentLanguage}
className="flex w-full flex-col"
/>
<Stream className="flex w-full flex-col" />
</div>
</div>
<Footer />
<div className="z-20 lg:hidden">
<Footer />
</div>
</div>
);
}
65 changes: 50 additions & 15 deletions app/components/AgentAssets.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { NFTMintCard } from '@coinbase/onchainkit/nft';
import { NFTCollectionTitle } from '@coinbase/onchainkit/nft/mint';
import { type Token, TokenRow } from '@coinbase/onchainkit/token';
import { useCallback, useEffect, useState } from 'react';
// import useGetNFTs from "../hooks/useGetNFTs";
import type { Address } from 'viem';
import { useToken } from 'wagmi';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { type Address, erc721Abi, } from 'viem';
import { useContractRead, useToken } from 'wagmi';
import useGetNFTs from '../hooks/useGetNFTs';
import useGetTokens from '../hooks/useGetTokens';

type AgentTokenProps = {
type AgentAssetProps = {
tokenAddress: Address;
};

function AgentToken({ tokenAddress }: AgentTokenProps) {
function AgentToken({ tokenAddress }: AgentAssetProps) {
const { data } = useToken({ address: tokenAddress, chainId: 84532 });
const token: Token = {
address: tokenAddress,
Expand All @@ -20,38 +22,63 @@ function AgentToken({ tokenAddress }: AgentTokenProps) {
image: '',
};

return <TokenRow token={token} className="max-w-56 rounded" />;
return <TokenRow token={token} className="max-w-56 rounded font-mono" />;
}

function AgentNFT({ tokenAddress }: AgentAssetProps) {
const { data: name } = useContractRead({
address: tokenAddress,
abi: erc721Abi,
functionName: 'name',
chainId: 84532,
});

const nftData = useMemo(() => {
return {
name,
};
}, [name]);

if (!name) {
return null;
}

return (
<NFTMintCard contractAddress={tokenAddress} useNFTData={() => nftData}>
<NFTCollectionTitle className="font-mono text-sm" />
</NFTMintCard>
);
}
export default function AgentAssets() {
const [tab, setTab] = useState('tokens');
// const [nfts, setNFTs] = useState<string[]>([]);
const [nfts, setNFTs] = useState<Address[]>([]);
const [tokens, setTokens] = useState<Address[]>([]);

const { getTokens } = useGetTokens({ onSuccess: setTokens });
// const { getNFTs } = useGetNFTs({ onSuccess: setNFTs });
const { getNFTs } = useGetNFTs({ onSuccess: setNFTs });

const handleTabChange = useCallback((tab: string) => {
return () => setTab(tab);
}, []);

useEffect(() => {
// getNFTs();
getNFTs();
getTokens();
}, [getTokens]);
}, [getNFTs, getTokens]);

return (
<div className="mr-2 mb-4 rounded-sm bg-black p-4">
<div className="flex flex-col items-start gap-4">
<div className="flex w-full grow gap-6 border-zinc-700 border-b">
{/* <button
<button
type="button"
onClick={handleTabChange("nfts")}
onClick={handleTabChange('nfts')}
className={`flex items-center justify-center py-1 ${
tab === "nfts" ? "border-b border-[#5788FA]" : ""
tab === 'nfts' ? 'border-[#5788FA] border-b' : ''
}`}
>
NFTs
</button> */}
</button>
<button
type="button"
onClick={handleTabChange('tokens')}
Expand All @@ -68,6 +95,14 @@ export default function AgentAssets() {
tokens?.map((token) => (
<AgentToken key={token} tokenAddress={token} />
))}

{tab === 'nfts' && nfts && (
<div className="grid-col-1 grid gap-4 sm:grid-cols-2">
{nfts?.map((nft) => (
<AgentNFT key={nft} tokenAddress={nft} />
))}
</div>
)}
</div>
</div>
);
Expand Down
21 changes: 4 additions & 17 deletions app/components/AgentProfile.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { AGENT_NAME, AGENT_WALLET_ADDRESS, notoSansThai } from '../constants';
import type { Language } from '../types';
import { AGENT_NAME, AGENT_WALLET_ADDRESS } from '../constants';

type AgentProfileProps = {
currentLanguage: Language;
};

export default function AgentProfile({ currentLanguage }: AgentProfileProps) {
export default function AgentProfile() {
const [eyePosition, setEyePosition] = useState({ x: 50, y: 50 });
const [showToast, setShowToast] = useState(false);
const avatarRef = useRef<SVGSVGElement>(null);
Expand Down Expand Up @@ -98,16 +93,8 @@ export default function AgentProfile({ currentLanguage }: AgentProfileProps) {
</div>
</div>

{/* TODO: update description */}
<p
className={`text-[#5788FA] text-base ${
currentLanguage === 'th' ? notoSansThai.className : ''
}`}
>
Lorem Ipsum is simply dummy text of the printing and typesetting
industry. Lorem Ipsum has been the industrys standard dummy text ever
since the 1500s, when an unknown printer took a galley of type and
scrambled it to make a type specimen book.
<p className="text-[#5788FA] text-base">
{`I'm your onchain concierge`}
</p>
</div>
</div>
Expand Down
23 changes: 9 additions & 14 deletions app/components/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import { cn } from '@coinbase/onchainkit/theme';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { notoSansThai } from '../constants';
import useChat from '../hooks/useChat';
import type { AgentMessage, Language, StreamEntry } from '../types';
import type { AgentMessage, StreamEntry } from '../types';
import { generateUUID, markdownToPlainText } from '../utils';
import ChatInput from './ChatInput';
import StreamItem from './StreamItem';

type ChatProps = {
currentLanguage: Language;
enableLiveStream?: boolean;
className?: string;
};

export default function Chat({ className, currentLanguage }: ChatProps) {
export default function Chat({ className }: ChatProps) {
const [userInput, setUserInput] = useState('');
const [streamEntries, setStreamEntries] = useState<StreamEntry[]>([]);
const conversationId = useMemo(() => {
Expand Down Expand Up @@ -76,21 +74,19 @@ export default function Chat({ className, currentLanguage }: ChatProps) {
}, [streamEntries]);

return (
<div className={cn('flex h-full w-1/2 grow flex-col md:flex', className)}>
<div
className={cn(
'flex h-full w-full flex-col border-[#5788FA]/50 md:flex md:w-1/2 md:border-r',
className,
)}
>
<div className="flex grow flex-col overflow-y-auto p-4 pb-20">
<p
className={`text-zinc-500 ${
currentLanguage === 'th' ? notoSansThai.className : ''
}`}
>
Ask me something...
</p>
<p className="text-zinc-500">Ask me something...</p>
<div className="mt-4 space-y-2" role="log" aria-live="polite">
{streamEntries.map((entry, index) => (
<StreamItem
key={`${entry.timestamp.toDateString()}-${index}`}
entry={entry}
currentLanguage={currentLanguage}
/>
))}
</div>
Expand All @@ -99,7 +95,6 @@ export default function Chat({ className, currentLanguage }: ChatProps) {
</div>

<ChatInput
currentLanguage={currentLanguage}
userInput={userInput}
handleKeyPress={handleKeyPress}
handleSubmit={handleSubmit}
Expand Down
32 changes: 8 additions & 24 deletions app/components/ChatInput.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,17 @@
import { type ChangeEvent, useCallback } from 'react';
import { notoSansThai } from '../constants';
import SendSvg from '../svg/SendSvg';
import { translations } from '../translations';
import type { Language } from '../types';

type PremadeChatInputProps = {
text: string;
currentLanguage: Language;
setUserInput: (input: string) => void;
};

function PremadeChatInput({
text,
currentLanguage,
setUserInput,
}: PremadeChatInputProps) {
function PremadeChatInput({ text, setUserInput }: PremadeChatInputProps) {
return (
<button
type="submit"
onClick={() => setUserInput(text)}
className={`w-full whitespace-nowrap rounded-sm border border-[#5788FA]/50 px-2 py-1 text-start text-[#5788FA] transition-colors hover:bg-zinc-900 hover:text-[#3D7BFF] lg:w-auto ${
currentLanguage === 'th' ? notoSansThai.className : ''
}`}
className="w-full whitespace-nowrap rounded-sm border border-[#5788FA]/50 px-2 py-1 text-start text-[#5788FA] transition-colors hover:bg-zinc-900 hover:text-[#3D7BFF] lg:w-auto"
>
{text}
</button>
Expand All @@ -33,12 +23,10 @@ export type ChatInputProps = {
userInput: string;
setUserInput: (input: string) => void;
handleKeyPress: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
currentLanguage: Language;
disabled?: boolean;
};

export default function ChatInput({
currentLanguage,
handleSubmit,
userInput,
setUserInput,
Expand All @@ -56,18 +44,16 @@ export default function ChatInput({
return (
<form
onSubmit={handleSubmit}
className="mt-auto flex w-full flex-col border-[#5788FA]/50 border-t bg-black p-4 pb-10 md:mt-0 lg:pb-2"
className="mt-auto flex w-full flex-col border-[#5788FA]/50 border-t bg-black p-4 pb-2 md:mt-0"
>
<div className="flex flex-col gap-2">
<div className="flex gap-2">
<textarea
value={userInput}
onChange={handleInputChange}
onKeyPress={handleKeyPress}
className={`h-24 w-full bg-black p-2 pr-10 text-gray-300 placeholder-[#5788FA] placeholder-opacity-50 lg:h-36 ${
currentLanguage === 'th' ? notoSansThai.className : ''
}`}
placeholder={translations[currentLanguage].chat.placeholder}
className="h-24 w-full bg-black p-2 pr-10 text-gray-300 placeholder-[#5788FA] placeholder-opacity-50 lg:h-36"
placeholder="How can I help?"
rows={1}
/>
<button
Expand All @@ -86,20 +72,18 @@ export default function ChatInput({
<div className="flex grow flex-col flex-wrap gap-2 overflow-x-auto text-xs lg:flex-row lg:text-sm">
<PremadeChatInput
setUserInput={setUserInput}
currentLanguage={currentLanguage}
text="Create a new token with all the specifications"
text="What actions can you take?"
/>
<PremadeChatInput
setUserInput={setUserInput}
currentLanguage={currentLanguage}
text={translations[currentLanguage].chat.suggestions.swap}
text="Deploy an NFT"
/>
</div>
<button
type="submit"
disabled={!/[a-zA-Z]/.test(userInput) || disabled}
className={`rounded-sm p-1.5 transition-colors max-xl:hidden ${
/[a-zA-Z]/.test(userInput) || disabled
/[a-zA-Z]/.test(userInput) && !disabled
? 'bg-[#5788FA] text-zinc-950 hover:bg-[#3D7BFF]'
: 'cursor-not-allowed bg-[#5788FA] text-zinc-950 opacity-50'
}`}
Expand Down
3 changes: 1 addition & 2 deletions app/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default function Footer() {
return (
<div className="fixed bottom-4 left-4 z-30 text-sm text-zinc-400">
<div className="z-30 mt-auto flex w-full bg-black p-4 text-sm text-xs text-zinc-400 sm:text-sm md:border-[#5788FA]/50 md:border-t">
Powered by{' '}
<a
href="https://onchainkit.xyz/"
Expand All @@ -11,7 +11,6 @@ export default function Footer() {
OnchainKit
</a>
<span className="mx-2">·</span>
{/* TODO: Replace with a link to the template repo */}
<a
href="https://github.com/coinbase/onchain-agent-demo"
target="_blank"
Expand Down
Loading

0 comments on commit 281a493

Please sign in to comment.