Skip to content
Open
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
34 changes: 24 additions & 10 deletions packages/keychain/src/components/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,37 @@ export function Home() {
return <Authenticate />;
}

// No controller, send to login
if (!controller) {
// Allow viewing certain pages without controller
const publicPaths = [
"/account/", // Allow viewing any account inventory
];
const isPublicPath = publicPaths.some((path) => pathname.startsWith(path));

// No controller, send to login (unless on public path)
if (!controller && !isPublicPath) {
return <CreateController isSlot={pathname.startsWith("/slot")} />;
}

if (!upgrade.isSynced || isConfigLoading) {
// This is likely never observable in a real application but just in case.
return <PageLoading />;
}
// Skip upgrade checks if no controller (viewing public path)
if (controller) {
if (!upgrade.isSynced || isConfigLoading) {
// This is likely never observable in a real application but just in case.
return <PageLoading />;
}

if (upgrade.available) {
return <Upgrade />;
if (upgrade.available) {
return <Upgrade />;
}
}

return (
<Layout>
{(() => {
// If no controller and on public path, just show the outlet
if (!controller && isPublicPath) {
return <Outlet />;
}

switch (context?.type) {
case "connect": {
// if no policies, we can connect immediately
Expand Down Expand Up @@ -101,13 +115,13 @@ export function Home() {
onConnect={() => {
context.resolve({
code: ResponseCodes.SUCCESS,
address: controller.address(),
address: controller!.address(),
});
}}
onSkip={() => {
context.resolve({
code: ResponseCodes.SUCCESS,
address: controller.address(),
address: controller!.address(),
});
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ import { useAccount } from "@/hooks/account";
import { useConnection, useControllerTheme } from "@/hooks/connection";
import { useNavigation } from "@/context/navigation";
import { createExecuteUrl } from "@/utils/connection/execute";
import { useViewerAddress } from "@/hooks/viewer";

const OFFSET = 10;

export function CollectionAsset() {
const { chainId, project } = useConnection();
const account = useAccount();
const { canPerformActions } = useViewerAddress();
const explorer = useExplorer();
const address = account?.address || "";
const [searchParams, setSearchParams] = useSearchParams();
Expand Down Expand Up @@ -305,66 +307,68 @@ export function CollectionAsset() {
</div>
</LayoutContent>

<LayoutFooter
className={cn(
"relative flex flex-col items-center justify-center gap-y-4 bg-background-100 pt-0 select-none",
!isListed && !isOwner && "hidden",
)}
>
<div className="flex gap-3 w-full">
<Button
variant="secondary"
isLoading={loading}
onClick={handleUnlist}
className={cn(
"w-full gap-2 text-destructive-100",
(!isListed || !isOwner) && "hidden",
)}
>
<TagIcon variant="solid" size="sm" />
Unlist
</Button>
<Link
className={cn(
"flex items-center justify-center gap-x-4 w-full",
(isListed || !isOwner) && "hidden",
)}
to={`list?${searchParams.toString()}`}
>
<Button variant="secondary" className={cn("w-full gap-2")}>
<TagIcon variant="solid" size="sm" />
List
</Button>
</Link>
<Link
className={cn(
"flex items-center justify-center gap-x-4 w-full",
(!isListed || isOwner) && "hidden",
)}
to={`purchase?${searchParams.toString()}`}
>
{canPerformActions && (
<LayoutFooter
className={cn(
"relative flex flex-col items-center justify-center gap-y-4 bg-background-100 pt-0 select-none",
!isListed && !isOwner && "hidden",
)}
>
<div className="flex gap-3 w-full">
<Button
variant="secondary"
isLoading={loading}
variant="primary"
className="w-full gap-2"
onClick={handleUnlist}
className={cn(
"w-full gap-2 text-destructive-100",
(!isListed || !isOwner) && "hidden",
)}
>
Purchase
</Button>
</Link>
<Link
className={cn(
"flex items-center justify-center gap-x-4 w-full",
!isOwner && "hidden",
)}
to={`send?${searchParams.toString()}`}
>
<Button variant="secondary" className="w-full gap-2">
<PaperPlaneIcon variant="solid" size="sm" />
Send
<TagIcon variant="solid" size="sm" />
Unlist
</Button>
</Link>
</div>
</LayoutFooter>
<Link
className={cn(
"flex items-center justify-center gap-x-4 w-full",
(isListed || !isOwner) && "hidden",
)}
to={`list?${searchParams.toString()}`}
>
<Button variant="secondary" className={cn("w-full gap-2")}>
<TagIcon variant="solid" size="sm" />
List
</Button>
</Link>
<Link
className={cn(
"flex items-center justify-center gap-x-4 w-full",
(!isListed || isOwner) && "hidden",
)}
to={`purchase?${searchParams.toString()}`}
>
<Button
isLoading={loading}
variant="primary"
className="w-full gap-2"
>
Purchase
</Button>
</Link>
<Link
className={cn(
"flex items-center justify-center gap-x-4 w-full",
!isOwner && "hidden",
)}
to={`send?${searchParams.toString()}`}
>
<Button variant="secondary" className="w-full gap-2">
<PaperPlaneIcon variant="solid" size="sm" />
Send
</Button>
</Link>
</div>
</LayoutFooter>
)}
</>
)}
</>
Expand Down
116 changes: 63 additions & 53 deletions packages/keychain/src/components/inventory/collection/collection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ import { useControllerTheme } from "@/hooks/connection";
import { useArcade } from "@/hooks/arcade";
import { EditionModel, GameModel } from "@cartridge/arcade";
import { useMarketplace } from "@/hooks/marketplace";
import { useViewerAddress } from "@/hooks/viewer";

export function Collection() {
const { games, editions } = useArcade();
const { address: contractAddress } = useParams();
const { project } = useConnection();
const { collectionOrders: orders } = useMarketplace();
const theme = useControllerTheme();
const { canPerformActions } = useViewerAddress();

const edition: EditionModel | undefined = useMemo(() => {
return Object.values(editions).find(
Expand All @@ -46,7 +48,9 @@ export function Collection() {

const location = useLocation();
const [searchParams] = useSearchParams();
const { collection, assets, status } = useCollection({ contractAddress });
const { collection, assets, status } = useCollection({
contractAddress,
});

// Use local state for selection instead of URL parameters
const [selectedTokenIds, setSelectedTokenIds] = useState<string[]>([]);
Expand Down Expand Up @@ -119,23 +123,25 @@ export function Collection() {
certified
/>

<div
className={cn(
"flex items-center gap-x-1.5 text-xs cursor-pointer self-start text-foreground-300",
)}
onClick={handleSelectAll}
>
<CheckboxIcon
className={cn(selection && "text-foreground-100")}
variant={selection ? "minus-line" : "unchecked-line"}
size="sm"
/>
<div>
{selection
? `${selectedTokenIds.length} Selected`
: "Select all"}
{canPerformActions && (
<div
className={cn(
"flex items-center gap-x-1.5 text-xs cursor-pointer self-start text-foreground-300",
)}
onClick={handleSelectAll}
>
<CheckboxIcon
className={cn(selection && "text-foreground-100")}
variant={selection ? "minus-line" : "unchecked-line"}
size="sm"
/>
<div>
{selection
? `${selectedTokenIds.length} Selected`
: "Select all"}
</div>
</div>
</div>
)}

<div className="grid grid-cols-2 gap-4 place-items-center">
{assets.map((asset) => {
Expand All @@ -150,7 +156,7 @@ export function Collection() {
state={location.state}
key={asset.tokenId}
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
if (selection) {
if (selection && canPerformActions) {
e.preventDefault();
handleSelect(asset.tokenId);
}
Expand All @@ -165,10 +171,12 @@ export function Collection() {
: `${asset.name} #${parseInt(BigInt(asset.tokenId).toString())}`
}
image={asset.imageUrl || placeholder}
selectable
selectable={canPerformActions}
selected={isSelected}
listingCount={listingCount}
onSelect={() => handleSelect(asset.tokenId)}
onSelect={() =>
canPerformActions && handleSelect(asset.tokenId)
}
className="rounded overflow-hidden"
/>
</Link>
Expand All @@ -177,40 +185,42 @@ export function Collection() {
</div>
</LayoutContent>

<LayoutFooter
className={cn(
"relative flex flex-col items-center justify-center gap-y-4 bg-background",
!selection && "hidden",
)}
>
<div className="flex gap-3 w-full">
<Link
className={cn(
"flex items-center justify-center gap-x-4 w-full",
!allUnlisted && "pointer-events-none",
)}
to={allUnlisted ? `list?${createNavigationParams()}` : ""}
>
<Button
variant="secondary"
className={cn("w-full gap-2")}
disabled={!allUnlisted}
{canPerformActions && (
<LayoutFooter
className={cn(
"relative flex flex-col items-center justify-center gap-y-4 bg-background",
!selection && "hidden",
)}
>
<div className="flex gap-3 w-full">
<Link
className={cn(
"flex items-center justify-center gap-x-4 w-full",
!allUnlisted && "pointer-events-none",
)}
to={allUnlisted ? `list?${createNavigationParams()}` : ""}
>
<TagIcon variant="solid" size="sm" />
List
</Button>
</Link>
<Link
className="flex items-center justify-center gap-x-4 w-full"
to={`send?${createNavigationParams()}`}
>
<Button variant="secondary" className="w-full gap-2">
<PaperPlaneIcon variant="solid" size="sm" />
Send
</Button>
</Link>
</div>
</LayoutFooter>
<Button
variant="secondary"
className={cn("w-full gap-2")}
disabled={!allUnlisted}
>
<TagIcon variant="solid" size="sm" />
List
</Button>
</Link>
<Link
className="flex items-center justify-center gap-x-4 w-full"
to={`send?${createNavigationParams()}`}
>
<Button variant="secondary" className="w-full gap-2">
<PaperPlaneIcon variant="solid" size="sm" />
Send
</Button>
</Link>
</div>
</LayoutFooter>
)}
</>
)}
</>
Expand Down
Loading
Loading