diff --git a/apps/connect/package-lock.json b/apps/connect/package-lock.json index 96c5ce470..70a01ca07 100644 --- a/apps/connect/package-lock.json +++ b/apps/connect/package-lock.json @@ -28,7 +28,8 @@ "dompurify": "^3.0.6", "mixpanel-browser": "^2.49.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.24.0" }, "devDependencies": { "@types/dompurify": "^3.0.5", @@ -9174,6 +9175,14 @@ } } }, + "node_modules/@remix-run/router": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.17.0.tgz", + "integrity": "sha512-2D6XaHEVvkCn682XBnipbJjgZUU7xjLtA4dGJRBVUKpEaDYOZMENZoZjAOSb7qirxt5RupjzZxz4fK2FO+EFPw==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.14.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz", @@ -25231,6 +25240,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.24.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.24.0.tgz", + "integrity": "sha512-sQrgJ5bXk7vbcC4BxQxeNa5UmboFm35we1AFK0VvQaz9g0LzxEIuLOhHIoZ8rnu9BO21ishGeL9no1WB76W/eg==", + "dependencies": { + "@remix-run/router": "1.17.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.24.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.24.0.tgz", + "integrity": "sha512-960sKuau6/yEwS8e+NVEidYQb1hNjAYM327gjEyXlc6r3Skf2vtwuJ2l7lssdegD2YjoKG5l8MsVyeTDlVeY8g==", + "dependencies": { + "@remix-run/router": "1.17.0", + "react-router": "6.24.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", diff --git a/apps/connect/package.json b/apps/connect/package.json index 0c4e8d296..3742a12d8 100644 --- a/apps/connect/package.json +++ b/apps/connect/package.json @@ -35,7 +35,8 @@ "dompurify": "^3.0.6", "mixpanel-browser": "^2.49.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.24.0" }, "devDependencies": { "@types/dompurify": "^3.0.5", diff --git a/apps/connect/src/App.tsx b/apps/connect/src/App.tsx index 0ea2918d5..3de1c4bb2 100644 --- a/apps/connect/src/App.tsx +++ b/apps/connect/src/App.tsx @@ -11,11 +11,14 @@ import { useQueryParams } from "./hooks/useQueryParams"; import { useFormatAssetParam } from "./hooks/useFormatAssetParam"; import WormholeConnect from "@wormhole-foundation/wormhole-connect"; import { eventHandler } from "./providers/telemetry"; +import { useRoutes } from "react-router-dom"; +import PrivacyPolicy from "./components/pages/PrivacyPolicy"; +import { PrivacyPolicyPath, isPreview } from "./utils/constants"; +import Banner from "./components/atoms/Banner"; const defaultConfig: WormholeConnectConfig = { ...wormholeConnectConfig, - ...((window.location.origin.includes("preview") || - window.location.origin.includes("testnet")) && { + ...(isPreview && { eventHandler: eventHandler, }), }; @@ -47,6 +50,17 @@ export default function Root() { useEffect(() => { localStorage.setItem("Connect Config", JSON.stringify(config, null, 2)); }, [config]); + + const Connect = ( + <> + + + + ); + const routes = useRoutes([ + { path: `/`, element: Connect }, + { path: PrivacyPolicyPath, element: }, + ]); return ( <> {versions.map(({ appName, version }, idx) => ( @@ -60,7 +74,7 @@ export default function Root() { - + {routes} ); } diff --git a/apps/connect/src/assets/imgs/arrow.svg b/apps/connect/src/assets/imgs/arrow.svg new file mode 100644 index 000000000..b7d03bcfe --- /dev/null +++ b/apps/connect/src/assets/imgs/arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/connect/src/assets/imgs/cookie.svg b/apps/connect/src/assets/imgs/cookie.svg new file mode 100644 index 000000000..89cc0bf80 --- /dev/null +++ b/apps/connect/src/assets/imgs/cookie.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/connect/src/components/atoms/Banner.tsx b/apps/connect/src/components/atoms/Banner.tsx new file mode 100644 index 000000000..059f58467 --- /dev/null +++ b/apps/connect/src/components/atoms/Banner.tsx @@ -0,0 +1,95 @@ +import { Button, styled } from "@mui/material"; +import { useState } from "react"; +import { PrivacyPolicyPath } from "../../utils/constants"; +import cookie from "../../assets/imgs/cookie.svg"; +import { Link } from "../../utils/styles"; +import { Link as RouterLink } from "react-router-dom"; + +const BannerContainer = styled("div")(({ theme }) => ({ + display: "flex", + justifyContent: "center", + alignItems: "center", + position: "fixed", + bottom: 60, + left: 30, + right: 30, + zIndex: 100, + [theme.breakpoints.down("sm")]: { + bottom: 75, + }, +})); + +const Content = styled("div")(({ theme }) => ({ + display: "flex", + justifyContent: "center", + alignItems: "center", + position: "fixed", + padding: 10, + backgroundColor: "#FFFFFF14", + gap: 20, + borderRadius: 12, + margin: 26, + mixBlendMode: "normal", + backdropFilter: "blur(60px)", + [theme.breakpoints.down("sm")]: { + margin: 5, + borderRadius: 0, + }, +})); + +const CloseButton = styled(Button)(() => ({ + color: "#070528", + backgroundColor: "white", + borderRadius: 6, + fontSize: 16, + maxHeight: 28, + minWidth: 67, + textTransform: "capitalize", + "&:hover": { + color: "white", + }, +})); +const Banner = () => { + const [showBanner, setShowBanner] = useState(true); + + const isNewOrExpired = () => { + const cache = localStorage.getItem("showPrivacyPolicy"); + const expirationDate = + new Date(Number(cache)).getTime() + 7 * 24 * 60 * 60 * 1000; + const today = new Date().getTime(); + if (!cache || expirationDate < today) { + return true; + } + return false; + }; + const handleClose = () => { + setShowBanner(false); + if (isNewOrExpired()) { + const today = new Date(); + localStorage.setItem("showPrivacyPolicy", today.getTime().toString()); + } + }; + + if (!isNewOrExpired() || !showBanner) { + return null; + } + return ( + + + cookie +
+ This website is designed to enhance your experience. By continuing to + use this site, you consent to our{" "} + + Privacy Policy + +
+ handleClose()}> + Close + +
+
+ ); +}; + +export default Banner; diff --git a/apps/connect/src/components/atoms/TableOfContent.tsx b/apps/connect/src/components/atoms/TableOfContent.tsx new file mode 100644 index 000000000..40d7043ab --- /dev/null +++ b/apps/connect/src/components/atoms/TableOfContent.tsx @@ -0,0 +1,88 @@ +import { styled } from "@mui/material"; +import { useEffect, useState } from "react"; + +const TableContainer = styled("div")(({ theme }) => ({ + borderLeft: "1px solid #FFFFFF1A", + width: 320, + [theme.breakpoints.down("md")]: { + width: "100%", + }, +})); + +const TableItem = styled("div")(() => ({ + paddingLeft: 20, + marginBottom: 10, + marginTop: 10, + cursor: "pointer", + textTransform: "capitalize", + fontSize: 14, + color: "#9C9DBF", + fontWeight: 500, + marginLeft: -2, + "&:hover": { + color: "white", + borderLeft: "3px solid white", + fontWeight: 600, + }, +})); + +const Title = styled("p")(() => ({ + color: "#9C9DBF", + fontSize: 14, + fontWeight: 500, +})); + +interface TableItem { + title: string | null; + active: boolean; + id: string; +} +export default function TableOfContent() { + const [tableOfContent, setTableOfContent] = useState([]); + const activeItem = { + fontWeight: 600, + color: "white", + borderLeft: "3px solid white", + }; + useEffect(() => { + console.log("TableOfContent", document.querySelectorAll("H2")); + const titles = Array.from(document.querySelectorAll("H2")) + .filter((el) => !!el.children[0].textContent) + .map((el) => { + return { title: el.children[0].textContent, active: false, id: el.id }; + }); + setTableOfContent(titles); + }, []); + const updateActive = (idx: number) => { + const newTableOfContent = tableOfContent.map((item, index) => { + if (index === idx) { + item.active = true; + setTimeout(() => { + //document.querySelector(`#${item.id}`)?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' }); + const el = document.querySelector(`#${item.id}`) as HTMLElement; + window.scroll({ top: el?.offsetTop || 0, behavior: "smooth" }); + }, 100); + } else { + item.active = false; + } + return item; + }); + setTableOfContent(newTableOfContent); + }; + return ( +
+ TABLE OF CONTENTS + + {tableOfContent.map((item, idx) => ( + updateActive(idx)} + > + {item.title} + + ))} + +
+ ); +} diff --git a/apps/connect/src/components/pages/PrivacyPolicy.tsx b/apps/connect/src/components/pages/PrivacyPolicy.tsx new file mode 100644 index 000000000..9c559d692 --- /dev/null +++ b/apps/connect/src/components/pages/PrivacyPolicy.tsx @@ -0,0 +1,610 @@ +import { Button, Container as MuiContainer, styled } from "@mui/material"; +import arrow from "../../assets/imgs/arrow.svg"; +import { useLocation, useNavigate } from "react-router-dom"; +import { Link } from "../../utils/styles"; +import TableOfContent from "../atoms/TableOfContent"; + +const TitleItem = styled("li")(() => ({ + paddingLeft: 25, +})); + +const P = styled("p")(() => ({ + fontSize: 14, + lineHeight: "20px", +})); + +const H1 = styled("h1")(() => ({ + fontSize: 24, + lineHeight: "24px", + fontWeight: 700, + color: "#FFFFFF", +})); + +const H2 = styled("h2")(() => ({ + fontSize: 20, + lineHeight: "24px", + fontWeight: 700, + color: "#FFFFFF", +})); + +const H3 = styled("h3")(() => ({ + fontSize: 17, + lineHeight: "20px", + fontWeight: 700, +})); + +const ContactButton = styled(Button)(() => ({ + fontSize: 16, + lineHeight: "24px", + fontWeight: 400, + textTransform: "lowercase", + borderRadius: 8, + padding: "8px 16px", + backgroundColor: "#272745", +})); + +const TitleContainer = styled("div")(() => ({ + display: "flex", + flexDirection: "row", + alignItems: "center", + gap: 20, +})); + +const GoBackButton = styled(Button)(() => ({ + minWidth: 24, + height: 24, +})); + +const ContentContainer = styled(MuiContainer)(() => ({ + color: "#9C9DBF", + maxWidth: 1800, +})); + +const Container = styled("div")(({ theme }) => ({ + display: "flex", + gap: 80, + justifyContent: "center", + margin: "auto", + padding: 40, + [theme.breakpoints.down("md")]: { + flexDirection: "column-reverse", + width: "100%", + }, +})); + +const UL = styled("ul")(() => ({ + fontSize: 14, + lineHeight: "20px", +})); + +const PrivacyPolicy = () => { + const navigate = useNavigate(); + const { state } = useLocation(); + const from = state?.from || "/"; + + return ( + + + + navigate(from)}> + arrow + +

Privacy Policies

+
+ {/* TODO: Update the date when we doo the release... Or the preview? */} +

+ Last Updated: May 17, 2024 +

+

+ xLabs (the “Company,” “we,” “us,” or “our + ”) is committed to protecting your privacy. It is our policy to + respect your privacy and comply with any applicable laws and + regulations regarding any personal information we may collect about + you, including across our website, portalbridge.com, and other sites + we own and operate (collectively, the “Platform”). This Privacy + Policy (the "Policy") outlines the data collection, processing, + and management practices of the Company in relation to services + provided on the Platform (the “Services”). By using our + Platform, you agree to the collection, use, and disclosure of your + personal information in accordance with this Policy. +

+

+ In the event our Platform contains links to third-party sites and + services, please be aware that those sites and services have their own + privacy policies. After following a link to any third-party content, + you should read their posted privacy policy information about how they + collect and use personal information. This Privacy Policy does not + apply to any of your activities after you leave our Platform. +

+
    +

    + INFORMATION WE COLLECT +

    +
      +

      + + Information About You That You Share With Us Directly + +

      +
      +

      + When you use Services on the Platform you may give us personal + information directly (for example, details you connect your + wallet to the Platform), and we will store that personal + information on our systems and process it for the purposes + described in this Privacy Policy. +

      +

      + Depending on the Service, the personal information we collect + will be relevant to providing that Service and include some or + all of the following non-personally identifiable data: +

      +
        +
      • Your public blockchain wallet address;
      • +
      • Amount of any asset involved in a transaction; and
      • +
      • + Any additional information required for verification or + participation in the Service offered. +
      • +
      +

      + We may also collect personal information from you when you + communicate with us, such as your name, email address, and any + other information you voluntarily provide. +

      +
      +

      + Information You Generate Using Our Services +

      +
      +

      + Information you generate while using our Services may be + retained to protect the safety and well-being of our users; to + protect our rights and property in connection with our Services; + to conduct research; to operate, improve, personalize, and + optimize our Services and our users’ experiences, including + through the use of analytics; and to manage and deliver + advertising. Where required by law, we will seek your consent + for this. +

      +
      +

      + + Cookies and Other Automated Information Collection + +

      +
      +

      + We use cookies and other similar technologies (e.g., beacons, + pixel tags, clear gifs, and device identifiers). We, our service + providers, and our business partners use these cookies and other + similar technologies to process information, which may include: +

      +
        +
      • IP address;
      • +
      • the type of computer or mobile device you are using;
      • +
      • platform type (like Apple iOS or Android);
      • +
      • your operating system version;
      • +
      • the screen resolution of your monitor(s);
      • +
      • + your mobile device’s identifiers, like your MAC Address, Apple + Identifier For Advertising (IDFA), and/or Android Advertising + ID (AAID); +
      • +
      • application performance and de-bugging information;
      • +
      • your browser type and language;
      • +
      • referring and exit pages, and URLs;
      • +
      • the number of clicks on an app feature or web page;
      • +
      • the amount of time spent on an app feature or web page;
      • +
      • domain names;
      • +
      • landing pages;
      • +
      • pages viewed and the order of those pages; and/or
      • +
      • + what your current progress is in our Services and the date and + time of activity. +
      • +
      +
      +
    + +

    + + PROCESSING AND USE OF YOUR PERSONAL INFORMATION + +

    +
    +

    + We only collect and use your personal information when we have a + legitimate reason for doing so. Most commonly, we will use your + personal data in the following circumstances: +

    +
      +
    • + Provide our Services and create accounts in those Services +
    • +
    • + Monitor and maintain the security and integrity of our Platform +
    • +
    • + Improve, optimize and personalize our Services and our user’s + experiences +
    • +
    • Communicate with you about the Services you are using
    • +
    • + Customer service and managing user communications, including + technical support +
    • +
    • Protect the safety and well-being of our users and others
    • +
    • + Where you provide sensitive or special category personal + information to us +
    • +
    • + Maintain our business operations, including any business + transition, like a merger, acquisition by another company, or + sale of all or part of our assets +
    • +
    • + For security purposes and to prevent fraud or potentially + illegal activities, and to enforce the applicable Terms of + Service +
    • +
    • + Cooperate with public authorities and law enforcement where + lawfully permitted or required +
    • +
    • + Protect our rights, including compliance with applicable legal + obligations, resolving any disputes we may have, and to + administer our agreements with third parties +
    • +
    +
    + +

    + + DISCLOSURE OF PERSONAL INFORMATION TO THIRD PARTIES + +

    +
    +

    + We share your personal data with our third-party service + providers, agents, subcontractors and other associated + organizations, our group companies, and affiliates (as described + below) in order to complete tasks and provide the Platform and + Services to you on our behalf. When using third party service + providers, they are required to respect the security of your + personal data and to treat it in accordance with the law. +

    +

    We may disclose personal information to:

    +
      +
    • a parent, subsidiary, or affiliate of the Company
    • +
    • + third-party service providers for the purpose of enabling them + to provide their services, including (without limitation) IT + service providers, data storage, hosting and server providers, + analytics, error loggers, debt collectors, maintenance or + problem-solving providers, professional advisors, and payment + systems operators +
    • +
    • our employees, contractors, and/or related entities
    • +
    • our existing or potential agents or business partners
    • +
    • + credit reporting agencies, courts, tribunals, and regulatory + authorities, in the event you fail to pay for goods or services + we have provided to you +
    • +
    • + courts, tribunals, regulatory authorities, and law enforcement + officers, as required by law, in connection with any actual or + prospective legal proceedings, or in order to establish, + exercise, or defend our legal rights +
    • +
    • + third parties, including agents or sub-contractors, who assist + us in providing information, products, or services +
    • +
    • third parties to collect and process data
    • +
    • + an entity that buys, or to which we transfer all or + substantially all of our assets and business +
    • +
    +
    +

    + SECURITY OF YOUR PERSONAL INFORMATION +

    +
    +

    + We implement reasonable and appropriate security measures to help + protect the security of your information both online and offline + and to ensure that your data is treated securely and in accordance + with this Privacy Policy. These measures vary based upon the + sensitivity of your information. It is important that you protect + and maintain the security of your personal information, including + the secure storage of any keys or other wallet data used to access + your crypto assets. +

    +

    + While we take precautions against possible security breaches of + our Services and our customer databases and records, no website or + Internet transmission is completely secure. We cannot guarantee + that unauthorized access, hacking, data loss, or other breaches + will never occur, and we cannot guarantee the security of your + information while it is being transmitted to our Service. Any + transmission is at your own risk. +

    +
    + +

    + HOW LONG WE KEEP YOUR PERSONAL INFORMATION +

    +
    +

    + How long we retain your personal information depends on why we + collected it and how we use it, but we will not retain your + personal information for longer than is necessary to provide you + with the Services or for our legal requirements. +

    +

    + We will retain personal information that is connected with your + account and/or the Services you use from us for as long as you are + actively using Services on the Platform. However, you acknowledge + that we may retain some information after you have ceased to use + the Platform’s Services where necessary to enable us to meet our + legal obligations or to exercise, defend, or establish our rights. +

    +
    + +

    + + YOUR RIGHTS AND CONTROLLING YOUR PERSONAL INFORMATION + +

    +
    +

    + Your choice: By providing personal information to us, you + understand we will collect, hold, use, and disclose your personal + information in accordance with this Privacy Policy. You may choose + to reject cookies and certain other tracking technologies by + changing the setting of your browser. You do not have to provide + personal information to us, however, if you do not, it may affect + your use of our Platform or the products and/or services offered + on or through it. You may also request details of the personal + information that we hold about you. +

    +

    + Information from third parties: If we receive personal + information about you from a third party, we will protect it as + set out in this Privacy Policy. If you are a third party providing + personal information about somebody else, you represent and + warrant that you have such person’s consent to provide the + personal information to us. +

    +

    + Marketing permission and Unsubscribing: If you have + previously agreed to us using your personal information for direct + marketing purposes, you may change your mind at any time by opting + out of the promotional communications or contacting us using the + details below. To unsubscribe from our email database or opt-out + of communications (including marketing communications), please + contact us using the details provided in this privacy policy or + opt-out using the opt-out facilities provided in the + communication. We may need to request specific information from + you to help us confirm your identity. +

    +

    + Correction: If you believe that any information we hold + about you is inaccurate, out of date, incomplete, irrelevant, or + misleading, please contact us using the details provided in this + privacy policy. We will take reasonable steps to correct any + information found to be inaccurate, incomplete, misleading, or out + of date. +

    +

    + Non-discrimination: We will not discriminate against you + for exercising any of your rights over your personal information. + Unless your personal information is required to provide you with a + particular service or offer (for example providing user support), + we will not deny you goods or services and/or charge you different + prices or rates for goods or services, including through granting + discounts or other benefits, or imposing penalties, or provide you + with a different level or quality of goods or services. +

    +

    + Notification of data breaches: We will comply with laws + applicable to us in respect of any data breach. +

    +

    + Complaints: If you believe that we have breached a relevant + data protection law and wish to make a complaint, please contact + us using the details below and provide us with full details of the + alleged breach. We will promptly investigate your complaint and + respond to you, in writing, setting out the outcome of our + investigation and the steps we will take to deal with your + complaint. You also have the right to contact a regulatory body or + data protection authority in relation to your complaint. +

    +

    + No fee usually required: You will not have to pay a fee to + access your personal data (or to exercise any of the other + rights). However, we may charge a reasonable fee if your request + is manifestly unfounded or excessive. Alternatively, we could + refuse to comply with your request in these circumstances. We may + need to request specific information from you to help us confirm + your identity and ensure your right to access your personal data + (or to exercise any of your other rights). This is a security + measure to ensure that personal data is not disclosed to any + person who has no right to receive it. We may also contact you to + ask you for further information in relation to your request to + speed up our response. +

    +

    + Period for replying to a legitimate request: We try to + respond to all legitimate requests within one month. Occasionally + it may take us longer than a month if your request is particularly + complex or you have made a number of requests. In this case, we + will notify you and keep you updated. +
    + Notwithstanding the above, we cannot edit or delete any + information that is stored on a blockchain as we do not have + custody or control over any blockchains. The information stored on + the blockchain may include purchases, sales, and transfers related + to any blockchain address associated with you. +

    +
    + +

    + CHILDREN’S PRIVACY +

    +
    +

    + The Platform is not directed to children under 18 (or other age as + required by local law), and we do not knowingly collect personal + information from children. If you are a parent or guardian and + believe your child has uploaded personal information to our + Platform without your consent, you may contact us. If we become + aware that a child has provided us with personal information in + violation of applicable law, we will delete any personal + information we have collected, unless we have a legal obligation + to keep it, and terminate the child’s account if applicable. +

    +
    + +

    + BUSINESS TRANSFERS +

    +
    +

    + If we or our assets are acquired, or in the unlikely event that we + go out of business or enter bankruptcy, we would include data, + including your personal information, among the assets transferred + to any parties who acquire us. You acknowledge that such transfers + may occur, and that any parties who acquire us may, to the extent + permitted by applicable law, continue to use your personal + information according to this Privacy Policy, which they will be + required to assume as it is the basis for any ownership or use + rights we have over such information. +

    +
    + +

    + INTERNATIONAL TRANSFERS +

    +
    +

    + All information processed by us may be transferred, processed, and + stored anywhere in the world, including, but not limited to, the + United States, members of the European Union, or other countries, + which may have data protection laws that are different from the + laws where you live. We endeavor to safeguard your information + consistent with the requirements of applicable laws. +

    +
    + +

    + LIMITS OF OUR POLICY +

    +
    +

    + Our Platform may link to external sites that are not operated by + us. Please be aware that we have no control over the content and + policies of those sites and cannot accept responsibility or + liability for their respective privacy practices. +

    +
    + +

    + CHANGES TO THIS POLICY +

    +
    +

    + At our discretion, we may change this Privacy Policy to reflect + updates to our business processes, current acceptable practices, + or legislative or regulatory changes. If we decide to change this + Policy, we will post the changes here at the same link by which + you are accessing this Policy, and if the changes are significant, + or if required by applicable law, we will contact you (based on + your selected preferences for communications from us) with details + of the changes. +

    +

    + If required by law, we will get your permission or give you the + opportunity to opt in to or opt out of, as applicable, any new + uses of your personal information. +

    +
    + +

    + SUPPLEMENTAL NOTICE FOR CALIFORNIA RESIDENTS +

    +
    +

    + This Supplemental Notice for California Residents only applies to + our processing of personal information that is subject to the + California Consumer Privacy Act of 2018 (the “CCPA”). The + CCPA provides California residents with the right to know what + categories of personal information we have collected about them, + and whether we disclosed that personal information for a business + purpose (e.g., to a service provider) in the preceding 12 months. + California residents can find this information by visiting:{" "} + + https://www.oag.ca.gov/privacy/ccpa + + . +

    +
    + +

    + SUPPLEMENTAL NOTICE FOR NEVADA RESIDENTS +

    +
    +

    + If you are a resident of Nevada, you have the right to opt-out of + the sale of certain personal information to third parties who + intend to license or sell that personal information. You can + exercise this right by contacting us with the subject line “Nevada + Do Not Sell Request” and providing us with your name and the email + address associated with your account.{" "} +

    +
    + +

    + YOUR RIGHTS AS A DATA SUBJECT (GDPR) +

    +
    +

    + You have certain rights under applicable legislation, and in + particular under Regulation EU 2016/679 (General Data Protection + Regulation or ‘GDPR’). You can find out more about the GDPR and + your rights by accessing the European Commission’s website. +

    +
    +

    + + CONTACT US FOR QUESTIONS AND REPORTING VIOLATIONS + +

    +
    +

    + If you have any questions, concerns, or complaints about our + Privacy Policy or our data collection and processing practices, or + if you want to report any security violations to us, or exercise + any of your rights under this Privacy Policy, please contact us at + the following address: +

    +
    + + legal@xlabs.xyz + +
    +
    +
    +
+
+ +
+ ); +}; + +export default PrivacyPolicy; diff --git a/apps/connect/src/main.tsx b/apps/connect/src/main.tsx index 8e565790b..e65abcf48 100644 --- a/apps/connect/src/main.tsx +++ b/apps/connect/src/main.tsx @@ -7,6 +7,7 @@ import theme from "./theme/portal.ts"; import Background from "./components/atoms/Background.tsx"; import App from "./App.tsx"; import { OpenTelemetryContext, tracer } from "./providers/telemetry.ts"; +import { HashRouter } from "react-router-dom"; if (redirects && redirects?.source?.length > 0) { const matcher = new RegExp(redirects.source.join("|")); @@ -24,7 +25,9 @@ ReactDOM.createRoot(document.getElementById("root")!).render( - + + + diff --git a/apps/connect/src/utils/constants.ts b/apps/connect/src/utils/constants.ts new file mode 100644 index 000000000..0d6964bbd --- /dev/null +++ b/apps/connect/src/utils/constants.ts @@ -0,0 +1,5 @@ +export const PrivacyPolicyPath = "/privacy-policy"; + +export const isPreview = + window.location.origin.includes("preview") || + window.location.origin.includes("testnet"); diff --git a/apps/connect/src/utils/styles.ts b/apps/connect/src/utils/styles.ts new file mode 100644 index 000000000..06320b5d5 --- /dev/null +++ b/apps/connect/src/utils/styles.ts @@ -0,0 +1,8 @@ +import { styled, Link as MuiLink, LinkProps } from "@mui/material"; +import { LinkProps as RouterLinkProps } from "react-router-dom"; + +export const Link = styled(MuiLink)(() => ({ + color: "white", + textDecoration: "underline", + fontWeight: 500, +})); diff --git a/apps/connect/vite.config.ts b/apps/connect/vite.config.ts index e8274b3ed..55f8008a3 100644 --- a/apps/connect/vite.config.ts +++ b/apps/connect/vite.config.ts @@ -88,6 +88,10 @@ export default defineConfig({ label: 'Advanced Tools', href: `${process.env.PUBLIC_URL}/advanced-tools/`, order: 1 + }, + { + label: 'Privacy Policy', + href: `${process.env.PUBLIC_URL}/#/privacy-policy/`, } ] } diff --git a/apps/connect/vite.token-bridge.config.ts b/apps/connect/vite.token-bridge.config.ts index 539f13c33..90cb92c63 100644 --- a/apps/connect/vite.token-bridge.config.ts +++ b/apps/connect/vite.token-bridge.config.ts @@ -3,10 +3,10 @@ import viteConfig, { chains } from './vite.config' import type { WormholeConnectConfig } from '@wormhole-foundation/wormhole-connect' const PUBLIC_URL = viteConfig.base; - const ADVANCE_TOOLS_HREF = `${PUBLIC_URL}/advanced-tools/` const ADVANCE_TOOLS_HREF_TEMPLATE = `${ADVANCE_TOOLS_HREF}#/transfer?sourceChain={:sourceChain}&targetChain={:targetChain}` const USDC_BRIDGE_HREF = `${PUBLIC_URL}/usdc-bridge/` +const PRIVACY_POLICY_HREF = `${PUBLIC_URL}/#/privacy-policy/` const ALGORAND = { icon: "data:image/svg+xml,%3Csvg width='80' height='80' viewBox='0 0 80 80' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cmask id='mask0_5964_23764' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='0' y='0' width='80' height='80'%3E%3Crect width='80' height='80' fill='%23D9D9D9'/%3E%3C/mask%3E%3Cg mask='url(%23mask0_5964_23764)'%3E%3Cpath d='M17.1382 72.7516L27.3034 55.1578L37.4598 37.6258L47.5588 20.032L49.2361 17.2424L49.9732 20.032L53.0718 31.6141L49.6024 37.6258L39.4461 55.1578L29.3471 72.7516H41.4853L51.6461 55.1578L56.9118 46.0519L59.3925 55.1578L64.0977 72.7516H75L70.2948 55.1578L65.5852 37.6258L64.3493 33.1015L71.9059 20.032H60.8799L60.5048 18.7299L56.6647 4.35825L56.1703 2.5H45.577L45.3298 2.87077L35.4162 20.032L25.2554 37.6258L15.1608 55.1578L5 72.7516H17.1382Z' fill='white'/%3E%3C/g%3E%3C/svg%3E%0A", @@ -541,6 +541,7 @@ export default defineConfig({ { label: "Home", active: true, href: `${PUBLIC_URL}/` }, { label: "Staking", href: "https://www.tally.xyz/gov/wormhole", isBlank: true }, { label: "USDC", href: USDC_BRIDGE_HREF }, + { label: "Privacy Policy", href: PRIVACY_POLICY_HREF }, ], redirects: { source: [ diff --git a/apps/connect/vite.usdc-bridge.config.ts b/apps/connect/vite.usdc-bridge.config.ts index fe493fde2..154cbdd9a 100644 --- a/apps/connect/vite.usdc-bridge.config.ts +++ b/apps/connect/vite.usdc-bridge.config.ts @@ -15,6 +15,7 @@ export default defineConfig({ { label: "Home", href: `${PUBLIC_URL}/` }, { label: "Staking", href: "https://www.tally.xyz/gov/wormhole", isBlank: true }, { label: "USDC", active: true, href: `${PUBLIC_URL}/usdc-bridge` }, + { label: "Privacy Policy", href: `${PUBLIC_URL}/privacy-policy` }, ], wormholeConnectConfig: { ...viteConfig?.define?.wormholeConnectConfig,