diff --git a/src/features/main/components/Main.tsx b/src/features/main/components/Main.tsx
index f135125..1745017 100644
--- a/src/features/main/components/Main.tsx
+++ b/src/features/main/components/Main.tsx
@@ -3,7 +3,7 @@ import { createAoContractClientForProcess } from "@/features/ao/lib/aoContractCl
import { AoWallet } from "@/features/ao/lib/aoWallet";
import { truncateAddress } from "@/features/arweave/lib/utils";
import { createChatClientForProcess } from "@/features/chat/contract/chatClient";
-import { ProfileInfo } from "@/features/profile/contract/model";
+import { ProfileAssets, ProfileInfo } from "@/features/profile/contract/model";
import { createProfileRegistryClientForProcess } from "@/features/profile/contract/profileRegistryClient";
import { Renderer } from "@/features/render/components/Renderer";
import { createRealityClientForProcess } from "@/features/reality/contract/realityClient";
@@ -11,6 +11,7 @@ import { mainMachine } from "../machines/mainMachine";
import { useMachine } from "@xstate/react";
import ProfileButton from "@/features/profile/components/ProfileButton";
import { createTrackingClientForProcess } from "@/features/tracking/contract/trackingClient";
+import { createProfileClientForProcess } from "@/features/profile/contract/profileClient";
const profileRegistryProcessId = import.meta.env
.VITE_PROFILE_PROCESS_ID as string;
@@ -29,12 +30,23 @@ export default function Main({ wallet, disconnect, worldId }: MainProps) {
import.meta.env.VITE_TRACKING_TEST_PROCESS_ID,
);
+ const aoContractClientForProcess = createAoContractClientForProcess(wallet);
+
+ const profileClientForProcess = createProfileClientForProcess(wallet);
+
+ const realityClientForProcess = createRealityClientForProcess(wallet);
+
+ const realityClientBaseWorldId = realityClientForProcess(worldId || "");
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [current, send] = useMachine(mainMachine, {
input: {
initialContext: {
address: wallet.address,
profileRegistryClient,
+ profileClientForProcess,
+ aoContractClientForProcess,
+ realityClientBaseWorldId
},
},
});
@@ -42,13 +54,14 @@ export default function Main({ wallet, disconnect, worldId }: MainProps) {
const renderer = (profile?: {
profileId: string;
profileInfo: ProfileInfo;
+ assets: ProfileAssets;
}) => (
-
+
@@ -79,6 +92,7 @@ export default function Main({ wallet, disconnect, worldId }: MainProps) {
renderer({
profileId: current.context.profileId!,
profileInfo: current.context.profileInfo!,
+ assets: current.context.assets!,
})
) : (
renderer()
diff --git a/src/features/main/machines/mainMachine.ts b/src/features/main/machines/mainMachine.ts
index d9f81e3..38f9f57 100644
--- a/src/features/main/machines/mainMachine.ts
+++ b/src/features/main/machines/mainMachine.ts
@@ -1,10 +1,16 @@
-import { ProfileInfo } from "@/features/profile/contract/model";
+import { AoContractClientForProcess, ReadArgs } from "@/features/ao/lib/aoContractClient";
+import { Asset, DetailedAsset, ProfileAssets, ProfileInfo } from "@/features/profile/contract/model";
+import { ProfileClientForProcess } from "@/features/profile/contract/profileClient";
import { ProfileRegistryClient } from "@/features/profile/contract/profileRegistryClient";
+import { RealityClient } from "@/features/reality/contract/realityClient";
import { fromPromise, setup, assign } from "xstate";
type InitialContext = {
address: string;
profileRegistryClient: ProfileRegistryClient;
+ profileClientForProcess: ProfileClientForProcess;
+ aoContractClientForProcess: AoContractClientForProcess;
+ realityClientBaseWorldId: RealityClient;
};
export const mainMachine = setup({
@@ -15,6 +21,7 @@ export const mainMachine = setup({
context: {} as InitialContext & {
profileId?: string;
profileInfo?: ProfileInfo;
+ assets?: ProfileAssets;
},
},
guards: {
@@ -23,8 +30,9 @@ export const mainMachine = setup({
params: {
profileId?: string;
profileInfo?: ProfileInfo;
+ assets?: ProfileAssets;
},
- ) => params.profileId !== undefined && params.profileInfo !== undefined,
+ ) => params.profileId !== undefined && params.profileInfo !== undefined && params.assets !== undefined,
},
actions: {
assignProfileIdAndInfo: assign(
@@ -33,10 +41,12 @@ export const mainMachine = setup({
params: {
profileId: string;
profileInfo: ProfileInfo;
+ assets: ProfileAssets;
},
) => ({
profileId: params.profileId,
profileInfo: params.profileInfo,
+ assets: params.assets
}),
),
},
@@ -48,13 +58,17 @@ export const mainMachine = setup({
input: {
address: string;
profileRegistryClient: ProfileRegistryClient;
+ bazarProfileClient: ProfileClientForProcess;
+ aoContractClientForProcess: AoContractClientForProcess;
+ realityClient: RealityClient;
};
}) => {
- const { address, profileRegistryClient } = input;
+ const { address, profileRegistryClient, bazarProfileClient, aoContractClientForProcess, realityClient } = input;
const noProfile = {
profileId: undefined,
profileInfo: undefined,
+ assets: undefined,
};
const profiles =
@@ -68,9 +82,71 @@ export const mainMachine = setup({
]);
if (profileInfos.length === 0) return noProfile;
+ let profileClient = bazarProfileClient(primaryProfileId)
+
+ let responseData = await profileClient.grabProfileAssets()
+ // Assuming responseData.Data is a string, we first need to parse it
+ const dataObject = JSON.parse(responseData.Data);
+
+ // Now we can extract the Assets array from the parsed JSON
+ const assetsData = dataObject.Assets;
+
+ // Info Arguments
+ const args: ReadArgs = {
+ tags: [{ name: "Action", value: "Info" }],
+ };
+
+ // Fetch parameters
+ let parameters = await realityClient.readParameters();
+
+ console.log(parameters);
+
+ // Extract the whitelist (or null if it doesn't exist)
+ const whitelist = parameters["asset-whitelist"]?.Whitelist ?? null;
+
+ // Filter and process assets (if whitelist exists, filter by it; otherwise, process all assets)
+ let detailedAssets: DetailedAsset[] = await Promise.all(
+ assetsData
+ .filter((asset: any) => {
+ // If whitelist is null, don't filter (show everything); otherwise, filter by whitelist
+ return whitelist === null || whitelist.includes(asset.Id);
+ })
+ .map(async (asset: any) => {
+ let assetFormal: Asset = Asset.parse(asset);
+
+ let assetClient = aoContractClientForProcess(asset.Id);
+ let message = await assetClient.dryrunReadReplyOne(args);
+
+ let logoTag = message.Tags.find((tag) => tag.name === "Logo");
+ let denominationTag = message.Tags.find((tag) => tag.name === "Denomination");
+
+ if (denominationTag !== undefined) {
+ let denominator = 10 ** Number(denominationTag.value);
+ let quantityValue = Number(assetFormal.Quantity);
+ assetFormal.Quantity = (quantityValue / denominator).toString();
+ }
+
+ let detailedAsset;
+
+ if (logoTag === undefined) {
+ detailedAsset = DetailedAsset.parse({ asset: assetFormal, icon: assetFormal.Id });
+ } else {
+ detailedAsset = DetailedAsset.parse({ asset: assetFormal, icon: logoTag.value });
+ }
+
+ return detailedAsset;
+ })
+ );
+
+ // Parsing and validating the data with the ProfileAssets schema
+ const profileAssets = ProfileAssets.parse(detailedAssets);
+
+ console.log("Validated Profile Assets:", profileAssets);
+
return {
profileId: primaryProfileId,
profileInfo: profileInfos[0],
+ assets: profileAssets,
};
},
),
@@ -96,6 +172,9 @@ export const mainMachine = setup({
input: ({ context }) => ({
address: context.address,
profileRegistryClient: context.profileRegistryClient,
+ bazarProfileClient: context.profileClientForProcess,
+ aoContractClientForProcess: context.aoContractClientForProcess,
+ realityClient: context.realityClientBaseWorldId
}),
onDone: [
@@ -110,6 +189,7 @@ export const mainMachine = setup({
params: ({ event }) => ({
profileId: event.output.profileId!,
profileInfo: event.output.profileInfo!,
+ assets: event.output.assets!,
}),
},
},
diff --git a/src/features/profile/components/AssetCard.tsx b/src/features/profile/components/AssetCard.tsx
new file mode 100644
index 0000000..61c06dd
--- /dev/null
+++ b/src/features/profile/components/AssetCard.tsx
@@ -0,0 +1,23 @@
+import { fetchUrl } from '@/features/arweave/lib/arweave';
+import { DetailedAsset } from '../contract/model'; // Adjust the path as needed
+
+interface AssetCardProps {
+ asset?: DetailedAsset; // Make asset optional
+}
+
+export default function AssetCard({ asset }: AssetCardProps) {
+ if (!asset) return null; // Return nothing if no asset
+
+ return (
+
+
})
+ {asset.asset.Quantity && parseInt(asset.asset.Quantity) > 1 && (
+
+ {asset.asset.Quantity}
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/src/features/profile/components/ProfileAssetsDisplay.tsx b/src/features/profile/components/ProfileAssetsDisplay.tsx
new file mode 100644
index 0000000..954b9c5
--- /dev/null
+++ b/src/features/profile/components/ProfileAssetsDisplay.tsx
@@ -0,0 +1,23 @@
+import { DetailedAsset } from '../contract/model'; // Adjust the path as needed
+import AssetCard from './AssetCard';
+
+interface ProfileAssetsDisplayProps {
+ assets: DetailedAsset[];
+}
+
+export default function ProfileAssetsDisplay({ assets }: ProfileAssetsDisplayProps) {
+ const totalCells = Math.max(4, Math.ceil(assets.length / 4) * 4);
+
+ return (
+
+ {[...Array(totalCells)].map((_, index) => (
+
+ ))}
+
+ );
+}
diff --git a/src/features/profile/components/ProfileButton.tsx b/src/features/profile/components/ProfileButton.tsx
index cf43e7d..3493ccd 100644
--- a/src/features/profile/components/ProfileButton.tsx
+++ b/src/features/profile/components/ProfileButton.tsx
@@ -3,7 +3,7 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
-import { ProfileInfo } from "../contract/model";
+import { ProfileAssets, ProfileInfo } from "../contract/model";
import ProfileDetailsDropdown from "./ProfileDetailsDropdown";
import {
Card,
@@ -16,9 +16,10 @@ import ProfileImage from "./ProfileImage";
interface ProfileButtonProps {
profileInfo?: ProfileInfo;
+ assets?: ProfileAssets;
}
-export default function ProfileButton({ profileInfo }: ProfileButtonProps) {
+export default function ProfileButton({ profileInfo, assets }: ProfileButtonProps) {
return (
@@ -26,7 +27,7 @@ export default function ProfileButton({ profileInfo }: ProfileButtonProps) {
{profileInfo ? (
-
+
) : (
diff --git a/src/features/profile/components/ProfileDetailsDropdown.tsx b/src/features/profile/components/ProfileDetailsDropdown.tsx
index 88fd013..bcc553a 100644
--- a/src/features/profile/components/ProfileDetailsDropdown.tsx
+++ b/src/features/profile/components/ProfileDetailsDropdown.tsx
@@ -1,13 +1,16 @@
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
-import { ProfileInfo } from "../contract/model";
+import { ProfileAssets, ProfileInfo } from "../contract/model";
import ProfileImage from "./ProfileImage";
+import ProfileAssetsDisplay from './ProfileAssetsDisplay'; // Import your new component
interface ProfileDetailsDropdownProps {
profileInfo: ProfileInfo;
+ profileAssets?: ProfileAssets;
}
export default function ProfileDetailsDropdown({
profileInfo,
+ profileAssets
}: ProfileDetailsDropdownProps) {
const hasDescription =
profileInfo.Description && profileInfo.Description != "";
@@ -32,8 +35,12 @@ export default function ProfileDetailsDropdown({
) : (
No bio
)}
+
- Edit your profile on{" "}
+ Edit your profile and view all your assets not just World specific ones on:{" "}
;
+
+// Define the Asset schema
+export const Asset = z.object({
+ Quantity: z.string(),
+ Id: z.string(),
+});
+export type Asset = z.infer;
+
+// Define the DetailedAsset schema with an optional icon
+export const DetailedAsset = z.object({
+ asset: Asset,
+ icon: z.string(), // Making icon optional
+});
+export type DetailedAsset = z.infer;
+
+// Define the ProfileAssets schema as an array of DetailedAssets
+export const ProfileAssets = z.array(DetailedAsset);
+export type ProfileAssets = z.infer;
\ No newline at end of file
diff --git a/src/features/profile/contract/profileClient.ts b/src/features/profile/contract/profileClient.ts
index df6b8f6..8a2b6b0 100644
--- a/src/features/profile/contract/profileClient.ts
+++ b/src/features/profile/contract/profileClient.ts
@@ -7,12 +7,13 @@ import { connect } from "@permaweb/aoconnect";
import { profileAOS } from "./config";
import { fetchUrl } from "@/features/arweave/lib/arweave";
import { ProfileInfoCreate } from "./model";
-import { MessageResult } from "@/features/ao/lib/aoClient";
+import { Message, MessageResult } from "@/features/ao/lib/aoClient";
export type ProfileClient = {
aoContractClient: AoContractClient;
// Reads
+ grabProfileAssets(): Promise;
// Writes
initializeProcess(): Promise;
@@ -26,6 +27,15 @@ export const createProfileClient = (
): ProfileClient => ({
aoContractClient: aoContractClient,
+ // Reads
+ grabProfileAssets: async () => {
+ const message = await aoContractClient.dryrunReadReplyOne({
+ tags: [{ name: "Action", value: "Info" }],
+ });
+ console.log(message, ' ', message.Data)
+ return message;
+ },
+
// Writes
initializeProcess: async () => {
const profileSrc = await fetch(fetchUrl(profileAOS.profileSrc)).then(
@@ -48,8 +58,10 @@ export const createProfileClient = (
}),
});
+export type ProfileClientForProcess = (processId: string) => ProfileClient;
+
export const createProfileClientForProcess =
- (wallet: AoWallet) => (processId: string) => {
+ (wallet: AoWallet): ProfileClientForProcess => (processId: string) => {
const aoContractClient = createAoContractClient(
processId,
connect(),
diff --git a/src/features/reality/contract/model.ts b/src/features/reality/contract/model.ts
index 43c5404..49b3b98 100644
--- a/src/features/reality/contract/model.ts
+++ b/src/features/reality/contract/model.ts
@@ -25,12 +25,21 @@ export const RealityToken = z.object({
});
export type RealityToken = z.infer;
+// Defining a constant for whitelist schema
+export const RealityWhitelist = z.object({
+ Whitelist: z.array(ArweaveAddress) // Array of Arweave addresses
+});
+export type RealityWhitelist = z.infer;
+
+// RealityParameters defines the full schema for parameters including token, bounds, 2D-Tile, Audio, and Whitelist
export const RealityParameters = z.object({
- Token: z.optional(RealityToken),
- Bounds: z.optional(RealityParameterBounds),
- "2D-Tile-0": z.optional(_2dTileParams),
- "Audio-0": z.optional(AudioParams),
+ Token: z.optional(RealityToken), // Optional token object
+ Bounds: z.optional(RealityParameterBounds), // Optional bounds object
+ "2D-Tile-0": z.optional(_2dTileParams), // Optional 2D tile parameters
+ "Audio-0": z.optional(AudioParams), // Optional audio parameters
+ "asset-whitelist": z.optional(RealityWhitelist), // Optional whitelist object
});
+
export type RealityParameters = z.infer;
export const RealityEntityType = z.enum(["Unknown", "Avatar", "Hidden"]);