Skip to content

Commit

Permalink
feat: added ownership registration
Browse files Browse the repository at this point in the history
  • Loading branch information
Jadapema committed Jan 30, 2025
1 parent 0ac9a6f commit a057b1d
Show file tree
Hide file tree
Showing 11 changed files with 389 additions and 370 deletions.
8 changes: 1 addition & 7 deletions src/hooks/use-register-asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,11 @@ export const useRegisterAsset = (): UseRegisterAssetHook => {
try {
const toAddress = sessionData?.profile?.ownedBy.address;
// const asset = BigInt(assetId).toString(10)
const asset = BigInt(assetId)
const asset = assetId.replace(/^f0/, '0x');
if (!toAddress) {
throw new Error('The active account address was not found in the session.');
}

console.log('address')
console.log(toAddress)
console.log('asset')
console.log(assetId)
console.log(asset)

const registerAssetData = encodeFunctionData({
abi: AssetOwnershipAbi.abi,
functionName: 'registerAsset',
Expand Down
221 changes: 221 additions & 0 deletions src/hooks/use-submit-assets-to-lens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import { useState, useCallback } from "react";
import { useCreatePost } from "@lens-protocol/react-web";
import { AnyMedia, MediaVideoMimeType, video } from "@lens-protocol/metadata";
import { uploadMetadataToIPFS, verifyIpfsData } from "@src/utils/ipfs.ts";
import uuidv4 from "@src/utils/uuidv4.ts";
import { ERRORS } from "@notifications/errors.ts";

interface SuccessResult {
hash: string;
status: "success";
}

interface ErrorResult {
hash: string;
status: "error";
message: string;
}

interface UseSubmitAssetToLensReturn {
data: SuccessResult[];
errors: ErrorResult[];
loading: boolean;
submitAssetToLens: (hashesString: string) => Promise<void>;
}

export function useSubmitAssetToLens(): UseSubmitAssetToLensReturn {
const [data, setData] = useState<SuccessResult[]>([]);
const [errors, setErrors] = useState<ErrorResult[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const { execute: createPost } = useCreatePost();

/**
* Sanitize the description to avoid strange characters
*/
const sanitizeDescription = useCallback((description: any): string => {
if (typeof description !== "string") {
return description;
}
let sanitized = description.replace(/"/g, "'");
sanitized = sanitized.replace(/\\/g, "");
// eslint-disable-next-line no-control-regex
sanitized = sanitized.replace(/[\x00-\x1F\x7F]/g, "");
return sanitized;
}, []);

/**
* Process a single hash
*/
const processHash = useCallback(
async (asset: string): Promise<SuccessResult | ErrorResult> => {
try {
// 1. Get metadata from the endpoint
const response = await fetch(`https://g.watchit.movie/metadata/${asset}/`);

if (!response.ok) {
return {
hash: asset,
status: "error",
message: `Error fetching metadata: ${response.statusText}`,
};
}

const responseData = await response.json();
const title = responseData.Data.title;
const descriptionRaw = responseData.Data.description;
const description = sanitizeDescription(descriptionRaw);

// 2. Search for wallpaper and poster/large CIDs
let wallpaperCid = "";
let largeCid = "";

for (const attachment of responseData.Data.attachments) {
if (attachment.title === "wallpaper") {
wallpaperCid = attachment.cid;
} else if (attachment.title === "large") {
largeCid = attachment.cid;
}
}

if (!wallpaperCid || !largeCid) {
return {
hash: asset,
status: "error",
message: "Missing wallpaper or large CIDs",
};
}

const getMediaUri = (cid: string) => `https://g.watchit.movie/content/${cid}/`;

// 3. Assemble the attachments (media)
const mediaItems: AnyMedia[] = [
{
item: getMediaUri(largeCid) as any,
type: "image/jpeg" as any,
altTag: "poster" as any,
},
{
item: getMediaUri(wallpaperCid) as any,
type: "image/png" as any,
altTag: "wallpaper" as any,
},
];

// 4. Create the metadata for Lens with @lens-protocol/metadata
const metadata = video({
id: uuidv4(),
title: title,
content: description,
video: {
item: asset,
type: MediaVideoMimeType.MP4,
altTag: "asset",
},
locale: "en",
attachments: mediaItems,
appId: "watchit",
});

// 5. Upload the metadata to IPFS via Pinata
const metadataUri = await uploadMetadataToIPFS(metadata);

// 6. Verify IPFS data
await verifyIpfsData(metadataUri);

// 7. Create the post in Lens
const result = await createPost({
metadata: metadataUri,
});

if (result.isFailure()) {
return {
hash: asset,
status: "error",
message: ERRORS.ASSET_OWNERSHIP_REGISTER_ERROR,
};
} else {
// 8. Wait for the transaction to be mined
const completion = await result.value.waitForCompletion();
if (completion.isFailure()) {
return {
hash: asset,
status: "error",
message: ERRORS.ASSET_OWNERSHIP_REGISTER_ERROR,
};
} else {
return {
hash: asset,
status: "success",
};
}
}
} catch (err: any) {
return {
hash: asset,
status: "error",
message: ERRORS.ASSET_OWNERSHIP_REGISTER_ERROR,
};
}
},
[createPost, sanitizeDescription]
);

/**
* Main function that processes one or more hashes (separated by commas)
*/
const submitAssetToLens = useCallback(
async (hashesString: string) => {
setLoading(true);
setErrors([]);
setData([]);

// Separate the string by commas and clean spaces
const hashes = hashesString
.split(",")
.map((h) => h.trim())
.filter(Boolean);

// Map each hash to the processing function
const promises = hashes.map((hash) => processHash(hash));

// Wait for all promises to settle
const results = await Promise.allSettled(promises);

// Separate successful and failed results
const successfulResults: SuccessResult[] = [];
const errorResults: ErrorResult[] = [];

results.forEach((result, index) => {
if (result.status === "fulfilled") {
const res = result.value;

if (res.status === "success") {
successfulResults.push(res as SuccessResult);
} else {
errorResults.push(res as ErrorResult);
}
} else {
// This shouldn't happen as processHash handles its own errors
errorResults.push({
hash: hashes[index],
status: "error",
message: "Unexpected error",
});
}
});

// Update state after all hashes are processed
setData(successfulResults);
setErrors(errorResults);
setLoading(false);
},
[processHash]
);

return {
data,
errors,
loading,
submitAssetToLens,
};
}
18 changes: 10 additions & 8 deletions src/pages/dashboard/marketing.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import { Helmet } from 'react-helmet-async';
import BlankView from '../../sections/blank/view';
import ComingSoonView from '../../sections/coming-soon/view';
import {useSelector} from "react-redux";
import {canViewSection} from "@src/pages/dashboard/studio.tsx";
import {canViewSection} from "@src/pages/dashboard/ownership.tsx";
import Header from "@src/layouts/dashboard/header.tsx";
import HeaderContent from "@src/layouts/dashboard/header-content.tsx";
import MarketingView from "@src/sections/marketing";
import { GLOBAL_CONSTANTS } from '@src/config-global.ts';
import { OgMetaTags } from '@src/components/og-meta-tags.tsx';

// ----------------------------------------------------------------------

export default function ChatPage() {
const sessionData = useSelector((state: any) => state.auth.session);
return (
<>
<Helmet>
<title> WatchIt | Marketing</title>
</Helmet>

<OgMetaTags
title="Watchit: Marketing (COMING SOON)"
description="Promote your content, engage audiences, and maximize visibility with AI-powered marketing tools. Stay tuned!"
url={`${GLOBAL_CONSTANTS.BASE_URL}/marketing/`}
>
{
canViewSection(sessionData) ? (<><Header>
<HeaderContent title="Marketing" />
Expand All @@ -27,6 +29,6 @@ export default function ChatPage() {
</BlankView>
</>)
}
</>
</OgMetaTags>
);
}
10 changes: 9 additions & 1 deletion src/pages/dashboard/ownership.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@ import { OgMetaTags } from '@src/components/og-meta-tags.tsx';
import Header from '@src/layouts/dashboard/header.tsx';
import HeaderContent from '@src/layouts/dashboard/header-content.tsx';
import Ownership from '@src/sections/ownership';
import { canViewSection } from '@src/pages/dashboard/studio.tsx';
import { useSelector } from 'react-redux';

// ----------------------------------------------------------------------

export const canViewSection = (sessionData: any): boolean => {
// Allowed profileId to view (temporary) this section
const allowedProfilesId = ['0x0563', '0x050d','0x055c','0x0514', '0x0510']; // Mihail, Carlos, Jacob, Geolffrey and Watchit Open
// Verify if the current profile is allowed to view this section
return allowedProfilesId.includes(sessionData?.profile?.id ?? '');
}

// ----------------------------------------------------------------------

export default function FileManagerPage() {
const sessionData = useSelector((state: any) => state.auth.session);

Expand Down
Loading

0 comments on commit a057b1d

Please sign in to comment.