Skip to content

Commit

Permalink
Merge branch 'main' of github.com:VoiceDeck/app into feat/global-footer
Browse files Browse the repository at this point in the history
  • Loading branch information
0xRowdy committed Feb 7, 2024
2 parents bc1fa7f + 3ac994e commit 3aa4e6c
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 33 deletions.
81 changes: 68 additions & 13 deletions app/impact-reports.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import {
HypercertMetadata,
HypercertsStorage,
} from "@hypercerts-org/sdk";
import { Claim, Report } from "~/types";
import { CMSContent, Claim, Report } from "~/types";

let reports: Report[] | null = null;
// represents contents retrieved from CMS `reports` collection
let cmsContents: CMSContent[] | null = null;
let hypercertClient: HypercertClient | null = null;

/**
Expand All @@ -26,23 +28,54 @@ export const fetchReports = async (ownerAddress: string): Promise<Report[]> => {
ownerAddress,
getHypercertClient().indexer,
);

reports = await Promise.all(
claims.map(async (claim, index) => {
const metadata = await getHypercertMetadata(
claim.uri as string,
getHypercertClient().storage,
);

const fromCMS = await getCMSContents();
const cmsReport = fromCMS.find(
(cmsReport) => cmsReport.title === metadata.name,
);
if (!cmsReport) {
throw new Error(
`CMS content for report titled '${metadata.name}' not found.`,
);
}

return {
id: claim.id,
hypercertId: claim.id,
title: metadata.name,
summary: metadata.description,
image: metadata.image,
// use hardcoded values for now
// TODO: fetch from CMS or define type(or enum or whatever)
state: index === 0 ? "Madhya Pradesh" : "Kerala",
originalReportUrl: metadata.external_url,
// first indice of `metadata.properties` holds the value of the state
state: metadata.properties?.[0].value,
category: metadata.hypercert?.work_scope.value?.[0],
// tentatively, it represent $1000
totalCost: 1000,
workTimeframe: metadata.hypercert?.work_timeframe.display_value,
impactScope: metadata.hypercert?.impact_scope.display_value,
impactTimeframe: metadata.hypercert?.impact_timeframe.display_value,
contributors: metadata.hypercert?.contributors.value?.map(
(name) => name,
),

// properties stored in CMS
cmsId: cmsReport.id,
status: cmsReport.status,
dateCreated: cmsReport.date_created,
slug: cmsReport.slug,
story: cmsReport.story,
bcRatio: cmsReport.bc_ratio,
villagesImpacted: cmsReport.villages_impacted,
peopleImpacted: cmsReport.people_impacted,
verifiedBy: cmsReport.verified_by,
dateUpdated: cmsReport.date_updated,
byline: cmsReport.byline,
totalCost: Number(cmsReport.total_cost),

// TODO: fetch from blockchain when Hypercert Marketplace is ready
fundedSoFar: Math.floor(Math.random() * 1000),
} as Report;
Expand Down Expand Up @@ -86,12 +119,7 @@ export const getHypercertClaims = async (
console.log(`Fetching claims owned by ${ownerAddress}`);
try {
// see graphql query: https://github.com/hypercerts-org/hypercerts/blob/d7f5fee/sdk/src/indexer/queries/claims.graphql#L1-L11
const response = await indexer.claimsByOwner(ownerAddress as string, {
orderDirections: "asc",
first: 100,
// skip the first 2 claims (they are dummy of 0x42FbF4d890B4EFA0FB0b56a9Cc2c5EB0e07C1536 in Sepolia testnet)
skip: 2,
});
const response = await indexer.claimsByOwner(ownerAddress as string);
claims = (response as ClaimsByOwnerQuery).claims as Claim[];
console.log(`Fetched claims: ${claims ? claims.length : 0}`);

Expand Down Expand Up @@ -125,3 +153,30 @@ export const getHypercertMetadata = async (
throw new Error(`Failed to fetch metadata of ${claimUri}`);
}
};

/**
* Fetches the contents of the CMS `reports` collection.
* @returns A promise that resolves to an array of CMS contents.
* @throws Will throw an error if the CMS contents cannot be fetched.
*/
export const getCMSContents = async (): Promise<CMSContent[]> => {
try {
if (cmsContents) {
console.log("CMS contents already exist, no need to fetch from remote");
console.log(`Existing CMS contents: ${cmsContents.length}`);
} else {
console.log("Fetching CMS contents from remote");
const response = await fetch(process.env.CMS_ENDPOINT as string);
if (!response.ok) {
throw new Error(`failed to fetch data from CMS : ${response.status}`);
}
const data = await response.json();
cmsContents = data.data as CMSContent[];
}

return cmsContents;
} catch (error) {
console.error(`Failed to fetch CMS contents: ${error}`);
throw new Error("Failed to fetch CMS contents");
}
};
19 changes: 13 additions & 6 deletions app/routes/_index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ function GetIcon({
strokeWidth: string;
size: string;
}) {
// this is a temporary fix for the category name mismatch in CMS configuration
// FIXME: remove this temporary fix once the category name mismatch is resolved in CMS configuration and it's minted again
category = category === "Oppurtunity" ? "Opportunity" : category;

const CategoryIcon = iconComponents[category];
return <CategoryIcon color={color} strokeWidth={strokeWidth} size={size} />;
}
Expand Down Expand Up @@ -100,7 +104,7 @@ export default function Index() {
// using id as placeholder for media outet name - not currently available on our example hypercerts
const uniqueIds = useMemo(() => {
return reports
.map((report: Report, index: number) => report.id)
.map((report: Report, index: number) => report.hypercertId)
.filter(
(value: string, index: number, self: string[]) =>
self.indexOf(value) === index,
Expand Down Expand Up @@ -200,10 +204,13 @@ export default function Index() {
</div>
<div className="border border-b-vd-blue-400 pt-6 pb-4">
<h2 className="text-base font-medium pb-4">Story from</h2>
{uniqueIds.map((id: string) => (
<div key={id} className="flex items-center gap-2 pb-1">
{uniqueIds.map((hypercert_id: string) => (
<div
key={hypercert_id}
className="flex items-center gap-2 pb-1"
>
<Circle size={18} strokeWidth={1} />
<p className="text-sm">{id.slice(0, 15)}</p>
<p className="text-sm">{hypercert_id.slice(0, 15)}</p>
</div>
))}
</div>
Expand All @@ -223,7 +230,7 @@ export default function Index() {
</section>
<section className="flex flex-wrap gap-5 md:gap-3">
{reports.map((report: Report) => (
<Card key={report.id}>
<Card key={report.hypercertId}>
<div className="h-[150px] overflow-hidden">
<img
src={report.image}
Expand Down Expand Up @@ -260,7 +267,7 @@ export default function Index() {
))}
{/* mapping out our 2 reports again to see how they fit */}
{reports.map((report: Report) => (
<Card key={report.id}>
<Card key={report.hypercertId}>
<div className="h-[150px] overflow-hidden">
<img
src={report.image}
Expand Down
93 changes: 79 additions & 14 deletions app/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,62 @@
/**
* Represents 1 Impact report
* @property {string} id - The ID of associated hypercert
* @property {string} title - The title of the report
* @property {string} summary - The summary of the report
* @property {string} image - The image of the report
* @property {string} state - The state where the impact is being made
* @property {string} category - The category of the report
* @property {number} totalCost - The total cost of the report in USD
* @property {number} fundedSoFar - The amount funded so far in USD
* @property {string} created_at - The date the report was created
* @property {string} updated_at - The date the report was updated
* Defines the structure of an Impact Report.
*
* @interface Report
* @property {string} hypercertId - Claim id of hypercert.
* @property {string} title - Title of the report.
* @property {string} summary - Brief summary of the report.
* @property {string} image - the image representing the report.
* @property {string} originalReportUrl - URL to the original report.
* @property {string} state - State where the impact is being made.
* @property {string} category - Category under which the report falls.
* @property {string} workTimeframe - Timeframe during which the work was performed.
* @property {string} impactScope - Scope of the impact.
* @property {string} impactTimeframe - Timeframe of the impact.
* @property {string[]} contributors - List of contributors to the report.
* @property {string} cmsId - Identifier for the report in the CMS.
* @property {string} status - Publication status of the report.
* @property {string | null} dateCreated - Date when the report was created.
* * @property {string | null} dateUpdated - Date when the report was last updated.
* @property {string} slug - Slug for the report URL.
* @property {string | null} story - Detailed story of the report.
* @property {number | null} bcRatio - bc ratio of the impact.
* @property {number | null} villagesImpacted - Number of villages impacted.
* @property {number | null} peopleImpacted - Number of people impacted.
* @property {string[] | null} verifiedBy - Entities that have verified the report.
* @property {string | null} byline - Byline of the report.
* @property {number} totalCost - Total cost of the report.
* @property {number} fundedSoFar - Amount funded so far.
*/
export interface Report {
id: string;
// properties for hypercert minting
hypercertId: string;
title: string;
summary: string;
image: string;
originalReportUrl: string;
state: string;
category: string;
workTimeframe: string;
impactScope: string;
impactTimeframe: string;
contributors: string[];

// properties stored in CMS
cmsId: string;
dateCreated: string | null;
dateUpdated: string | null;
status: string;
slug: string;
story: string | null;
bcRatio: number | null;
villagesImpacted: number | null;
peopleImpacted: number | null;
verifiedBy: string[] | null;
byline: string | null;
totalCost: number;

// properties regarding to hypercert marketplace
fundedSoFar: number;
created_at?: string;
updated_at?: string;
}

/**
Expand Down Expand Up @@ -49,3 +84,33 @@ export interface Claim {
totalUnits?: any | null;
uri?: string | null;
}

// type definition for the report object that is returned from VoiceDeck CMS
export interface CMSContent {
// properties for hypercert minting
title: string | null;
summary: string | null;
image: string | null;
original_report_url: string | null;
states: string[] | null;
category: string | null;
work_timeframe: string | null;
impact_scope: string | null;
impact_timeframe: string | null;
// NOTE: it's actually comma separated string
contributor: string | null;

//non hypercert propoerties
id: string;
status: string;
date_created: string | null;
slug: string;
story: string | null;
bc_ratio: number | null;
villages_impacted: number | null;
people_impacted: number | null;
verified_by: string[] | null;
date_updated: string | null;
byline: string | null;
total_cost: string | null;
}

0 comments on commit 3aa4e6c

Please sign in to comment.