diff --git a/src/common/Spacer.tsx b/src/common/Spacer.tsx index e8b2288..b1a2743 100644 --- a/src/common/Spacer.tsx +++ b/src/common/Spacer.tsx @@ -1,13 +1,14 @@ import { Box } from "@mui/material"; -export const Spacer = ({ height, width }: any) => { +export const Spacer = ({ height, width, ...props }: any) => { return ( ); diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index fecbb80..615965b 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -11,6 +11,11 @@ import { AppInfoSnippetMiddle, AppInfoSnippetRight, AppInfoUserName, + AppsCategoryInfo, + AppsCategoryInfoLabel, + AppsCategoryInfoSub, + AppsCategoryInfoValue, + AppsInfoDescription, AppsLibraryContainer, AppsParent, AppsWidthLimiter, @@ -22,88 +27,111 @@ import LogoSelected from "../../assets/svgs/LogoSelected.svg"; import { Spacer } from "../../common/Spacer"; import { executeEvent } from "../../utils/events"; +import { AppRating } from "./AppRating"; -export const AppInfo = ({ app }) => { - - - const isInstalled = app?.status?.status === 'READY' +export const AppInfo = ({ app, myName }) => { + const isInstalled = app?.status?.status === "READY"; return ( - - - - - - - + + + - - center-icon - - - - - - - {app?.metadata?.title || app?.name} - - - - { app?.name} - - - - - - - - - - - - { - executeEvent("addTab", { - data: app - }) - }} sx={{ - backgroundColor: isInstalled ? '#0091E1' : '#247C0E', - width: '100%', - maxWidth: '320px', - height: '29px' - }}> - {isInstalled ? 'Open' : 'Download'} + > + + center-icon + + + + + + {app?.metadata?.title || app?.name} + + + {app?.name} + + + + + + + { + executeEvent("addTab", { + data: app, + }); + }} + sx={{ + backgroundColor: isInstalled ? "#0091E1" : "#247C0E", + width: "100%", + maxWidth: "320px", + height: "29px", + }} + > + + {isInstalled ? "Open" : "Download"} + - + + + + + + + + + + + Category: + + + {app?.metadata?.categoryName || "none"} + + + + + + About this Q-App + + + + + {app?.metadata?.description || "No description"} + - - ); }; diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx index 5e58271..ae5fbc1 100644 --- a/src/components/Apps/AppRating.tsx +++ b/src/components/Apps/AppRating.tsx @@ -1,202 +1,251 @@ import { Box, Rating, Typography } from "@mui/material"; -import React, { useCallback, useContext, useEffect, useRef, useState } from "react"; +import React, { + useCallback, + useContext, + useEffect, + useRef, + useState, +} from "react"; import { getFee } from "../../background"; import { MyContext, getBaseApiReact } from "../../App"; import { CustomizedSnackbars } from "../Snackbar/Snackbar"; import { StarFilledIcon } from "../../assets/svgs/StarFilled"; import { StarEmptyIcon } from "../../assets/svgs/StarEmpty"; import { AppInfoUserName } from "./Apps-styles"; +import { Spacer } from "../../common/Spacer"; -export const AppRating = ({app, myName, ratingCountPosition = 'right'}) => { +export const AppRating = ({ app, myName, ratingCountPosition = "right" }) => { const [value, setValue] = useState(0); const { show } = useContext(MyContext); -const [hasPublishedRating, setHasPublishedRating] = useState(null) -const [pollInfo, setPollInfo] = useState(null) -const [votesInfo, setVotesInfo] = useState(null) -const [openSnack, setOpenSnack] = useState(false); -const [infoSnack, setInfoSnack] = useState(null); -const hasCalledRef = useRef(false) -console.log(`pollinfo-${app?.service}-${app?.name}`, value) + const [hasPublishedRating, setHasPublishedRating] = useState( + null + ); + const [pollInfo, setPollInfo] = useState(null); + const [votesInfo, setVotesInfo] = useState(null); + const [openSnack, setOpenSnack] = useState(false); + const [infoSnack, setInfoSnack] = useState(null); + const hasCalledRef = useRef(false); + console.log(`pollinfo-${app?.service}-${app?.name}`, value); -console.log('hasPublishedRating', hasPublishedRating) -const getRating = useCallback(async (name, service)=> { + console.log("hasPublishedRating", hasPublishedRating); + const getRating = useCallback(async (name, service) => { try { + hasCalledRef.current = true; + const pollName = `app-library-${service}-rating-${name}`; + const url = `${getBaseApiReact()}/polls/${pollName}`; - hasCalledRef.current = true - const pollName = `app-library-${service}-rating-${name}` - const url = `${getBaseApiReact()}/polls/${pollName}`; + const response = await fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - - const responseData = await response.json(); - console.log('responseData', responseData) - if(responseData?.message?.includes('POLL_NO_EXISTS')){ - setHasPublishedRating(false) - } else if(responseData?.pollName){ - setPollInfo(responseData) - setHasPublishedRating(true) + const responseData = await response.json(); + console.log("responseData", responseData); + if (responseData?.message?.includes("POLL_NO_EXISTS")) { + setHasPublishedRating(false); + } else if (responseData?.pollName) { + setPollInfo(responseData); + setHasPublishedRating(true); const urlVotes = `${getBaseApiReact()}/polls/votes/${pollName}`; - const responseVotes = await fetch(urlVotes, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - - const responseDataVotes = await responseVotes.json(); - setVotesInfo(responseDataVotes) - const voteCount = responseDataVotes.voteCounts - // Include initial value vote in the calculation - const ratingVotes = voteCount.filter(vote => !vote.optionName.startsWith("initialValue-")); - const initialValueVote = voteCount.find(vote => vote.optionName.startsWith("initialValue-")); - console.log('initialValueVote', initialValueVote) - if (initialValueVote) { - // Convert "initialValue-X" to just "X" and add it to the ratingVotes array - const initialRating = parseInt(initialValueVote.optionName.split("-")[1], 10); - console.log('initialRating', initialRating) - ratingVotes.push({ - optionName: initialRating.toString(), - voteCount: 1, - }); - } + const responseVotes = await fetch(urlVotes, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); - // Calculate the weighted average - let totalScore = 0; - let totalVotes = 0; - - ratingVotes.forEach(vote => { - const rating = parseInt(vote.optionName, 10); // Extract rating value (1-5) - const count = vote.voteCount; - totalScore += rating * count; // Weighted score - totalVotes += count; // Total number of votes - }); - console.log('ratingVotes', ratingVotes, totalScore, totalVotes) - - // Calculate average rating (ensure no division by zero) - const averageRating = totalVotes > 0 ? (totalScore / totalVotes) : 0; - setValue(averageRating); - } - } catch (error) { - console.log('error rating', error) - if(error?.message?.includes('POLL_NO_EXISTS')){ - setHasPublishedRating(false) + const responseDataVotes = await responseVotes.json(); + setVotesInfo(responseDataVotes); + const voteCount = responseDataVotes.voteCounts; + // Include initial value vote in the calculation + const ratingVotes = voteCount.filter( + (vote) => !vote.optionName.startsWith("initialValue-") + ); + const initialValueVote = voteCount.find((vote) => + vote.optionName.startsWith("initialValue-") + ); + console.log("initialValueVote", initialValueVote); + if (initialValueVote) { + // Convert "initialValue-X" to just "X" and add it to the ratingVotes array + const initialRating = parseInt( + initialValueVote.optionName.split("-")[1], + 10 + ); + console.log("initialRating", initialRating); + ratingVotes.push({ + optionName: initialRating.toString(), + voteCount: 1, + }); } + + // Calculate the weighted average + let totalScore = 0; + let totalVotes = 0; + + ratingVotes.forEach((vote) => { + const rating = parseInt(vote.optionName, 10); // Extract rating value (1-5) + const count = vote.voteCount; + totalScore += rating * count; // Weighted score + totalVotes += count; // Total number of votes + }); + console.log("ratingVotes", ratingVotes, totalScore, totalVotes); + + // Calculate average rating (ensure no division by zero) + const averageRating = totalVotes > 0 ? totalScore / totalVotes : 0; + setValue(averageRating); + } + } catch (error) { + console.log("error rating", error); + if (error?.message?.includes("POLL_NO_EXISTS")) { + setHasPublishedRating(false); + } } + }, []); + useEffect(() => { + if (hasCalledRef.current) return; + if (!app) return; + getRating(app?.name, app?.service); + }, [getRating, app?.name]); - -}, []) - useEffect(()=> { - if(hasCalledRef.current) return - if(!app) return - getRating(app?.name, app?.service) - }, [getRating, app?.name]) - - const rateFunc = async (event, newValue)=> { + const rateFunc = async (event, newValue) => { try { - if(!myName) throw new Error('You need a name to rate.') - if(!app?.name) return - console.log('newValue', newValue) - const fee = await getFee("ARBITRARY"); + if (!myName) throw new Error("You need a name to rate."); + if (!app?.name) return; + console.log("newValue", newValue); + const fee = await getFee("ARBITRARY"); await show({ message: `Would you like to rate this app a rating of ${newValue}?`, publishFee: fee.fee + " QORT", }); - if(hasPublishedRating === false){ - const pollName = `app-library-${app.service}-rating-${app.name}` - const pollOptions = [`1, 2, 3, 4, 5, initialValue-${newValue}`] - await new Promise((res, rej)=> { - chrome?.runtime?.sendMessage({ - action: 'CREATE_POLL', type: 'qortalRequest', payload: { - pollName: pollName , pollDescription: `Rating for ${app.service} ${app.name}`, pollOptions: pollOptions , pollOwnerAddress : myName + if (hasPublishedRating === false) { + const pollName = `app-library-${app.service}-rating-${app.name}`; + const pollOptions = [`1, 2, 3, 4, 5, initialValue-${newValue}`]; + await new Promise((res, rej) => { + chrome?.runtime?.sendMessage( + { + action: "CREATE_POLL", + type: "qortalRequest", + payload: { + pollName: pollName, + pollDescription: `Rating for ${app.service} ${app.name}`, + pollOptions: pollOptions, + pollOwnerAddress: myName, + }, + }, + (response) => { + console.log("response", response); + if (response.error) { + rej(response?.message); + return; + } else { + res(response); + setInfoSnack({ + type: "success", + message: + "Successfully rated. Please wait a couple minutes for the network to propogate the changes.", + }); + setOpenSnack(true); + } } - }, (response) => { - console.log('response', response); - if (response.error) { - rej(response?.message) - return - } else { - res(response) - setInfoSnack({ - type: "success", - message: - "Successfully rated. Please wait a couple minutes for the network to propogate the changes.", - }); - setOpenSnack(true); - } - }); - }) - } else { - const pollName = `app-library-${app.service}-rating-${app.name}` - const optionIndex = pollInfo?.pollOptions.findIndex((option)=> +option.optionName === +newValue) - if(isNaN(optionIndex) || optionIndex === -1) throw new Error('Cannot find rating option') - await new Promise((res, rej)=> { - chrome?.runtime?.sendMessage({ - action: 'VOTE_ON_POLL', type: 'qortalRequest', payload: { - pollName: pollName , optionIndex - } - }, (response) => { - console.log('response', response); - if (response.error) { - rej(response?.message) - return - } else { - res(response) - setInfoSnack({ - type: "success", - message: - "Successfully rated. Please wait a couple minutes for the network to propogate the changes.", - }); - setOpenSnack(true); - } - }); - }) - } - - } catch (error) { - setInfoSnack({ - type: "error", - message: error.message || "An error occurred while trying to rate.", + ); }); - setOpenSnack(true); + } else { + const pollName = `app-library-${app.service}-rating-${app.name}`; + const optionIndex = pollInfo?.pollOptions.findIndex( + (option) => +option.optionName === +newValue + ); + if (isNaN(optionIndex) || optionIndex === -1) + throw new Error("Cannot find rating option"); + await new Promise((res, rej) => { + chrome?.runtime?.sendMessage( + { + action: "VOTE_ON_POLL", + type: "qortalRequest", + payload: { + pollName: pollName, + optionIndex, + }, + }, + (response) => { + console.log("response", response); + if (response.error) { + rej(response?.message); + return; + } else { + res(response); + setInfoSnack({ + type: "success", + message: + "Successfully rated. Please wait a couple minutes for the network to propogate the changes.", + }); + setOpenSnack(true); + } + } + ); + }); + } + } catch (error) { + setInfoSnack({ + type: "error", + message: error.message || "An error occurred while trying to rate.", + }); + setOpenSnack(true); } - } - console.log('vvotes', (votesInfo?.totalVotes ?? 0 ) + votesInfo?.voteCounts?.length === 6 ? 1 : 0, votesInfo) + }; + console.log( + "vvotes", + (votesInfo?.totalVotes ?? 0) + votesInfo?.voteCounts?.length === 6 ? 1 : 0, + votesInfo + ); return (
- - } - emptyIcon={} + - {ratingCountPosition && ( - - { (votesInfo?.totalVotes ?? 0) + (votesInfo?.voteCounts?.length === 6 ? 1 : 0)} - + display: "flex", + alignItems: "center", + flexDirection: ratingCountPosition === "top" ? "column" : "row", + }} + > + {ratingCountPosition === "top" && ( + <> + + {(votesInfo?.totalVotes ?? 0) + + (votesInfo?.voteCounts?.length === 6 ? 1 : 0)}{" "} + {" RATINGS"} + + + {value?.toFixed(1)} + + )} - - - } + emptyIcon={} + sx={{ + display: "flex", + gap: "2px", + }} + /> + {ratingCountPosition === "right" && ( + + {(votesInfo?.totalVotes ?? 0) + + (votesInfo?.voteCounts?.length === 6 ? 1 : 0)} + + )} + + + ({ + display: "flex", + alignItems: 'center', + width: '100%', + })); + + export const AppsCategoryInfoSub = styled(Box)(({ theme }) => ({ + display: "flex", + flexDirection: 'column', + })); + export const AppsCategoryInfoLabel = styled(Typography)(({ theme }) => ({ + fontSize: '12px', + fontWeight: 700, + lineHeight: 1.2, + color: '#8D8F93', + })); + export const AppsCategoryInfoValue = styled(Typography)(({ theme }) => ({ + fontSize: '12px', + fontWeight: 500, + lineHeight: 1.2, + color: '#8D8F93', + })); + export const AppsInfoDescription = styled(Typography)(({ theme }) => ({ + fontSize: '13px', + fontWeight: 300, + lineHeight: 1.2, + width: '90%', + textAlign: 'start' })); \ No newline at end of file diff --git a/src/components/Apps/Apps.tsx b/src/components/Apps/Apps.tsx index 0c2d1f3..1c532c2 100644 --- a/src/components/Apps/Apps.tsx +++ b/src/components/Apps/Apps.tsx @@ -267,7 +267,7 @@ export const Apps = ({ mode, setMode, show , myName}) => { hasPublishApp={!!(myApp || myWebsite)} /> )} - {mode === "appInfo" && } + {mode === "appInfo" && } {mode === "publish" && } {tabs.map((tab) => {