diff --git a/api/functions/graphql/nexus-typegen.ts b/api/functions/graphql/nexus-typegen.ts index 44f453f..41db1ba 100644 --- a/api/functions/graphql/nexus-typegen.ts +++ b/api/functions/graphql/nexus-typegen.ts @@ -305,6 +305,7 @@ export interface NexusGenObjects { payment_request: string; // String! } WalletKey: { // root type + is_current: boolean; // Boolean! key: string; // String! name: string; // String! } @@ -611,6 +612,7 @@ export interface NexusGenFieldTypes { payment_request: string; // String! } WalletKey: { // field return type + is_current: boolean; // Boolean! key: string; // String! name: string; // String! } @@ -935,6 +937,7 @@ export interface NexusGenFieldTypeNames { payment_request: 'String' } WalletKey: { // field return type name + is_current: 'Boolean' key: 'String' name: 'String' } diff --git a/api/functions/graphql/schema.graphql b/api/functions/graphql/schema.graphql index 2944fcc..1e7b8cc 100644 --- a/api/functions/graphql/schema.graphql +++ b/api/functions/graphql/schema.graphql @@ -425,6 +425,7 @@ type Vote { } type WalletKey { + is_current: Boolean! key: String! name: String! } \ No newline at end of file diff --git a/api/functions/graphql/types/users.js b/api/functions/graphql/types/users.js index fb8a9df..b00c827 100644 --- a/api/functions/graphql/types/users.js +++ b/api/functions/graphql/types/users.js @@ -171,9 +171,9 @@ const MyProfile = objectType({ t.nonNull.list.nonNull.field('walletsKeys', { type: "WalletKey", - resolve: (parent) => { - return prisma.user.findUnique({ where: { id: parent.id } }).userKeys(); - + resolve: async (parent, _, context) => { + const userKeys = await prisma.user.findUnique({ where: { id: parent.id } }).userKeys(); + return userKeys.map(k => ({ ...k, is_current: k.key === context.userPubKey })) } }); } @@ -283,6 +283,7 @@ const WalletKey = objectType({ definition(t) { t.nonNull.string('key'); t.nonNull.string('name'); + t.nonNull.boolean('is_current') } }) diff --git a/api/functions/upload-image-url/upload-image-url.js b/api/functions/upload-image-url/upload-image-url.js index 1cdc496..b40682a 100644 --- a/api/functions/upload-image-url/upload-image-url.js +++ b/api/functions/upload-image-url/upload-image-url.js @@ -7,6 +7,9 @@ const { getDirectUploadUrl } = require('../../services/imageUpload.service') const { prisma } = require('../../prisma') const postUploadImageUrl = async (req, res) => { + + return res.status(404).send("This api is in progress"); + const userPubKey = await extractKeyFromCookie(req.headers.cookie ?? req.headers.Cookie) const user = await getUserByPubKey(userPubKey) diff --git a/package-lock.json b/package-lock.json index c56d075..c5bfa65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -79,6 +79,7 @@ "react-loader-spinner": "^6.0.0-0", "react-loading-skeleton": "^3.1.0", "react-modal": "^3.15.1", + "react-popper-tooltip": "^4.4.2", "react-query": "^3.35.0", "react-redux": "^8.0.0", "react-router-dom": "^6.3.0", @@ -2147,9 +2148,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", - "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", "dependencies": { "regenerator-runtime": "^0.13.4" }, @@ -10146,6 +10147,21 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/@storybook/components/node_modules/react-popper-tooltip": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz", + "integrity": "sha512-EnERAnnKRptQBJyaee5GJScWNUKQPDD2ywvzZyUjst/wj5U64C8/CnSYLNEmP2hG0IJ3ZhtDxE8oDN+KOyavXQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@popperjs/core": "^2.5.4", + "react-popper": "^2.2.4" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0", + "react-dom": "^16.6.0 || ^17.0.0" + } + }, "node_modules/@storybook/core": { "version": "6.4.22", "resolved": "https://registry.npmjs.org/@storybook/core/-/core-6.4.22.tgz", @@ -62367,31 +62383,31 @@ } }, "node_modules/react-popper": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.2.5.tgz", - "integrity": "sha512-kxGkS80eQGtLl18+uig1UIf9MKixFSyPxglsgLBxlYnyDf65BiY9B3nZSc6C9XUNDgStROB0fMQlTEz1KxGddw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", + "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", "dependencies": { "react-fast-compare": "^3.0.1", "warning": "^4.0.2" }, "peerDependencies": { "@popperjs/core": "^2.0.0", - "react": "^16.8.0 || ^17" + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" } }, "node_modules/react-popper-tooltip": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz", - "integrity": "sha512-EnERAnnKRptQBJyaee5GJScWNUKQPDD2ywvzZyUjst/wj5U64C8/CnSYLNEmP2hG0IJ3ZhtDxE8oDN+KOyavXQ==", - "dev": true, + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-4.4.2.tgz", + "integrity": "sha512-y48r0mpzysRTZAIh8m2kpZ8S1YPNqGtQPDrlXYSGvDS1c1GpG/NUXbsbIdfbhXfmSaRJuTcaT6N1q3CKuHRVbg==", "dependencies": { - "@babel/runtime": "^7.12.5", - "@popperjs/core": "^2.5.4", - "react-popper": "^2.2.4" + "@babel/runtime": "^7.18.3", + "@popperjs/core": "^2.11.5", + "react-popper": "^2.3.0" }, "peerDependencies": { - "react": "^16.6.0 || ^17.0.0", - "react-dom": "^16.6.0 || ^17.0.0" + "react": ">=16.6.0", + "react-dom": ">=16.6.0" } }, "node_modules/react-query": { @@ -72194,9 +72210,9 @@ } }, "@babel/runtime": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", - "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -78277,6 +78293,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true + }, + "react-popper-tooltip": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz", + "integrity": "sha512-EnERAnnKRptQBJyaee5GJScWNUKQPDD2ywvzZyUjst/wj5U64C8/CnSYLNEmP2hG0IJ3ZhtDxE8oDN+KOyavXQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5", + "@popperjs/core": "^2.5.4", + "react-popper": "^2.2.4" + } } } }, @@ -118480,23 +118507,22 @@ "requires": {} }, "react-popper": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.2.5.tgz", - "integrity": "sha512-kxGkS80eQGtLl18+uig1UIf9MKixFSyPxglsgLBxlYnyDf65BiY9B3nZSc6C9XUNDgStROB0fMQlTEz1KxGddw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", + "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", "requires": { "react-fast-compare": "^3.0.1", "warning": "^4.0.2" } }, "react-popper-tooltip": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz", - "integrity": "sha512-EnERAnnKRptQBJyaee5GJScWNUKQPDD2ywvzZyUjst/wj5U64C8/CnSYLNEmP2hG0IJ3ZhtDxE8oDN+KOyavXQ==", - "dev": true, + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-4.4.2.tgz", + "integrity": "sha512-y48r0mpzysRTZAIh8m2kpZ8S1YPNqGtQPDrlXYSGvDS1c1GpG/NUXbsbIdfbhXfmSaRJuTcaT6N1q3CKuHRVbg==", "requires": { - "@babel/runtime": "^7.12.5", - "@popperjs/core": "^2.5.4", - "react-popper": "^2.2.4" + "@babel/runtime": "^7.18.3", + "@popperjs/core": "^2.11.5", + "react-popper": "^2.3.0" } }, "react-query": { diff --git a/package.json b/package.json index f22b832..0b3b6f4 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "react-loader-spinner": "^6.0.0-0", "react-loading-skeleton": "^3.1.0", "react-modal": "^3.15.1", + "react-popper-tooltip": "^4.4.2", "react-query": "^3.35.0", "react-redux": "^8.0.0", "react-router-dom": "^6.3.0", diff --git a/src/Components/InfoCard/InfoCard.tsx b/src/Components/InfoCard/InfoCard.tsx new file mode 100644 index 0000000..6e41f47 --- /dev/null +++ b/src/Components/InfoCard/InfoCard.tsx @@ -0,0 +1,15 @@ +import React, { PropsWithChildren } from 'react' + +interface Props { + +} + +export default function InfoCard(props: PropsWithChildren) { + return ( +
+

+ {props.children} +

+
+ ) +} diff --git a/src/Components/VoteButton/VoteButton.tsx b/src/Components/VoteButton/VoteButton.tsx index 3b1abd6..8efe5b8 100644 --- a/src/Components/VoteButton/VoteButton.tsx +++ b/src/Components/VoteButton/VoteButton.tsx @@ -1,7 +1,7 @@ import { MdLocalFireDepartment } from 'react-icons/md' import Button from 'src/Components/Button/Button' import { useAppSelector, usePressHolder, useResizeListener, useVote } from 'src/utils/hooks' -import { ComponentProps, SyntheticEvent, useRef, useState } from 'react' +import { ComponentProps, SyntheticEvent, useRef, useState, useEffect } from 'react' import styles from './styles.module.scss' import { random, randomItem, numberFormatter } from 'src/utils/helperFunctions' import { useDebouncedCallback, useMountEffect, useThrottledCallback } from '@react-hookz/web' @@ -179,28 +179,44 @@ export default function VoteButton({ onHold(); } - useMountEffect(() => { - const bodyRect = document.body.getBoundingClientRect(); - const btnRect = btnContainerRef.current.getBoundingClientRect() - setBtnPosition({ - top: btnRect.top - bodyRect.top, - left: btnRect.left - bodyRect.left, - width: btnRect.width, - height: btnRect.height - }); + const updateParticlesContainerPos = useDebouncedCallback( + () => { + const bodyRect = document.body.getBoundingClientRect(); + const btnRect = btnContainerRef.current.getBoundingClientRect() + setBtnPosition({ + top: btnRect.top - bodyRect.top, + left: btnRect.left - bodyRect.left, + width: btnRect.width, + height: btnRect.height + }); + }, + [], + 300 + ) - }) - useResizeListener(() => { - const bodyRect = document.body.getBoundingClientRect(); - const btnRect = btnContainerRef.current.getBoundingClientRect() - setBtnPosition({ - top: btnRect.top - bodyRect.top, - left: btnRect.left - bodyRect.left, - width: btnRect.width, - height: btnRect.height - }); - }, { debounce: 300 }) + useEffect(() => { + updateParticlesContainerPos(); + document.addEventListener('scroll', updateParticlesContainerPos) + document.addEventListener('resize', updateParticlesContainerPos) + + return () => { + + document.removeEventListener('scroll', updateParticlesContainerPos) + document.removeEventListener('resize', updateParticlesContainerPos) + } + }, [updateParticlesContainerPos]) + + // useResizeListener(() => { + // const bodyRect = document.body.getBoundingClientRect(); + // const btnRect = btnContainerRef.current.getBoundingClientRect() + // setBtnPosition({ + // top: btnRect.top - bodyRect.top, + // left: btnRect.left - bodyRect.left, + // width: btnRect.width, + // height: btnRect.height + // }); + // }, { debounce: 300 }) return ( - - */} {value.length < 3 && } -

Note: if you link a wallet that was used to create another account previously, you won't be able to login to that account until you remove it from here.

+ + 💡 Note: if you link a wallet that was used to create another account previously, you won't be able to login to that account until you remove it from here. + ) } diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/WalletKey.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/WalletKey.tsx index de2f77d..e45bcf7 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/WalletKey.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/WalletKey.tsx @@ -1,24 +1,26 @@ import { useToggle } from '@react-hookz/web'; import { createAction } from '@reduxjs/toolkit'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { FiTrash2 } from 'react-icons/fi'; +import { FiTrash2, FiLock } from 'react-icons/fi'; import Button from 'src/Components/Button/Button'; import IconButton from 'src/Components/IconButton/IconButton'; import { useReduxEffect } from 'src/utils/hooks/useReduxEffect'; import { WalletKeyType } from './LinkedAccountsCard' import { useAppDispatch } from "src/utils/hooks"; import { openModal } from "src/redux/features/modals.slice"; +import 'react-popper-tooltip/dist/styles.css'; +import { usePopperTooltip } from 'react-popper-tooltip' interface Props { walletKey: WalletKeyType, - canDelete: boolean; + hasMultiWallets?: boolean; onRename: (newName: string) => void onDelete: () => void } -export default function WalletKey({ walletKey, canDelete, onRename, onDelete }: Props) { +export default function WalletKey({ walletKey, hasMultiWallets, onRename, onDelete }: Props) { const ref = useRef(null!); const [name, setName] = useState(walletKey.name); @@ -26,6 +28,15 @@ export default function WalletKey({ walletKey, canDelete, onRename, onDelete }: const dispatch = useAppDispatch(); + + const { + getArrowProps, + getTooltipProps, + setTooltipRef, + setTriggerRef, + visible, + } = usePopperTooltip(); + const CONFIRM_DELETE_WALLET = useMemo(() => createAction<{ confirmed?: boolean }>(`CONFIRM_DELETE_WALLET_${walletKey.key.slice(0, 10)}`)({}), [walletKey.key]) const saveNameChanges = () => { @@ -80,11 +91,31 @@ export default function WalletKey({ walletKey, canDelete, onRename, onDelete }: onClick={saveNameChanges} >Save} - {canDelete && handleDelete()} - > } + {hasMultiWallets &&
+ {!walletKey.is_current ? + handleDelete()} + > + : <> + + + + {visible && ( +
+
+ You're now logged-in with this wallet.
To remove it, login to your account with a different wallet. +
+ )} + + + } + +
} ) } diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.Skeleton.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.Skeleton.tsx index 2e236a1..e2fb909 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.Skeleton.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.Skeleton.tsx @@ -16,12 +16,15 @@ export default function PreferencesTabSkeleton() {
    {Array(3).fill(0).map((_, idx) => -
  • -
    +
  • +
    - -
    -
  • + +
+ +
+
)} diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx index ac7ba73..9e9076d 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx @@ -80,7 +80,7 @@ export default function PreferencesTab() { return (
-
+
-
+
remove(skill.id)}> )} } + + + ℹ️ Can't find a specific skill? You can suggest it to be added here + ) } diff --git a/src/features/Profiles/pages/ProfilePage/ProfilePage.tsx b/src/features/Profiles/pages/ProfilePage/ProfilePage.tsx index 50f709c..92142be 100644 --- a/src/features/Profiles/pages/ProfilePage/ProfilePage.tsx +++ b/src/features/Profiles/pages/ProfilePage/ProfilePage.tsx @@ -12,8 +12,6 @@ import SkillsCard from "./SkillsCard/SkillsCard" import TournamentsCard from "./TournamentsCard/TournamentsCard" import { MEDIA_QUERIES } from "src/utils/theme" import SimilarMakersCard from "./SimilarMakersCard/SimilarMakersCard" -import { useEffect } from "react" -import { gql, useApolloClient } from "@apollo/client" export default function ProfilePage() { @@ -65,7 +63,7 @@ export default function ProfilePage() { -