diff --git a/components/UI/iconsComponents/icons/shareIcon.tsx b/components/UI/iconsComponents/icons/shareIcon.tsx new file mode 100644 index 00000000..f266d0bd --- /dev/null +++ b/components/UI/iconsComponents/icons/shareIcon.tsx @@ -0,0 +1,25 @@ +import React, { FunctionComponent } from "react"; + +const ShareIcon: FunctionComponent = ({ color, width }) => { + return ( + <> + + + + + + + ); +}; + +export default ShareIcon; diff --git a/components/UI/menus/bannerPopup.tsx b/components/UI/menus/bannerPopup.tsx new file mode 100644 index 00000000..7c24f753 --- /dev/null +++ b/components/UI/menus/bannerPopup.tsx @@ -0,0 +1,54 @@ +import React, { FunctionComponent } from "react"; +import styles from "../../../styles/components/popup.module.css"; +import Button from "../button"; +import CloseIcon from "../iconsComponents/icons/closeIcon"; +import { Modal } from "@mui/material"; + +type BannerPopupProps = { + title: string; + banner: string; + description: string; + buttonName: string; + onClick: () => void; + onClose?: () => void; +}; + +const BannerPopup: FunctionComponent = ({ + title, + banner, + description, + buttonName, + onClick, + onClose, +}) => { + return ( + +
+
+

{title}

+ banner +
+
+ +

{description}

+
+ +
+
+ {onClose && ( + + )} +
+
+ ); +}; + +export default BannerPopup; diff --git a/components/UI/menus/popup.tsx b/components/UI/menus/popup.tsx index e579c30b..86e8cf4e 100644 --- a/components/UI/menus/popup.tsx +++ b/components/UI/menus/popup.tsx @@ -1,46 +1,39 @@ -import React, { FunctionComponent } from "react"; +import React, { FunctionComponent, ReactNode } from "react"; import styles from "../../../styles/components/popup.module.css"; -import Button from "../button"; import CloseIcon from "../iconsComponents/icons/closeIcon"; +import { Modal } from "@mui/material"; type PopupProps = { - title: string; - banner: string; - description: string; - buttonName: string; - onClick: () => void; + children: ReactNode; + bottomContent?: ReactNode; onClose?: () => void; }; const Popup: FunctionComponent = ({ - title, - banner, - description, - buttonName, - onClick, + children, + bottomContent = null, onClose, }) => { return ( -
+
-
-

{title}

- banner -
-
- -

{description}

-
- -
-
+
{children}
+ {bottomContent && ( +
{bottomContent}
+ )} {onClose && ( )}
-
+ ); }; diff --git a/components/UI/menus/sharePopup.tsx b/components/UI/menus/sharePopup.tsx new file mode 100644 index 00000000..3b8bd7cb --- /dev/null +++ b/components/UI/menus/sharePopup.tsx @@ -0,0 +1,70 @@ +import React, { FunctionComponent } from "react"; +import Popup from "./popup"; +import TwitterIcon from "../iconsComponents/icons/twitterIcon"; +import DiscordIcon from "../iconsComponents/icons/discordIcon"; +import iconsStyles from "../../../styles/components/icons.module.css"; +import Button from "../button"; +import styles from "../../../styles/components/popup.module.css"; + +type SharePopupProps = { + close: () => void; + toCopy: string; +}; + +const SharePopup: FunctionComponent = ({ close, toCopy }) => { + const [copied, setCopied] = React.useState(false); + const handleCopy = () => { + navigator.clipboard.writeText(toCopy); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }; + + return ( + + +
+ +
+ + } + > + <> +

Share

+ + +
+ ); +}; + +export default SharePopup; diff --git a/components/UI/profileCard.tsx b/components/UI/profileCard.tsx deleted file mode 100644 index 2e03aded..00000000 --- a/components/UI/profileCard.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React, { FunctionComponent } from "react"; -import styles from "../../styles/profile.module.css"; - -const ProfileCard: FunctionComponent = ({ - title, - content, - isUppercase = false, -}) => { - return ( -
-

- {title} -

-
- {content} -
- ); -}; - -export default ProfileCard; diff --git a/components/UI/profileCards.tsx b/components/UI/profileCards.tsx new file mode 100644 index 00000000..2dd00da5 --- /dev/null +++ b/components/UI/profileCards.tsx @@ -0,0 +1,163 @@ +import React, { FunctionComponent, useState } from "react"; +import styles from "../../styles/profile.module.css"; +import { Tooltip } from "@mui/material"; +import CopyIcon from "./iconsComponents/icons/copyIcon"; +import VerifiedIcon from "./iconsComponents/icons/verifiedIcon"; +import { minifyAddressFromStrings } from "../../utils/stringService"; +import SocialMediaActions from "./actions/socialmediaActions"; +import ShareIcon from "./iconsComponents/icons/shareIcon"; +import SharePopup from "./menus/sharePopup"; +import theme from "../../styles/theme"; + +const ProfileCards: FunctionComponent = ({ + title, + identity, + addressOrDomain, + sinceDate, + achievements, + soloBuildings, +}) => { + const [showSharePopup, setShowSharePopup] = useState(false); + const [selectedTab, setSelectedTab] = useState("nfts"); + const [copied, setCopied] = useState(false); + + const copyToClipboard = () => { + setCopied(true); + navigator.clipboard.writeText(identity?.addr as string); + setTimeout(() => { + setCopied(false); + }, 1500); + }; + + return ( + <> +
+

{title}

+
+ <> +
+
+ starknet.id avatar + {/* We do not enable the profile pic change atm */} + {/* +
+ console.log("changing pfp")} + /> +
+
*/} +
+
+
+
+ {!copied ? ( + +
copyToClipboard()}> + +
+
+ ) : ( + + )} +
+
+ {typeof addressOrDomain === "string" && + minifyAddressFromStrings( + [addressOrDomain, identity?.addr || ""], + 8 + )} +
+
+ {sinceDate ? ( +
+

{sinceDate}

+
+ ) : null} +
+
+ <> +
+
+ + +
+ + +
+
+

Progress

+
+ <> +
+
setSelectedTab("nfts")} + > + NFTs unlocked ({soloBuildings.length}) +
+
setSelectedTab("achievements")} + > + Achievements ({achievements.length}) +
+
+ {selectedTab === "nfts" ? ( +
+ {soloBuildings.map((building) => { + return ( +
+ +

{building.name}

+
+ ); + })} +
+ ) : null} + {selectedTab === "achievements" ? ( +
+ {achievements.map((achievement) => { + return ( +
+ +

+ Level {achievement.level} +

+

{achievement.name}

+
+ ); + })} +
+ ) : null} + +
+ {showSharePopup ? ( + setShowSharePopup(false)} + toCopy={window.location.href} + /> + ) : null} + + ); +}; + +export default ProfileCards; diff --git a/pages/[addressOrDomain].tsx b/pages/[addressOrDomain].tsx index f5180c7f..b0e663b6 100644 --- a/pages/[addressOrDomain].tsx +++ b/pages/[addressOrDomain].tsx @@ -2,21 +2,16 @@ import React, { useContext, useEffect, useLayoutEffect, useState } from "react"; import type { NextPage } from "next"; import styles from "../styles/profile.module.css"; import { useRouter } from "next/router"; -import { isHexString, minifyAddressWithChars } from "../utils/stringService"; +import { isHexString } from "../utils/stringService"; import { Connector, useAccount, useConnectors } from "@starknet-react/core"; -import SocialMediaActions from "../components/UI/actions/socialmediaActions"; import { StarknetIdJsContext } from "../context/StarknetIdJsProvider"; -import { Tooltip } from "@mui/material"; import { hexToDecimal } from "../utils/feltService"; import { minifyAddress } from "../utils/stringService"; import { utils } from "starknetid.js"; import ErrorScreen from "../components/UI/screens/errorScreen"; -import ProfileCard from "../components/UI/profileCard"; import { Land } from "../components/lands/land"; -import { hasVerifiedSocials } from "../utils/profile"; import { useMediaQuery } from "@mui/material"; -import VerifiedIcon from "../components/UI/iconsComponents/icons/verifiedIcon"; -import CopyIcon from "../components/UI/iconsComponents/icons/copyIcon"; +import ProfileCards from "../components/UI/profileCards"; import useCreationDate from "../hooks/useCreationDate"; const AddressOrDomain: NextPage = () => { @@ -28,13 +23,11 @@ const AddressOrDomain: NextPage = () => { const [initProfile, setInitProfile] = useState(false); const [identity, setIdentity] = useState(); const [notFound, setNotFound] = useState(false); - const [copied, setCopied] = useState(false); const [isOwner, setIsOwner] = useState(false); const dynamicRoute = useRouter().asPath; const isMobile = useMediaQuery("(max-width:768px)"); const [achievements, setAchievements] = useState([]); const [soloBuildings, setSoloBuildings] = useState([]); - const [selectedTab, setSelectedTab] = useState("nfts"); const sinceDate = useCreationDate(identity); useEffect(() => setNotFound(false), [dynamicRoute]); @@ -194,14 +187,6 @@ const AddressOrDomain: NextPage = () => { } }, [addressOrDomain, address, dynamicRoute]); - const copyToClipboard = () => { - setCopied(true); - navigator.clipboard.writeText(identity?.addr as string); - setTimeout(() => { - setCopied(false); - }, 1500); - }; - if (notFound) { return ( { setSoloBuildings={setSoloBuildings} />
- -
- starknet.id avatar - {/* We do not enable the profile pic change atm */} - {/* -
- console.log("changing pfp")} - /> -
-
*/} -
-
-
-
- {!copied ? ( - -
copyToClipboard()}> - -
-
- ) : ( - - )} -
-
- {typeof addressOrDomain === "string" && - isHexString(addressOrDomain) - ? minifyAddressWithChars(addressOrDomain, 8) - : minifyAddressWithChars(identity?.addr, 8)} -
-
- {sinceDate ? ( -
-

{sinceDate}

-
- ) : null} -
-
- } - /> - {hasVerifiedSocials(identity) ? ( - } - /> - ) : null} - -
-
setSelectedTab("nfts")} - > - NFTs unlocked ({soloBuildings.length}) -
-
setSelectedTab("achievements")} - > - Achievements ({achievements.length}) -
-
- {selectedTab === "nfts" ? ( -
- {soloBuildings.map((building) => { - return ( -
- -

{building.name}

-
- ); - })} -
- ) : null} - {selectedTab === "achievements" ? ( -
- {achievements.map((achievement) => { - return ( -
- -

- Level {achievement.level} -

-

- {achievement.name} -

-
- ); - })} -
- ) : null} - - } +
diff --git a/pages/quest/[questPage].tsx b/pages/quest/[questPage].tsx index e960dd0c..646bde92 100644 --- a/pages/quest/[questPage].tsx +++ b/pages/quest/[questPage].tsx @@ -13,7 +13,7 @@ import useHasRootDomain from "../../hooks/useHasRootDomain"; import useHasRootOrBraavosDomain from "../../hooks/useHasRootOrBraavosDomain"; import { useAccount } from "@starknet-react/core"; import { starknetIdAppLink } from "../../utils/links"; -import Popup from "../../components/UI/menus/popup"; +import BannerPopup from "../../components/UI/menus/bannerPopup"; const QuestPage: NextPage = () => { const router = useRouter(); @@ -79,7 +79,7 @@ const QuestPage: NextPage = () => { ) : (
{showDomainPopup && ( - { ) && utils.isStarkDomain(randomString + "." + randomString2 + "..stark") && utils.isStarkDomain(randomString + "." + randomString2 + "..stark") && - utils.isStarkDomain("." + randomString + ".." + randomString2 + ".stark") && + utils.isStarkDomain( + "." + randomString + ".." + randomString2 + ".stark" + ) && utils.isStarkDomain("." + randomString + "." + randomString2 + ".stark") ).toBeFalsy(); }); @@ -260,23 +263,40 @@ describe("Should test shortenDomain function", () => { }); }); -describe('minifyAddressWithChars function', () => { - it('should return empty string if address is undefined', () => { - expect(minifyAddressWithChars(undefined, 4)).toBe(''); +describe("minifyAddressWithChars function", () => { + it("should return empty string if address is undefined", () => { + expect(minifyAddressWithChars(undefined, 4)).toBe(""); + }); + + it("should return minified address with specified number of characters at the start and end", () => { + const address = "1234567890abcdef"; + expect(minifyAddressWithChars(address, 4)).toBe("1234...cdef"); + }); + + it("should return the original address if number of characters is equal to half the length of the address", () => { + const address = "12345678"; + expect(minifyAddressWithChars(address, 4)).toBe("12345678"); }); - it('should return minified address with specified number of characters at the start and end', () => { - const address = '1234567890abcdef'; - expect(minifyAddressWithChars(address, 4)).toBe('1234...cdef'); + it("should return minified address in lowercase", () => { + const address = "ABCDEFGH"; + expect(minifyAddressWithChars(address, 3)).toBe("abc...fgh"); + }); +}); + +describe("minifyAddressFromStrings", () => { + it("Should returns an empty string if the string array is empty", () => { + const result = minifyAddressFromStrings([], 4); + expect(result).toEqual(""); }); - it('should return the original address if number of characters is equal to half the length of the address', () => { - const address = '12345678'; - expect(minifyAddressWithChars(address, 4)).toBe('12345678'); + it("Should returns the minified version of the first string corresponding to a valid address in the array", () => { + const result = minifyAddressFromStrings(["0x1234567890abcdef", "test"], 4); + expect(result).toEqual("0x12...cdef"); }); - it('should return minified address in lowercase', () => { - const address = 'ABCDEFGH'; - expect(minifyAddressWithChars(address, 3)).toBe('abc...fgh'); + it("Should returns the minified version of the first string corresponding to a valid address in the array", () => { + const result = minifyAddressFromStrings(["test2", "0x1234567890abcdef"], 4); + expect(result).toEqual("0x12...cdef"); }); }); diff --git a/types/frontTypes.d.ts b/types/frontTypes.d.ts index ff2de17e..79e9e846 100644 --- a/types/frontTypes.d.ts +++ b/types/frontTypes.d.ts @@ -142,8 +142,11 @@ type SquareStyle = "bottomRight" | "bottomLeft" | "topRight" | "topLeft"; type ProfileCard = { title: string; - isUppercase?: boolean; - content: React.ReactNode; + identity: Identity; + addressOrDomain: string | string[] | undefined; + sinceDate: string | null; + achievements: BuildingsInfo[]; + soloBuildings: BuildingsInfo[]; }; type UserAchievement = { diff --git a/utils/stringService.ts b/utils/stringService.ts index 11b01a6b..c945ea17 100644 --- a/utils/stringService.ts +++ b/utils/stringService.ts @@ -89,3 +89,16 @@ export function numberToString(element: number | undefined): string { export function convertNumberToFixedLengthString(number: string): string { return number.padStart(12, "0"); } + +export function minifyAddressFromStrings( + strings: string[], + chars: number +): string { + for (let index = 0; index < strings.length; index++) { + const string = strings[index]; + if (string && isHexString(string)) { + return minifyAddressWithChars(string, chars); + } + } + return ""; +}