From e9bcff88d64df03c990fb27ab0e0f09eab615c77 Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Mon, 8 Aug 2022 19:49:26 +0300 Subject: [PATCH 01/24] add logs --- api/functions/login/login.js | 1 + 1 file changed, 1 insertion(+) diff --git a/api/functions/login/login.js b/api/functions/login/login.js index 7dcdbf2..6f0d695 100644 --- a/api/functions/login/login.js +++ b/api/functions/login/login.js @@ -106,6 +106,7 @@ const loginHandler = async (req, res) => { return res.status(200).json({ status: "OK" }) } catch (error) { + console.log(error); return res.status(400).json({ status: 'ERROR', reason: 'Unexpected error happened, please try again' }) } } From cb11f056d97e27a0fa6d69469731c05b21535e82 Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Mon, 8 Aug 2022 21:26:42 +0300 Subject: [PATCH 02/24] reenable "account" setting --- .../Profiles/pages/EditProfilePage/EditProfilePage.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/features/Profiles/pages/EditProfilePage/EditProfilePage.tsx b/src/features/Profiles/pages/EditProfilePage/EditProfilePage.tsx index 787c952..8382d21 100644 --- a/src/features/Profiles/pages/EditProfilePage/EditProfilePage.tsx +++ b/src/features/Profiles/pages/EditProfilePage/EditProfilePage.tsx @@ -16,10 +16,10 @@ const links = [ text: "πŸ‘Ύ My Profile", path: 'my-profile', }, - // { - // text: "πŸ™β€β™‚οΈ Account", - // path: 'account', - // }, + { + text: "πŸ™β€β™‚οΈ Account", + path: 'account', + }, { text: "βš™οΈ Preferences", path: 'preferences', From 30dc59ddedb70622d8c5bdb35af3aad36d10606d Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Mon, 8 Aug 2022 21:52:48 +0300 Subject: [PATCH 03/24] fix: allow linking already linked key --- api/functions/login/login.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/api/functions/login/login.js b/api/functions/login/login.js index 6f0d695..1b1d99b 100644 --- a/api/functions/login/login.js +++ b/api/functions/login/login.js @@ -38,16 +38,13 @@ const loginHandler = async (req, res) => { if (existingKeys.length >= 3) return res.status(400).json({ status: 'ERROR', reason: "Can only link up to 3 wallets" }) - if (existingKeys.includes(key)) - return res.status(400).json({ status: 'ERROR', reason: "Wallet already linked" }) - - - await prisma.userKey.create({ - data: { - key, - user_id, - } - }); + if (!existingKeys.includes(key)) + await prisma.userKey.create({ + data: { + key, + user_id, + } + }); return res .status(200) @@ -106,7 +103,6 @@ const loginHandler = async (req, res) => { return res.status(200).json({ status: "OK" }) } catch (error) { - console.log(error); return res.status(400).json({ status: 'ERROR', reason: 'Unexpected error happened, please try again' }) } } From 4dfa4800dae25a6337d5c2905f40ff60374d2e9d Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Tue, 9 Aug 2022 13:05:21 +0300 Subject: [PATCH 04/24] feat: add update/delete wallet keys api & widgets --- api/functions/graphql/nexus-typegen.ts | 23 ++ api/functions/graphql/schema.graphql | 12 ++ api/functions/graphql/types/users.js | 111 +++++++++- .../migration.sql | 2 + prisma/schema.prisma | 3 +- .../AccountCard/AccountCard.tsx | 162 +++++++++++++- .../LinkingAccountModal.tsx | 13 +- .../LinkingAccountModal/myWalletsKeys.graphql | 13 ++ src/graphql/index.tsx | 197 +++++++++++++----- 9 files changed, 479 insertions(+), 57 deletions(-) create mode 100644 prisma/migrations/20220809081452_add_name_to_user_key/migration.sql create mode 100644 src/features/Profiles/pages/EditProfilePage/AccountCard/LinkingAccountModal/myWalletsKeys.graphql diff --git a/api/functions/graphql/nexus-typegen.ts b/api/functions/graphql/nexus-typegen.ts index 78a7cfd..b3b9805 100644 --- a/api/functions/graphql/nexus-typegen.ts +++ b/api/functions/graphql/nexus-typegen.ts @@ -49,6 +49,10 @@ export interface NexusGenInputs { twitter?: string | null; // String website?: string | null; // String } + UserKeyInputType: { // input type + key: string; // String! + name: string; // String! + } } export interface NexusGenEnums { @@ -204,6 +208,10 @@ export interface NexusGenObjects { twitter?: string | null; // String website?: string | null; // String } + UserKey: { // root type + key: string; // String! + name: string; // String! + } Vote: { // root type amount_in_sat: number; // Int! id: number; // Int! @@ -314,6 +322,7 @@ export interface NexusGenFieldTypes { deleteStory: NexusGenRootTypes['Story'] | null; // Story donate: NexusGenRootTypes['Donation']; // Donation! updateProfile: NexusGenRootTypes['User'] | null; // User + updateUserWalletKeys: NexusGenRootTypes['UserKey'][]; // [UserKey!]! vote: NexusGenRootTypes['Vote']; // Vote! } PostComment: { // field return type @@ -353,6 +362,7 @@ export interface NexusGenFieldTypes { getTrendingPosts: NexusGenRootTypes['Post'][]; // [Post!]! hottestProjects: NexusGenRootTypes['Project'][]; // [Project!]! me: NexusGenRootTypes['User'] | null; // User + myWalletsKeys: NexusGenRootTypes['UserKey'][]; // [UserKey!]! newProjects: NexusGenRootTypes['Project'][]; // [Project!]! officialTags: NexusGenRootTypes['Tag'][]; // [Tag!]! popularTags: NexusGenRootTypes['Tag'][]; // [Tag!]! @@ -415,6 +425,10 @@ export interface NexusGenFieldTypes { twitter: string | null; // String website: string | null; // String } + UserKey: { // field return type + key: string; // String! + name: string; // String! + } Vote: { // field return type amount_in_sat: number; // Int! id: number; // Int! @@ -523,6 +537,7 @@ export interface NexusGenFieldTypeNames { deleteStory: 'Story' donate: 'Donation' updateProfile: 'User' + updateUserWalletKeys: 'UserKey' vote: 'Vote' } PostComment: { // field return type name @@ -562,6 +577,7 @@ export interface NexusGenFieldTypeNames { getTrendingPosts: 'Post' hottestProjects: 'Project' me: 'User' + myWalletsKeys: 'UserKey' newProjects: 'Project' officialTags: 'Tag' popularTags: 'Tag' @@ -624,6 +640,10 @@ export interface NexusGenFieldTypeNames { twitter: 'String' website: 'String' } + UserKey: { // field return type name + key: 'String' + name: 'String' + } Vote: { // field return type name amount_in_sat: 'Int' id: 'Int' @@ -667,6 +687,9 @@ export interface NexusGenArgTypes { updateProfile: { // args data?: NexusGenInputs['UpdateProfileInput'] | null; // UpdateProfileInput } + updateUserWalletKeys: { // args + data?: NexusGenInputs['UserKeyInputType'][] | null; // [UserKeyInputType!] + } vote: { // args amount_in_sat: number; // Int! item_id: number; // Int! diff --git a/api/functions/graphql/schema.graphql b/api/functions/graphql/schema.graphql index b2994b5..e199a9e 100644 --- a/api/functions/graphql/schema.graphql +++ b/api/functions/graphql/schema.graphql @@ -100,6 +100,7 @@ type Mutation { deleteStory(id: Int!): Story donate(amount_in_sat: Int!): Donation! updateProfile(data: UpdateProfileInput): User + updateUserWalletKeys(data: [UserKeyInputType!]): [UserKey!]! vote(amount_in_sat: Int!, item_id: Int!, item_type: VOTE_ITEM_TYPE!): Vote! } @@ -161,6 +162,7 @@ type Query { getTrendingPosts: [Post!]! hottestProjects(skip: Int = 0, take: Int = 50): [Project!]! me: User + myWalletsKeys: [UserKey!]! newProjects(skip: Int = 0, take: Int = 50): [Project!]! officialTags: [Tag!]! popularTags: [Tag!]! @@ -251,6 +253,16 @@ type User { website: String } +type UserKey { + key: String! + name: String! +} + +input UserKeyInputType { + key: String! + name: String! +} + enum VOTE_ITEM_TYPE { Bounty PostComment diff --git a/api/functions/graphql/types/users.js b/api/functions/graphql/types/users.js index 824cc76..e04dd1f 100644 --- a/api/functions/graphql/types/users.js +++ b/api/functions/graphql/types/users.js @@ -1,6 +1,6 @@ const { prisma } = require('../../../prisma'); -const { objectType, extendType, intArg, nonNull, inputObjectType } = require("nexus"); +const { objectType, extendType, intArg, nonNull, inputObjectType, list } = require("nexus"); const { getUserByPubKey } = require("../../../auth/utils/helperFuncs"); const { removeNulls } = require("./helpers"); @@ -117,14 +117,123 @@ const updateProfile = extendType({ }) +const UserKey = objectType({ + name: 'UserKey', + definition(t) { + t.nonNull.string('key'); + t.nonNull.string('name'); + } +}) + +const myWalletsKeys = extendType({ + type: "Query", + definition(t) { + t.nonNull.list.nonNull.field('myWalletsKeys', { + type: "UserKey", + + async resolve(_, __, ctx) { + const user = await getUserByPubKey(ctx.userPubKey); + + if (!user) + throw new Error("You have to login"); + + return prisma.userKey.findMany({ + where: { + user_id: user.id, + } + }) + } + }) + } +}) + +const UserKeyInputType = inputObjectType({ + name: 'UserKeyInputType', + definition(t) { + t.nonNull.string('key'); + t.nonNull.string('name'); + } +}) + + + +const updateUserWalletKeys = extendType({ + type: 'Mutation', + definition(t) { + t.nonNull.list.nonNull.field('updateUserWalletKeys', { + type: 'UserKey', + args: { data: list(nonNull(UserKeyInputType)) }, + async resolve(_root, args, ctx) { + + + + const user = await getUserByPubKey(ctx.userPubKey); + if (!user) + throw new Error("You have to login"); + + + + // Check if all the sent keys belong to the user + const userKeys = (await prisma.userKey.findMany({ + where: { + AND: { + user_id: { + equals: user.id, + }, + key: { + in: args.data.map(i => i.key) + } + }, + }, + select: { + key: true + } + })).map(i => i.key); + + const newKeys = []; + for (let i = 0; i < args.data.length; i++) { + const item = args.data[i]; + if (userKeys.includes(item.key)) + newKeys.push(item); + } + + + if (newKeys.length === 0) + throw new Error("You can't delete all your keys") + + await prisma.userKey.deleteMany({ + where: { + user_id: user.id + } + }) + + await prisma.userKey.createMany({ + data: newKeys.map(i => ({ + user_id: user.id, + key: i.key, + name: i.name, + })) + }) + + return newKeys; + + } + }) + } +}) + + module.exports = { // Types User, UpdateProfileInput, + UserKey, // Queries me, profile, + myWalletsKeys, // Mutations updateProfile, + updateUserWalletKeys, } \ No newline at end of file diff --git a/prisma/migrations/20220809081452_add_name_to_user_key/migration.sql b/prisma/migrations/20220809081452_add_name_to_user_key/migration.sql new file mode 100644 index 0000000..e04904a --- /dev/null +++ b/prisma/migrations/20220809081452_add_name_to_user_key/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "UserKey" ADD COLUMN "name" TEXT NOT NULL DEFAULT E'New Key Name'; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5042441..b623fdc 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -68,7 +68,8 @@ model User { } model UserKey { - key String @id + key String @id + name String @default("New Key Name") user User? @relation(fields: [user_id], references: [id]) user_id Int? diff --git a/src/features/Profiles/pages/EditProfilePage/AccountCard/AccountCard.tsx b/src/features/Profiles/pages/EditProfilePage/AccountCard/AccountCard.tsx index b27b49e..dccd63b 100644 --- a/src/features/Profiles/pages/EditProfilePage/AccountCard/AccountCard.tsx +++ b/src/features/Profiles/pages/EditProfilePage/AccountCard/AccountCard.tsx @@ -1,20 +1,119 @@ import Button from 'src/Components/Button/Button'; import { useAppDispatch } from 'src/utils/hooks'; import { openModal } from 'src/redux/features/modals.slice'; +import { useMyWalletsKeysQuery, useUpdateUserWalletsKeysMutation } from 'src/graphql'; +import Skeleton from 'react-loading-skeleton'; +import { useReducer } from 'react'; interface Props { } + +type State = { + hasNewChanges: boolean, + keys: Array<{ key: string, name: string }>, + oldKeys: Array<{ key: string, name: string }> +} + + +type Action = + | { + type: 'set' + payload: State['keys'] + } + | { + type: 'delete', + payload: { idx: number } + } + | { + type: 'update', + payload: { + idx: number, + value: string, + } + } + | { + type: 'cancel' + } + +function reducer(state: State, action: Action): State { + switch (action.type) { + case 'set': + return { + hasNewChanges: false, + keys: [...action.payload], + oldKeys: [...action.payload], + } + case 'delete': + if (state.keys.length === 1) + return state; + return { + hasNewChanges: true, + oldKeys: state.oldKeys, + keys: [...state.keys.slice(0, action.payload.idx), ...state.keys.slice(action.payload.idx + 1)] + }; + case 'update': + return { + hasNewChanges: true, + oldKeys: state.oldKeys, + keys: state.keys.map((item, idx) => { + if (idx === action.payload.idx) + return { + ...item, + name: action.payload.value + } + return item; + }), + + } + case 'cancel': + return { + hasNewChanges: false, + keys: [...state.oldKeys], + oldKeys: state.oldKeys, + } + } +} + export default function AccountCard({ }: Props) { - const dispatch = useAppDispatch() + const dispatch = useAppDispatch(); + const [keysState, keysDispatch] = useReducer(reducer, { keys: [], oldKeys: [], hasNewChanges: false, }); + const myKeysQuery = useMyWalletsKeysQuery({ + onCompleted: data => { + keysDispatch({ + type: 'set', + payload: data.myWalletsKeys + }) + } + }); + const [updateKeys, updatingKeysStatus] = useUpdateUserWalletsKeysMutation({ + onCompleted: data => { + keysDispatch({ + type: "set", + payload: data.updateUserWalletKeys + }) + } + }) const connectNewWallet = () => { dispatch(openModal({ Modal: "LinkingAccountModal" })) } + const saveChanges = () => { + updateKeys({ + variables: { + data: keysState.keys.map(v => ({ key: v.key, name: v.name })) + } + }) + } + + const cancelChanges = () => { + keysDispatch({ type: 'cancel' }); + } + return (
@@ -28,9 +127,64 @@ export default function AccountCard({ }: Props) {
You can add a new wallet from the button below.

- + { + myKeysQuery.loading ? +
    + {Array(2).fill(0).map((_, idx) => +
  • + +
  • + )} +
+ : + <> +
    + {keysState.keys.map((item, idx) => +
  • + { + keysDispatch({ + type: 'update', + payload: { + idx, + value: e.target.value + } + }) + }} + className='p-0 border-0 focus:border-0 focus:outline-none grow + focus:ring-0 placeholder:!text-gray-400' /> + + + +
  • + )} +
+
+ + +
+ {keysState.keys.length < 3 && } + + }
diff --git a/src/features/Profiles/pages/EditProfilePage/AccountCard/LinkingAccountModal/LinkingAccountModal.tsx b/src/features/Profiles/pages/EditProfilePage/AccountCard/LinkingAccountModal/LinkingAccountModal.tsx index 038040d..757ccee 100644 --- a/src/features/Profiles/pages/EditProfilePage/AccountCard/LinkingAccountModal/LinkingAccountModal.tsx +++ b/src/features/Profiles/pages/EditProfilePage/AccountCard/LinkingAccountModal/LinkingAccountModal.tsx @@ -7,6 +7,7 @@ import { QRCodeSVG } from 'qrcode.react'; import Button from "src/Components/Button/Button"; import { FiCopy } from "react-icons/fi"; import useCopyToClipboard from "src/utils/hooks/useCopyToClipboard"; +import { useApolloClient } from '@apollo/client'; @@ -57,7 +58,8 @@ export default function LinkingAccountModal({ onClose, direction, ...props }: Mo const [copied, setCopied] = useState(false); const { loadingLnurl, data: { lnurl }, error } = useLnurlQuery(); - const clipboard = useCopyToClipboard() + const clipboard = useCopyToClipboard(); + const apolloClient = useApolloClient(); @@ -71,6 +73,13 @@ export default function LinkingAccountModal({ onClose, direction, ...props }: Mo clipboard(lnurl); } + const done = () => { + apolloClient.refetchQueries({ + include: ['MyWalletsKeys'] + }) + onClose?.() + } + let content = <> @@ -114,7 +123,7 @@ export default function LinkingAccountModal({ onClose, direction, ...props }: Mo >{copied ? "Copied" : "Copy"} - - - )} - -
- - -
- {keysState.keys.length < 3 && } - - } - - - ) -} diff --git a/src/features/Profiles/pages/EditProfilePage/AccountCard/LinkingAccountModal/myWalletsKeys.graphql b/src/features/Profiles/pages/EditProfilePage/AccountCard/LinkingAccountModal/myWalletsKeys.graphql deleted file mode 100644 index c5bd961..0000000 --- a/src/features/Profiles/pages/EditProfilePage/AccountCard/LinkingAccountModal/myWalletsKeys.graphql +++ /dev/null @@ -1,13 +0,0 @@ -query MyWalletsKeys { - myWalletsKeys { - key - name - } -} - -mutation UpdateUserWalletsKeys($data: [UserKeyInputType!]) { - updateUserWalletKeys(data: $data) { - key - name - } -} diff --git a/src/features/Profiles/pages/EditProfilePage/EditProfilePage.tsx b/src/features/Profiles/pages/EditProfilePage/EditProfilePage.tsx index ac3939c..6a9a6a0 100644 --- a/src/features/Profiles/pages/EditProfilePage/EditProfilePage.tsx +++ b/src/features/Profiles/pages/EditProfilePage/EditProfilePage.tsx @@ -7,7 +7,6 @@ import { useAppSelector, useMediaQuery } from "src/utils/hooks"; import UpdateMyProfileTab from "./UpdateMyProfileTab/UpdateMyProfileTab"; import { Helmet } from 'react-helmet' import { MEDIA_QUERIES } from "src/utils/theme"; -import AccountCard from "./AccountCard/AccountCard"; import PreferencesTab from "./PreferencesTab/PreferencesTab"; import Card from "src/Components/Card/Card"; @@ -18,11 +17,7 @@ const links = [ path: 'my-profile', }, { - text: "πŸ™β€β™‚οΈ Account", - path: 'account', - }, - { - text: "βš™οΈ Preferences", + text: "βš™οΈ Settings & Preferences", path: 'preferences', } ] @@ -30,24 +25,10 @@ const links = [ export default function EditProfilePage() { - const userId = useAppSelector(state => state.user.me?.id) - const profileQuery = useProfileQuery({ - variables: { - profileId: userId!, - }, - skip: !userId, - }) - const isMediumScreen = useMediaQuery(MEDIA_QUERIES.isMedium) + const isMediumScreen = useMediaQuery(MEDIA_QUERIES.isMedium); - - if (!userId || profileQuery.loading) - return - - if (!profileQuery.data?.profile) - return - return ( <> @@ -97,9 +78,8 @@ export default function EditProfilePage() {
} /> - } /> - } /> - + } /> + } />
diff --git a/src/features/Profiles/pages/EditProfilePage/AccountCard/AccountCard.stories.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.stories.tsx similarity index 90% rename from src/features/Profiles/pages/EditProfilePage/AccountCard/AccountCard.stories.tsx rename to src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.stories.tsx index 7463ed6..319570a 100644 --- a/src/features/Profiles/pages/EditProfilePage/AccountCard/AccountCard.stories.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.stories.tsx @@ -1,5 +1,5 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; -import AccountCard from './AccountCard'; +import AccountCard from './LinkedAccountsCard'; export default { title: 'Profiles/Profile Page/Account Card', diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx new file mode 100644 index 0000000..0526825 --- /dev/null +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx @@ -0,0 +1,168 @@ +import Button from 'src/Components/Button/Button'; +import { useAppDispatch } from 'src/utils/hooks'; +import { openModal } from 'src/redux/features/modals.slice'; +import Card from 'src/Components/Card/Card'; +import { MyProfile } from 'src/graphql'; +import Skeleton from 'react-loading-skeleton'; +import { useReducer } from 'react'; + + +interface Props { + walletsKeys: MyProfile['walletsKeys'] +} + + +type State = { + hasNewChanges: boolean, + keys: Array<{ key: string, name: string }>, + oldKeys: Array<{ key: string, name: string }> +} + + +type Action = + | { + type: 'set' + payload: State['keys'] + } + | { + type: 'delete', + payload: { idx: number } + } + | { + type: 'update', + payload: { + idx: number, + value: string, + } + } + | { + type: 'cancel' + } + +function reducer(state: State, action: Action): State { + switch (action.type) { + case 'set': + return { + hasNewChanges: false, + keys: [...action.payload], + oldKeys: [...action.payload], + } + case 'delete': + if (state.keys.length === 1) + return state; + return { + hasNewChanges: true, + oldKeys: state.oldKeys, + keys: [...state.keys.slice(0, action.payload.idx), ...state.keys.slice(action.payload.idx + 1)] + }; + case 'update': + return { + hasNewChanges: true, + oldKeys: state.oldKeys, + keys: state.keys.map((item, idx) => { + if (idx === action.payload.idx) + return { + ...item, + name: action.payload.value + } + return item; + }), + + } + case 'cancel': + return { + hasNewChanges: false, + keys: [...state.oldKeys], + oldKeys: state.oldKeys, + } + } +} + +export default function LinkedAccountsCard({ walletsKeys }: Props) { + + const dispatch = useAppDispatch(); + const [keysState, keysDispatch] = useReducer(reducer, { keys: [], oldKeys: [], hasNewChanges: false, }); + + // const [updateKeys, updatingKeysStatus] = useUpdateUserWalletsKeysMutation({ + // onCompleted: data => { + // keysDispatch({ + // type: "set", + // payload: data.updateUserWalletKeys + // }) + // } + // }) + + const connectNewWallet = () => { + dispatch(openModal({ Modal: "LinkingAccountModal" })) + } + + const saveChanges = () => { + // updateKeys({ + // variables: { + // data: keysState.keys.map(v => ({ key: v.key, name: v.name })) + // } + // }) + } + + const cancelChanges = () => { + keysDispatch({ type: 'cancel' }); + } + + + return ( + +

πŸ” Linked Accounts

+

+ These are the wallets that you can login to this account from. You can add a new wallet below. +

+
+
    + {walletsKeys.map((item, idx) => +
  • + { + keysDispatch({ + type: 'update', + payload: { + idx, + value: e.target.value + } + }) + }} + className='p-0 border-0 focus:border-0 focus:outline-none grow + focus:ring-0 placeholder:!text-gray-400' /> + + {walletsKeys.length > 1 && } +
  • + )} +
+ {/*
+ + +
*/} + {walletsKeys.length < 3 && + } + +
+
+ ) +} diff --git a/src/features/Profiles/pages/EditProfilePage/AccountCard/LinkingAccountModal/LinkingAccountModal.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx similarity index 100% rename from src/features/Profiles/pages/EditProfilePage/AccountCard/LinkingAccountModal/LinkingAccountModal.tsx rename to src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx diff --git a/src/features/Profiles/pages/EditProfilePage/AccountCard/LinkingAccountModal/index.ts b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/index.ts similarity index 100% rename from src/features/Profiles/pages/EditProfilePage/AccountCard/LinkingAccountModal/index.ts rename to src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/index.ts diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx index 2711363..2fb17e8 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx @@ -1,20 +1,31 @@ -import { Nullable } from 'remirror'; +import LinkedAccountsCard from './LinkedAccountsCard/LinkedAccountsCard'; import CommentsSettingsCard from './CommentsSettingsCard/CommentsSettingsCard'; +import { useMyProfilePreferencesQuery, useUpdateUserPreferencesMutation } from 'src/graphql'; +import LoadingPage from 'src/Components/LoadingPage/LoadingPage'; +import NotFoundPage from "src/features/Shared/pages/NotFoundPage/NotFoundPage"; interface Props { - isOwner?: boolean; - nostr_pub_key: Nullable; - nostr_prv_key: Nullable; - } -export default function PreferencesTab({ nostr_prv_key, nostr_pub_key, isOwner }: Props) { + + +export default function PreferencesTab() { + + const query = useMyProfilePreferencesQuery(); + const [mutate, mutationStatus] = useUpdateUserPreferencesMutation(); + + if (query.loading) + return + + if (!query.data?.me) + return return (
-
- +
+ +
) diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/profilePreferences.graphql b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/profilePreferences.graphql new file mode 100644 index 0000000..d30db02 --- /dev/null +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/profilePreferences.graphql @@ -0,0 +1,21 @@ +query MyProfilePreferences { + me { + walletsKeys { + key + name + } + nostr_prv_key + nostr_pub_key + } +} + +mutation UpdateUserPreferences($userKeys: [UserKeyInputType!]) { + updateUserPreferences(userKeys: $userKeys) { + walletsKeys { + key + name + } + nostr_pub_key + nostr_prv_key + } +} diff --git a/src/features/Profiles/pages/EditProfilePage/SaveChangesCard/SaveChangesCard.tsx b/src/features/Profiles/pages/EditProfilePage/SaveChangesCard/SaveChangesCard.tsx index 3f616c7..0011cd5 100644 --- a/src/features/Profiles/pages/EditProfilePage/SaveChangesCard/SaveChangesCard.tsx +++ b/src/features/Profiles/pages/EditProfilePage/SaveChangesCard/SaveChangesCard.tsx @@ -17,14 +17,10 @@ interface Props { export default function SaveChangesCard(props: Props) { - const userId = useAppSelector(state => state.user.me?.id!) - const profileQuery = useProfileQuery({ - variables: { - profileId: userId, - }, - }) + const user = useAppSelector(state => state.user.me) - if (!profileQuery.data?.profile) + + if (!user) return <> @@ -38,18 +34,18 @@ export default function SaveChangesCard(props: Props) {
- + to={createRoute({ type: 'profile', id: user.id, username: user.name })}> +
-

{profileQuery.data.profile ? trimText(profileQuery.data.profile.name, 30) : "Anonymouse"}

- {profileQuery.data.profile.jobTitle &&

{profileQuery.data.profile.jobTitle}

} +

{user ? trimText(user.name, 30) : "Anonymouse"}

+ {user.jobTitle &&

{user.jobTitle}

}
{/* {showTimeAgo &&

{dayjs().diff(props.date, 'hour') < 24 ? `${dayjs().diff(props.date, 'hour')}h ago` : undefined}

} */}
-

{trimText(profileQuery.data.profile.bio, 120)}

+

{trimText(user.bio, 120)}

} +
+ + {value.length > 1 && } +
)} @@ -157,7 +137,7 @@ export default function LinkedAccountsCard({ walletsKeys }: Props) { Save Changes
*/} - {walletsKeys.length < 3 && + {value.length < 3 && } diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx index 2fb17e8..62ad3be 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx @@ -1,18 +1,45 @@ import LinkedAccountsCard from './LinkedAccountsCard/LinkedAccountsCard'; import CommentsSettingsCard from './CommentsSettingsCard/CommentsSettingsCard'; -import { useMyProfilePreferencesQuery, useUpdateUserPreferencesMutation } from 'src/graphql'; +import { UpdateUserPreferencesMutationVariables, useMyProfilePreferencesQuery, useUpdateUserPreferencesMutation } from 'src/graphql'; import LoadingPage from 'src/Components/LoadingPage/LoadingPage'; import NotFoundPage from "src/features/Shared/pages/NotFoundPage/NotFoundPage"; +import * as yup from "yup"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { Controller, SubmitHandler, useForm } from 'react-hook-form'; +import SaveChangesCard from '../SaveChangesCard/SaveChangesCard'; +import { toast } from 'react-toastify'; +import { NotificationsService } from 'src/services'; + interface Props { } +export type IProfilePreferencesForm = NonNullable; +const schema: yup.SchemaOf = yup.object({ + walletsKeys: yup.array().of(yup.object().shape({ + name: yup.string().required(), + key: yup.string().trim().required(), + }).required()) + .required(), +}).required(); export default function PreferencesTab() { - const query = useMyProfilePreferencesQuery(); + const { register, formState: { errors, isDirty, }, handleSubmit, reset, control } = useForm({ + defaultValues: { + walletsKeys: [] + }, + resolver: yupResolver(schema), + mode: 'onBlur', + }); + + const query = useMyProfilePreferencesQuery({ + onCompleted: data => { + if (data.me) reset(data.me) + } + }); const [mutate, mutationStatus] = useUpdateUserPreferencesMutation(); if (query.loading) @@ -21,12 +48,51 @@ export default function PreferencesTab() { if (!query.data?.me) return + + const onSubmit: SubmitHandler = data => { + if (!Array.isArray(data.walletsKeys)) + return; + + const toastId = toast.loading("Saving changes...", NotificationsService.defaultOptions) + + + mutate({ + variables: { + walletsKeys: data.walletsKeys.map(({ key, name }) => ({ key, name })), + }, + onCompleted: ({ updateUserPreferences }) => { + if (updateUserPreferences) { + reset(updateUserPreferences); + toast.update(toastId, { render: "Saved changes successfully", type: "success", ...NotificationsService.defaultOptions, isLoading: false }); + } + } + }) + .catch(() => { + toast.update(toastId, { render: "A network error happened", type: "error", ...NotificationsService.defaultOptions, isLoading: false }); + mutationStatus.reset() + }) + }; + return (
- + ( + + )} + />
+
+ reset()} + /> +
) } diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/profilePreferences.graphql b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/profilePreferences.graphql index d30db02..6077304 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/profilePreferences.graphql +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/profilePreferences.graphql @@ -9,8 +9,8 @@ query MyProfilePreferences { } } -mutation UpdateUserPreferences($userKeys: [UserKeyInputType!]) { - updateUserPreferences(userKeys: $userKeys) { +mutation UpdateUserPreferences($walletsKeys: [UserKeyInputType!]) { + updateUserPreferences(userKeys: $walletsKeys) { walletsKeys { key name diff --git a/src/graphql/index.tsx b/src/graphql/index.tsx index d6fede7..d6c2f98 100644 --- a/src/graphql/index.tsx +++ b/src/graphql/index.tsx @@ -140,7 +140,7 @@ export type Mutation = { deleteStory: Maybe; donate: Donation; updateProfileDetails: Maybe; - updateUserPreferences: Array; + updateUserPreferences: MyProfile; vote: Vote; }; @@ -576,11 +576,11 @@ export type MyProfilePreferencesQueryVariables = Exact<{ [key: string]: never; } export type MyProfilePreferencesQuery = { __typename?: 'Query', me: { __typename?: 'MyProfile', nostr_prv_key: string | null, nostr_pub_key: string | null, walletsKeys: Array<{ __typename?: 'WalletKey', key: string, name: string }> } | null }; export type UpdateUserPreferencesMutationVariables = Exact<{ - userKeys: InputMaybe | UserKeyInputType>; + walletsKeys: InputMaybe | UserKeyInputType>; }>; -export type UpdateUserPreferencesMutation = { __typename?: 'Mutation', updateUserPreferences: Array<{ __typename?: 'MyProfile', nostr_pub_key: string | null, nostr_prv_key: string | null, walletsKeys: Array<{ __typename?: 'WalletKey', key: string, name: string }> }> }; +export type UpdateUserPreferencesMutation = { __typename?: 'Mutation', updateUserPreferences: { __typename?: 'MyProfile', nostr_pub_key: string | null, nostr_prv_key: string | null, walletsKeys: Array<{ __typename?: 'WalletKey', key: string, name: string }> } }; export type MyProfileAboutQueryVariables = Exact<{ [key: string]: never; }>; @@ -1420,8 +1420,8 @@ export type MyProfilePreferencesQueryHookResult = ReturnType; export type MyProfilePreferencesQueryResult = Apollo.QueryResult; export const UpdateUserPreferencesDocument = gql` - mutation UpdateUserPreferences($userKeys: [UserKeyInputType!]) { - updateUserPreferences(userKeys: $userKeys) { + mutation UpdateUserPreferences($walletsKeys: [UserKeyInputType!]) { + updateUserPreferences(userKeys: $walletsKeys) { walletsKeys { key name @@ -1446,7 +1446,7 @@ export type UpdateUserPreferencesMutationFn = Apollo.MutationFunction Date: Wed, 17 Aug 2022 13:39:14 +0300 Subject: [PATCH 08/24] fix: invalidate the preferences query on linking new account --- .../PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx index 757ccee..81257c6 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx @@ -75,7 +75,7 @@ export default function LinkingAccountModal({ onClose, direction, ...props }: Mo const done = () => { apolloClient.refetchQueries({ - include: ['MyWalletsKeys'] + include: ['MyProfilePreferences'] }) onClose?.() } From fc3bd116b8a0468c5a305f48269d60564e5fad7b Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Wed, 17 Aug 2022 13:53:22 +0300 Subject: [PATCH 09/24] update: linking account modal ui --- api/functions/graphql/nexus-typegen.ts | 917 ------------------ .../LinkingAccountModal.tsx | 27 +- 2 files changed, 13 insertions(+), 931 deletions(-) delete mode 100644 api/functions/graphql/nexus-typegen.ts diff --git a/api/functions/graphql/nexus-typegen.ts b/api/functions/graphql/nexus-typegen.ts deleted file mode 100644 index bd8c67e..0000000 --- a/api/functions/graphql/nexus-typegen.ts +++ /dev/null @@ -1,917 +0,0 @@ -/** - * This file was generated by Nexus Schema - * Do not make changes to this file directly - */ - - -import type { core } from "nexus" -declare global { - interface NexusGenCustomInputMethods { - /** - * Date custom scalar type - */ - date(fieldName: FieldName, opts?: core.CommonInputFieldConfig): void // "Date"; - } -} -declare global { - interface NexusGenCustomOutputMethods { - /** - * Date custom scalar type - */ - date(fieldName: FieldName, ...opts: core.ScalarOutSpread): void // "Date"; - } -} - - -declare global { - interface NexusGen extends NexusGenTypes {} -} - -export interface NexusGenInputs { - ProfileDetailsInput: { // input type - avatar?: string | null; // String - bio?: string | null; // String - email?: string | null; // String - github?: string | null; // String - jobTitle?: string | null; // String - lightning_address?: string | null; // String - linkedin?: string | null; // String - location?: string | null; // String - name?: string | null; // String - twitter?: string | null; // String - website?: string | null; // String - } - StoryInputType: { // input type - body: string; // String! - cover_image?: string | null; // String - id?: number | null; // Int - is_published?: boolean | null; // Boolean - tags: string[]; // [String!]! - title: string; // String! - } - UserKeyInputType: { // input type - key: string; // String! - name: string; // String! - } -} - -export interface NexusGenEnums { - POST_TYPE: "Bounty" | "Question" | "Story" - VOTE_ITEM_TYPE: "Bounty" | "PostComment" | "Project" | "Question" | "Story" | "User" -} - -export interface NexusGenScalars { - String: string - Int: number - Float: number - Boolean: boolean - ID: string - Date: any -} - -export interface NexusGenObjects { - Author: { // root type - avatar: string; // String! - id: number; // Int! - join_date: NexusGenScalars['Date']; // Date! - lightning_address?: string | null; // String - name: string; // String! - } - Award: { // root type - id: number; // Int! - image: string; // String! - title: string; // String! - url: string; // String! - } - Bounty: { // root type - applicants_count: number; // Int! - applications: NexusGenRootTypes['BountyApplication'][]; // [BountyApplication!]! - body: string; // String! - cover_image?: string | null; // String - createdAt: NexusGenScalars['Date']; // Date! - deadline: string; // String! - excerpt: string; // String! - id: number; // Int! - is_published?: boolean | null; // Boolean - reward_amount: number; // Int! - title: string; // String! - updatedAt: NexusGenScalars['Date']; // Date! - votes_count: number; // Int! - } - BountyApplication: { // root type - author: NexusGenRootTypes['Author']; // Author! - date: string; // String! - id: number; // Int! - workplan: string; // String! - } - Category: { // root type - cover_image?: string | null; // String - icon?: string | null; // String - id: number; // Int! - title: string; // String! - } - Donation: { // root type - amount: number; // Int! - createdAt: NexusGenScalars['Date']; // Date! - id: number; // Int! - paid: boolean; // Boolean! - payment_hash: string; // String! - payment_request: string; // String! - } - DonationsStats: { // root type - applications: string; // String! - donations: string; // String! - prizes: string; // String! - touranments: string; // String! - } - Hackathon: { // root type - cover_image: string; // String! - description: string; // String! - end_date: NexusGenScalars['Date']; // Date! - id: number; // Int! - location: string; // String! - start_date: NexusGenScalars['Date']; // Date! - title: string; // String! - website: string; // String! - } - LnurlDetails: { // root type - commentAllowed?: number | null; // Int - maxSendable?: number | null; // Int - metadata?: string | null; // String - minSendable?: number | null; // Int - } - Mutation: {}; - MyProfile: { // root type - avatar: string; // String! - bio?: string | null; // String - email?: string | null; // String - github?: string | null; // String - id: number; // Int! - jobTitle?: string | null; // String - join_date: NexusGenScalars['Date']; // Date! - lightning_address?: string | null; // String - linkedin?: string | null; // String - location?: string | null; // String - name: string; // String! - nostr_prv_key?: string | null; // String - nostr_pub_key?: string | null; // String - role?: string | null; // String - twitter?: string | null; // String - website?: string | null; // String - } - PostComment: { // root type - author: NexusGenRootTypes['Author']; // Author! - body: string; // String! - created_at: NexusGenScalars['Date']; // Date! - id: number; // Int! - parentId?: number | null; // Int - votes_count: number; // Int! - } - Project: { // root type - cover_image: string; // String! - description: string; // String! - id: number; // Int! - lightning_address?: string | null; // String - lnurl_callback_url?: string | null; // String - screenshots: string[]; // [String!]! - thumbnail_image: string; // String! - title: string; // String! - votes_count: number; // Int! - website: string; // String! - } - Query: {}; - Question: { // root type - body: string; // String! - createdAt: NexusGenScalars['Date']; // Date! - excerpt: string; // String! - id: number; // Int! - is_published?: boolean | null; // Boolean - title: string; // String! - updatedAt: NexusGenScalars['Date']; // Date! - votes_count: number; // Int! - } - Story: { // root type - body: string; // String! - cover_image?: string | null; // String - createdAt: NexusGenScalars['Date']; // Date! - excerpt: string; // String! - id: number; // Int! - is_published?: boolean | null; // Boolean - title: string; // String! - updatedAt: NexusGenScalars['Date']; // Date! - votes_count: number; // Int! - } - Tag: { // root type - description?: string | null; // String - icon?: string | null; // String - id: number; // Int! - isOfficial?: boolean | null; // Boolean - title: string; // String! - } - User: { // root type - avatar: string; // String! - bio?: string | null; // String - email?: string | null; // String - github?: string | null; // String - id: number; // Int! - jobTitle?: string | null; // String - join_date: NexusGenScalars['Date']; // Date! - lightning_address?: string | null; // String - linkedin?: string | null; // String - location?: string | null; // String - name: string; // String! - role?: string | null; // String - twitter?: string | null; // String - website?: string | null; // String - } - Vote: { // root type - amount_in_sat: number; // Int! - id: number; // Int! - item_id: number; // Int! - item_type: NexusGenEnums['VOTE_ITEM_TYPE']; // VOTE_ITEM_TYPE! - paid: boolean; // Boolean! - payment_hash: string; // String! - payment_request: string; // String! - } - WalletKey: { // root type - key: string; // String! - name: string; // String! - } -} - -export interface NexusGenInterfaces { - BaseUser: NexusGenRootTypes['MyProfile'] | NexusGenRootTypes['User']; - PostBase: NexusGenRootTypes['Bounty'] | NexusGenRootTypes['Question'] | NexusGenRootTypes['Story']; -} - -export interface NexusGenUnions { - Post: NexusGenRootTypes['Bounty'] | NexusGenRootTypes['Question'] | NexusGenRootTypes['Story']; -} - -export type NexusGenRootTypes = NexusGenInterfaces & NexusGenObjects & NexusGenUnions - -export type NexusGenAllTypes = NexusGenRootTypes & NexusGenScalars & NexusGenEnums - -export interface NexusGenFieldTypes { - Author: { // field return type - avatar: string; // String! - id: number; // Int! - join_date: NexusGenScalars['Date']; // Date! - lightning_address: string | null; // String - name: string; // String! - } - Award: { // field return type - id: number; // Int! - image: string; // String! - project: NexusGenRootTypes['Project']; // Project! - title: string; // String! - url: string; // String! - } - Bounty: { // field return type - applicants_count: number; // Int! - applications: NexusGenRootTypes['BountyApplication'][]; // [BountyApplication!]! - author: NexusGenRootTypes['Author']; // Author! - body: string; // String! - cover_image: string | null; // String - createdAt: NexusGenScalars['Date']; // Date! - deadline: string; // String! - excerpt: string; // String! - id: number; // Int! - is_published: boolean | null; // Boolean - reward_amount: number; // Int! - tags: NexusGenRootTypes['Tag'][]; // [Tag!]! - title: string; // String! - type: string; // String! - updatedAt: NexusGenScalars['Date']; // Date! - votes_count: number; // Int! - } - BountyApplication: { // field return type - author: NexusGenRootTypes['Author']; // Author! - date: string; // String! - id: number; // Int! - workplan: string; // String! - } - Category: { // field return type - apps_count: number; // Int! - cover_image: string | null; // String - icon: string | null; // String - id: number; // Int! - project: NexusGenRootTypes['Project'][]; // [Project!]! - title: string; // String! - votes_sum: number; // Int! - } - Donation: { // field return type - amount: number; // Int! - by: NexusGenRootTypes['User'] | null; // User - createdAt: NexusGenScalars['Date']; // Date! - id: number; // Int! - paid: boolean; // Boolean! - payment_hash: string; // String! - payment_request: string; // String! - } - DonationsStats: { // field return type - applications: string; // String! - donations: string; // String! - prizes: string; // String! - touranments: string; // String! - } - Hackathon: { // field return type - cover_image: string; // String! - description: string; // String! - end_date: NexusGenScalars['Date']; // Date! - id: number; // Int! - location: string; // String! - start_date: NexusGenScalars['Date']; // Date! - tags: NexusGenRootTypes['Tag'][]; // [Tag!]! - title: string; // String! - website: string; // String! - } - LnurlDetails: { // field return type - commentAllowed: number | null; // Int - maxSendable: number | null; // Int - metadata: string | null; // String - minSendable: number | null; // Int - } - Mutation: { // field return type - confirmDonation: NexusGenRootTypes['Donation']; // Donation! - confirmVote: NexusGenRootTypes['Vote']; // Vote! - createStory: NexusGenRootTypes['Story'] | null; // Story - deleteStory: NexusGenRootTypes['Story'] | null; // Story - donate: NexusGenRootTypes['Donation']; // Donation! - updateProfileDetails: NexusGenRootTypes['MyProfile'] | null; // MyProfile - updateUserPreferences: NexusGenRootTypes['MyProfile']; // MyProfile! - vote: NexusGenRootTypes['Vote']; // Vote! - } - MyProfile: { // field return type - avatar: string; // String! - bio: string | null; // String - email: string | null; // String - github: string | null; // String - id: number; // Int! - jobTitle: string | null; // String - join_date: NexusGenScalars['Date']; // Date! - lightning_address: string | null; // String - linkedin: string | null; // String - location: string | null; // String - name: string; // String! - nostr_prv_key: string | null; // String - nostr_pub_key: string | null; // String - role: string | null; // String - stories: NexusGenRootTypes['Story'][]; // [Story!]! - twitter: string | null; // String - walletsKeys: NexusGenRootTypes['WalletKey'][]; // [WalletKey!]! - website: string | null; // String - } - PostComment: { // field return type - author: NexusGenRootTypes['Author']; // Author! - body: string; // String! - created_at: NexusGenScalars['Date']; // Date! - id: number; // Int! - parentId: number | null; // Int - votes_count: number; // Int! - } - Project: { // field return type - awards: NexusGenRootTypes['Award'][]; // [Award!]! - category: NexusGenRootTypes['Category']; // Category! - cover_image: string; // String! - description: string; // String! - id: number; // Int! - lightning_address: string | null; // String - lnurl_callback_url: string | null; // String - screenshots: string[]; // [String!]! - tags: NexusGenRootTypes['Tag'][]; // [Tag!]! - thumbnail_image: string; // String! - title: string; // String! - votes_count: number; // Int! - website: string; // String! - } - Query: { // field return type - allCategories: NexusGenRootTypes['Category'][]; // [Category!]! - allProjects: NexusGenRootTypes['Project'][]; // [Project!]! - getAllHackathons: NexusGenRootTypes['Hackathon'][]; // [Hackathon!]! - getCategory: NexusGenRootTypes['Category']; // Category! - getDonationsStats: NexusGenRootTypes['DonationsStats']; // DonationsStats! - getFeed: NexusGenRootTypes['Post'][]; // [Post!]! - getLnurlDetailsForProject: NexusGenRootTypes['LnurlDetails']; // LnurlDetails! - getMyDrafts: NexusGenRootTypes['Post'][]; // [Post!]! - getPostById: NexusGenRootTypes['Post']; // Post! - getProject: NexusGenRootTypes['Project']; // Project! - getTrendingPosts: NexusGenRootTypes['Post'][]; // [Post!]! - hottestProjects: NexusGenRootTypes['Project'][]; // [Project!]! - me: NexusGenRootTypes['MyProfile'] | null; // MyProfile - newProjects: NexusGenRootTypes['Project'][]; // [Project!]! - officialTags: NexusGenRootTypes['Tag'][]; // [Tag!]! - popularTags: NexusGenRootTypes['Tag'][]; // [Tag!]! - profile: NexusGenRootTypes['User'] | null; // User - projectsByCategory: NexusGenRootTypes['Project'][]; // [Project!]! - searchProjects: NexusGenRootTypes['Project'][]; // [Project!]! - } - Question: { // field return type - author: NexusGenRootTypes['Author']; // Author! - body: string; // String! - createdAt: NexusGenScalars['Date']; // Date! - excerpt: string; // String! - id: number; // Int! - is_published: boolean | null; // Boolean - tags: NexusGenRootTypes['Tag'][]; // [Tag!]! - title: string; // String! - type: string; // String! - updatedAt: NexusGenScalars['Date']; // Date! - votes_count: number; // Int! - } - Story: { // field return type - author: NexusGenRootTypes['Author']; // Author! - body: string; // String! - comments: NexusGenRootTypes['PostComment'][]; // [PostComment!]! - comments_count: number; // Int! - cover_image: string | null; // String - createdAt: NexusGenScalars['Date']; // Date! - excerpt: string; // String! - id: number; // Int! - is_published: boolean | null; // Boolean - tags: NexusGenRootTypes['Tag'][]; // [Tag!]! - title: string; // String! - type: string; // String! - updatedAt: NexusGenScalars['Date']; // Date! - votes_count: number; // Int! - } - Tag: { // field return type - description: string | null; // String - icon: string | null; // String - id: number; // Int! - isOfficial: boolean | null; // Boolean - title: string; // String! - } - User: { // field return type - avatar: string; // String! - bio: string | null; // String - email: string | null; // String - github: string | null; // String - id: number; // Int! - jobTitle: string | null; // String - join_date: NexusGenScalars['Date']; // Date! - lightning_address: string | null; // String - linkedin: string | null; // String - location: string | null; // String - name: string; // String! - role: string | null; // String - stories: NexusGenRootTypes['Story'][]; // [Story!]! - twitter: string | null; // String - website: string | null; // String - } - Vote: { // field return type - amount_in_sat: number; // Int! - id: number; // Int! - item_id: number; // Int! - item_type: NexusGenEnums['VOTE_ITEM_TYPE']; // VOTE_ITEM_TYPE! - paid: boolean; // Boolean! - payment_hash: string; // String! - payment_request: string; // String! - } - WalletKey: { // field return type - key: string; // String! - name: string; // String! - } - BaseUser: { // field return type - avatar: string; // String! - bio: string | null; // String - email: string | null; // String - github: string | null; // String - id: number; // Int! - jobTitle: string | null; // String - join_date: NexusGenScalars['Date']; // Date! - lightning_address: string | null; // String - linkedin: string | null; // String - location: string | null; // String - name: string; // String! - role: string | null; // String - stories: NexusGenRootTypes['Story'][]; // [Story!]! - twitter: string | null; // String - website: string | null; // String - } - PostBase: { // field return type - body: string; // String! - createdAt: NexusGenScalars['Date']; // Date! - excerpt: string; // String! - id: number; // Int! - is_published: boolean | null; // Boolean - title: string; // String! - updatedAt: NexusGenScalars['Date']; // Date! - votes_count: number; // Int! - } -} - -export interface NexusGenFieldTypeNames { - Author: { // field return type name - avatar: 'String' - id: 'Int' - join_date: 'Date' - lightning_address: 'String' - name: 'String' - } - Award: { // field return type name - id: 'Int' - image: 'String' - project: 'Project' - title: 'String' - url: 'String' - } - Bounty: { // field return type name - applicants_count: 'Int' - applications: 'BountyApplication' - author: 'Author' - body: 'String' - cover_image: 'String' - createdAt: 'Date' - deadline: 'String' - excerpt: 'String' - id: 'Int' - is_published: 'Boolean' - reward_amount: 'Int' - tags: 'Tag' - title: 'String' - type: 'String' - updatedAt: 'Date' - votes_count: 'Int' - } - BountyApplication: { // field return type name - author: 'Author' - date: 'String' - id: 'Int' - workplan: 'String' - } - Category: { // field return type name - apps_count: 'Int' - cover_image: 'String' - icon: 'String' - id: 'Int' - project: 'Project' - title: 'String' - votes_sum: 'Int' - } - Donation: { // field return type name - amount: 'Int' - by: 'User' - createdAt: 'Date' - id: 'Int' - paid: 'Boolean' - payment_hash: 'String' - payment_request: 'String' - } - DonationsStats: { // field return type name - applications: 'String' - donations: 'String' - prizes: 'String' - touranments: 'String' - } - Hackathon: { // field return type name - cover_image: 'String' - description: 'String' - end_date: 'Date' - id: 'Int' - location: 'String' - start_date: 'Date' - tags: 'Tag' - title: 'String' - website: 'String' - } - LnurlDetails: { // field return type name - commentAllowed: 'Int' - maxSendable: 'Int' - metadata: 'String' - minSendable: 'Int' - } - Mutation: { // field return type name - confirmDonation: 'Donation' - confirmVote: 'Vote' - createStory: 'Story' - deleteStory: 'Story' - donate: 'Donation' - updateProfileDetails: 'MyProfile' - updateUserPreferences: 'MyProfile' - vote: 'Vote' - } - MyProfile: { // field return type name - avatar: 'String' - bio: 'String' - email: 'String' - github: 'String' - id: 'Int' - jobTitle: 'String' - join_date: 'Date' - lightning_address: 'String' - linkedin: 'String' - location: 'String' - name: 'String' - nostr_prv_key: 'String' - nostr_pub_key: 'String' - role: 'String' - stories: 'Story' - twitter: 'String' - walletsKeys: 'WalletKey' - website: 'String' - } - PostComment: { // field return type name - author: 'Author' - body: 'String' - created_at: 'Date' - id: 'Int' - parentId: 'Int' - votes_count: 'Int' - } - Project: { // field return type name - awards: 'Award' - category: 'Category' - cover_image: 'String' - description: 'String' - id: 'Int' - lightning_address: 'String' - lnurl_callback_url: 'String' - screenshots: 'String' - tags: 'Tag' - thumbnail_image: 'String' - title: 'String' - votes_count: 'Int' - website: 'String' - } - Query: { // field return type name - allCategories: 'Category' - allProjects: 'Project' - getAllHackathons: 'Hackathon' - getCategory: 'Category' - getDonationsStats: 'DonationsStats' - getFeed: 'Post' - getLnurlDetailsForProject: 'LnurlDetails' - getMyDrafts: 'Post' - getPostById: 'Post' - getProject: 'Project' - getTrendingPosts: 'Post' - hottestProjects: 'Project' - me: 'MyProfile' - newProjects: 'Project' - officialTags: 'Tag' - popularTags: 'Tag' - profile: 'User' - projectsByCategory: 'Project' - searchProjects: 'Project' - } - Question: { // field return type name - author: 'Author' - body: 'String' - createdAt: 'Date' - excerpt: 'String' - id: 'Int' - is_published: 'Boolean' - tags: 'Tag' - title: 'String' - type: 'String' - updatedAt: 'Date' - votes_count: 'Int' - } - Story: { // field return type name - author: 'Author' - body: 'String' - comments: 'PostComment' - comments_count: 'Int' - cover_image: 'String' - createdAt: 'Date' - excerpt: 'String' - id: 'Int' - is_published: 'Boolean' - tags: 'Tag' - title: 'String' - type: 'String' - updatedAt: 'Date' - votes_count: 'Int' - } - Tag: { // field return type name - description: 'String' - icon: 'String' - id: 'Int' - isOfficial: 'Boolean' - title: 'String' - } - User: { // field return type name - avatar: 'String' - bio: 'String' - email: 'String' - github: 'String' - id: 'Int' - jobTitle: 'String' - join_date: 'Date' - lightning_address: 'String' - linkedin: 'String' - location: 'String' - name: 'String' - role: 'String' - stories: 'Story' - twitter: 'String' - website: 'String' - } - Vote: { // field return type name - amount_in_sat: 'Int' - id: 'Int' - item_id: 'Int' - item_type: 'VOTE_ITEM_TYPE' - paid: 'Boolean' - payment_hash: 'String' - payment_request: 'String' - } - WalletKey: { // field return type name - key: 'String' - name: 'String' - } - BaseUser: { // field return type name - avatar: 'String' - bio: 'String' - email: 'String' - github: 'String' - id: 'Int' - jobTitle: 'String' - join_date: 'Date' - lightning_address: 'String' - linkedin: 'String' - location: 'String' - name: 'String' - role: 'String' - stories: 'Story' - twitter: 'String' - website: 'String' - } - PostBase: { // field return type name - body: 'String' - createdAt: 'Date' - excerpt: 'String' - id: 'Int' - is_published: 'Boolean' - title: 'String' - updatedAt: 'Date' - votes_count: 'Int' - } -} - -export interface NexusGenArgTypes { - Mutation: { - confirmDonation: { // args - payment_request: string; // String! - preimage: string; // String! - } - confirmVote: { // args - payment_request: string; // String! - preimage: string; // String! - } - createStory: { // args - data?: NexusGenInputs['StoryInputType'] | null; // StoryInputType - } - deleteStory: { // args - id: number; // Int! - } - donate: { // args - amount_in_sat: number; // Int! - } - updateProfileDetails: { // args - data?: NexusGenInputs['ProfileDetailsInput'] | null; // ProfileDetailsInput - } - updateUserPreferences: { // args - userKeys?: NexusGenInputs['UserKeyInputType'][] | null; // [UserKeyInputType!] - } - vote: { // args - amount_in_sat: number; // Int! - item_id: number; // Int! - item_type: NexusGenEnums['VOTE_ITEM_TYPE']; // VOTE_ITEM_TYPE! - } - } - Query: { - allProjects: { // args - skip?: number | null; // Int - take: number | null; // Int - } - getAllHackathons: { // args - sortBy?: string | null; // String - tag?: number | null; // Int - } - getCategory: { // args - id: number; // Int! - } - getFeed: { // args - skip?: number | null; // Int - sortBy?: string | null; // String - tag?: number | null; // Int - take: number | null; // Int - } - getLnurlDetailsForProject: { // args - project_id: number; // Int! - } - getMyDrafts: { // args - type: NexusGenEnums['POST_TYPE']; // POST_TYPE! - } - getPostById: { // args - id: number; // Int! - type: NexusGenEnums['POST_TYPE']; // POST_TYPE! - } - getProject: { // args - id: number; // Int! - } - hottestProjects: { // args - skip?: number | null; // Int - take: number | null; // Int - } - newProjects: { // args - skip?: number | null; // Int - take: number | null; // Int - } - profile: { // args - id: number; // Int! - } - projectsByCategory: { // args - category_id: number; // Int! - skip?: number | null; // Int - take: number | null; // Int - } - searchProjects: { // args - search: string; // String! - skip?: number | null; // Int - take: number | null; // Int - } - } -} - -export interface NexusGenAbstractTypeMembers { - Post: "Bounty" | "Question" | "Story" - BaseUser: "MyProfile" | "User" - PostBase: "Bounty" | "Question" | "Story" -} - -export interface NexusGenTypeInterfaces { - Bounty: "PostBase" - MyProfile: "BaseUser" - Question: "PostBase" - Story: "PostBase" - User: "BaseUser" -} - -export type NexusGenObjectNames = keyof NexusGenObjects; - -export type NexusGenInputNames = keyof NexusGenInputs; - -export type NexusGenEnumNames = keyof NexusGenEnums; - -export type NexusGenInterfaceNames = keyof NexusGenInterfaces; - -export type NexusGenScalarNames = keyof NexusGenScalars; - -export type NexusGenUnionNames = keyof NexusGenUnions; - -export type NexusGenObjectsUsingAbstractStrategyIsTypeOf = never; - -export type NexusGenAbstractsUsingStrategyResolveType = "BaseUser" | "Post" | "PostBase"; - -export type NexusGenFeaturesConfig = { - abstractTypeStrategies: { - isTypeOf: false - resolveType: true - __typename: false - } -} - -export interface NexusGenTypes { - context: any; - inputTypes: NexusGenInputs; - rootTypes: NexusGenRootTypes; - inputTypeShapes: NexusGenInputs & NexusGenEnums & NexusGenScalars; - argTypes: NexusGenArgTypes; - fieldTypes: NexusGenFieldTypes; - fieldTypeNames: NexusGenFieldTypeNames; - allTypes: NexusGenAllTypes; - typeInterfaces: NexusGenTypeInterfaces; - objectNames: NexusGenObjectNames; - inputNames: NexusGenInputNames; - enumNames: NexusGenEnumNames; - interfaceNames: NexusGenInterfaceNames; - scalarNames: NexusGenScalarNames; - unionNames: NexusGenUnionNames; - allInputTypes: NexusGenTypes['inputNames'] | NexusGenTypes['enumNames'] | NexusGenTypes['scalarNames']; - allOutputTypes: NexusGenTypes['objectNames'] | NexusGenTypes['enumNames'] | NexusGenTypes['unionNames'] | NexusGenTypes['interfaceNames'] | NexusGenTypes['scalarNames']; - allNamedTypes: NexusGenTypes['allInputTypes'] | NexusGenTypes['allOutputTypes'] - abstractTypes: NexusGenTypes['interfaceNames'] | NexusGenTypes['unionNames']; - abstractTypeMembers: NexusGenAbstractTypeMembers; - objectsUsingAbstractStrategyIsTypeOf: NexusGenObjectsUsingAbstractStrategyIsTypeOf; - abstractsUsingStrategyResolveType: NexusGenAbstractsUsingStrategyResolveType; - features: NexusGenFeaturesConfig; -} - - -declare global { - interface NexusGenPluginTypeConfig { - } - interface NexusGenPluginInputTypeConfig { - } - interface NexusGenPluginFieldConfig { - } - interface NexusGenPluginInputFieldConfig { - } - interface NexusGenPluginSchemaConfig { - } - interface NexusGenPluginArgConfig { - } -} \ No newline at end of file diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx index 81257c6..db40eba 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx @@ -8,6 +8,7 @@ import Button from "src/Components/Button/Button"; import { FiCopy } from "react-icons/fi"; import useCopyToClipboard from "src/utils/hooks/useCopyToClipboard"; import { useApolloClient } from '@apollo/client'; +import { IoClose } from 'react-icons/io5'; @@ -84,30 +85,27 @@ export default function LinkingAccountModal({ onClose, direction, ...props }: Mo let content = <> if (error) - content =
-

Something wrong happened...

- Refresh the page + content =
+

Ooops...😡

+

An error happened while fetching the link, please check your internet connection and try again.

else if (loadingLnurl) - content =
- -

Fetching Lnurl-Auth...

+ content =
+ +

Fetching Lnurl-Auth Link...

else content = - <> -

- Link your account ⚑ -

+

- Scan this code or copy + paste it to your other lightning wallet to be able to login later with it to this account. + Scan this QR code with your other lightning wallet & you will be able to use it to login to this account.
When done, click the button below to close this modal.

@@ -125,12 +123,11 @@ export default function LinkingAccountModal({ onClose, direction, ...props }: Mo color='primary' onClick={done} fullWidth - className='mt-16' > Done?
- +
@@ -141,8 +138,10 @@ export default function LinkingAccountModal({ onClose, direction, ...props }: Mo initial='initial' animate="animate" exit='exit' - className="modal-card w-full max-w-[326px] bg-white border-2 border-gray-200 rounded-16 p-16 flex flex-col gap-16 items-center" + className="modal-card max-w-[364px] p-24 rounded-xl relative" > + +

Link new ⚑ wallet

{content} ) From 470776b041e5e1f1f2c3e0defa9b08bc89cfb7b4 Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Wed, 17 Aug 2022 14:10:49 +0300 Subject: [PATCH 10/24] fix: updating network status on change --- api/functions/graphql/nexus-typegen.ts | 917 ++++++++++++++++++ .../PreferencesTab/PreferencesTab.tsx | 7 +- 2 files changed, 922 insertions(+), 2 deletions(-) create mode 100644 api/functions/graphql/nexus-typegen.ts diff --git a/api/functions/graphql/nexus-typegen.ts b/api/functions/graphql/nexus-typegen.ts new file mode 100644 index 0000000..bd8c67e --- /dev/null +++ b/api/functions/graphql/nexus-typegen.ts @@ -0,0 +1,917 @@ +/** + * This file was generated by Nexus Schema + * Do not make changes to this file directly + */ + + +import type { core } from "nexus" +declare global { + interface NexusGenCustomInputMethods { + /** + * Date custom scalar type + */ + date(fieldName: FieldName, opts?: core.CommonInputFieldConfig): void // "Date"; + } +} +declare global { + interface NexusGenCustomOutputMethods { + /** + * Date custom scalar type + */ + date(fieldName: FieldName, ...opts: core.ScalarOutSpread): void // "Date"; + } +} + + +declare global { + interface NexusGen extends NexusGenTypes {} +} + +export interface NexusGenInputs { + ProfileDetailsInput: { // input type + avatar?: string | null; // String + bio?: string | null; // String + email?: string | null; // String + github?: string | null; // String + jobTitle?: string | null; // String + lightning_address?: string | null; // String + linkedin?: string | null; // String + location?: string | null; // String + name?: string | null; // String + twitter?: string | null; // String + website?: string | null; // String + } + StoryInputType: { // input type + body: string; // String! + cover_image?: string | null; // String + id?: number | null; // Int + is_published?: boolean | null; // Boolean + tags: string[]; // [String!]! + title: string; // String! + } + UserKeyInputType: { // input type + key: string; // String! + name: string; // String! + } +} + +export interface NexusGenEnums { + POST_TYPE: "Bounty" | "Question" | "Story" + VOTE_ITEM_TYPE: "Bounty" | "PostComment" | "Project" | "Question" | "Story" | "User" +} + +export interface NexusGenScalars { + String: string + Int: number + Float: number + Boolean: boolean + ID: string + Date: any +} + +export interface NexusGenObjects { + Author: { // root type + avatar: string; // String! + id: number; // Int! + join_date: NexusGenScalars['Date']; // Date! + lightning_address?: string | null; // String + name: string; // String! + } + Award: { // root type + id: number; // Int! + image: string; // String! + title: string; // String! + url: string; // String! + } + Bounty: { // root type + applicants_count: number; // Int! + applications: NexusGenRootTypes['BountyApplication'][]; // [BountyApplication!]! + body: string; // String! + cover_image?: string | null; // String + createdAt: NexusGenScalars['Date']; // Date! + deadline: string; // String! + excerpt: string; // String! + id: number; // Int! + is_published?: boolean | null; // Boolean + reward_amount: number; // Int! + title: string; // String! + updatedAt: NexusGenScalars['Date']; // Date! + votes_count: number; // Int! + } + BountyApplication: { // root type + author: NexusGenRootTypes['Author']; // Author! + date: string; // String! + id: number; // Int! + workplan: string; // String! + } + Category: { // root type + cover_image?: string | null; // String + icon?: string | null; // String + id: number; // Int! + title: string; // String! + } + Donation: { // root type + amount: number; // Int! + createdAt: NexusGenScalars['Date']; // Date! + id: number; // Int! + paid: boolean; // Boolean! + payment_hash: string; // String! + payment_request: string; // String! + } + DonationsStats: { // root type + applications: string; // String! + donations: string; // String! + prizes: string; // String! + touranments: string; // String! + } + Hackathon: { // root type + cover_image: string; // String! + description: string; // String! + end_date: NexusGenScalars['Date']; // Date! + id: number; // Int! + location: string; // String! + start_date: NexusGenScalars['Date']; // Date! + title: string; // String! + website: string; // String! + } + LnurlDetails: { // root type + commentAllowed?: number | null; // Int + maxSendable?: number | null; // Int + metadata?: string | null; // String + minSendable?: number | null; // Int + } + Mutation: {}; + MyProfile: { // root type + avatar: string; // String! + bio?: string | null; // String + email?: string | null; // String + github?: string | null; // String + id: number; // Int! + jobTitle?: string | null; // String + join_date: NexusGenScalars['Date']; // Date! + lightning_address?: string | null; // String + linkedin?: string | null; // String + location?: string | null; // String + name: string; // String! + nostr_prv_key?: string | null; // String + nostr_pub_key?: string | null; // String + role?: string | null; // String + twitter?: string | null; // String + website?: string | null; // String + } + PostComment: { // root type + author: NexusGenRootTypes['Author']; // Author! + body: string; // String! + created_at: NexusGenScalars['Date']; // Date! + id: number; // Int! + parentId?: number | null; // Int + votes_count: number; // Int! + } + Project: { // root type + cover_image: string; // String! + description: string; // String! + id: number; // Int! + lightning_address?: string | null; // String + lnurl_callback_url?: string | null; // String + screenshots: string[]; // [String!]! + thumbnail_image: string; // String! + title: string; // String! + votes_count: number; // Int! + website: string; // String! + } + Query: {}; + Question: { // root type + body: string; // String! + createdAt: NexusGenScalars['Date']; // Date! + excerpt: string; // String! + id: number; // Int! + is_published?: boolean | null; // Boolean + title: string; // String! + updatedAt: NexusGenScalars['Date']; // Date! + votes_count: number; // Int! + } + Story: { // root type + body: string; // String! + cover_image?: string | null; // String + createdAt: NexusGenScalars['Date']; // Date! + excerpt: string; // String! + id: number; // Int! + is_published?: boolean | null; // Boolean + title: string; // String! + updatedAt: NexusGenScalars['Date']; // Date! + votes_count: number; // Int! + } + Tag: { // root type + description?: string | null; // String + icon?: string | null; // String + id: number; // Int! + isOfficial?: boolean | null; // Boolean + title: string; // String! + } + User: { // root type + avatar: string; // String! + bio?: string | null; // String + email?: string | null; // String + github?: string | null; // String + id: number; // Int! + jobTitle?: string | null; // String + join_date: NexusGenScalars['Date']; // Date! + lightning_address?: string | null; // String + linkedin?: string | null; // String + location?: string | null; // String + name: string; // String! + role?: string | null; // String + twitter?: string | null; // String + website?: string | null; // String + } + Vote: { // root type + amount_in_sat: number; // Int! + id: number; // Int! + item_id: number; // Int! + item_type: NexusGenEnums['VOTE_ITEM_TYPE']; // VOTE_ITEM_TYPE! + paid: boolean; // Boolean! + payment_hash: string; // String! + payment_request: string; // String! + } + WalletKey: { // root type + key: string; // String! + name: string; // String! + } +} + +export interface NexusGenInterfaces { + BaseUser: NexusGenRootTypes['MyProfile'] | NexusGenRootTypes['User']; + PostBase: NexusGenRootTypes['Bounty'] | NexusGenRootTypes['Question'] | NexusGenRootTypes['Story']; +} + +export interface NexusGenUnions { + Post: NexusGenRootTypes['Bounty'] | NexusGenRootTypes['Question'] | NexusGenRootTypes['Story']; +} + +export type NexusGenRootTypes = NexusGenInterfaces & NexusGenObjects & NexusGenUnions + +export type NexusGenAllTypes = NexusGenRootTypes & NexusGenScalars & NexusGenEnums + +export interface NexusGenFieldTypes { + Author: { // field return type + avatar: string; // String! + id: number; // Int! + join_date: NexusGenScalars['Date']; // Date! + lightning_address: string | null; // String + name: string; // String! + } + Award: { // field return type + id: number; // Int! + image: string; // String! + project: NexusGenRootTypes['Project']; // Project! + title: string; // String! + url: string; // String! + } + Bounty: { // field return type + applicants_count: number; // Int! + applications: NexusGenRootTypes['BountyApplication'][]; // [BountyApplication!]! + author: NexusGenRootTypes['Author']; // Author! + body: string; // String! + cover_image: string | null; // String + createdAt: NexusGenScalars['Date']; // Date! + deadline: string; // String! + excerpt: string; // String! + id: number; // Int! + is_published: boolean | null; // Boolean + reward_amount: number; // Int! + tags: NexusGenRootTypes['Tag'][]; // [Tag!]! + title: string; // String! + type: string; // String! + updatedAt: NexusGenScalars['Date']; // Date! + votes_count: number; // Int! + } + BountyApplication: { // field return type + author: NexusGenRootTypes['Author']; // Author! + date: string; // String! + id: number; // Int! + workplan: string; // String! + } + Category: { // field return type + apps_count: number; // Int! + cover_image: string | null; // String + icon: string | null; // String + id: number; // Int! + project: NexusGenRootTypes['Project'][]; // [Project!]! + title: string; // String! + votes_sum: number; // Int! + } + Donation: { // field return type + amount: number; // Int! + by: NexusGenRootTypes['User'] | null; // User + createdAt: NexusGenScalars['Date']; // Date! + id: number; // Int! + paid: boolean; // Boolean! + payment_hash: string; // String! + payment_request: string; // String! + } + DonationsStats: { // field return type + applications: string; // String! + donations: string; // String! + prizes: string; // String! + touranments: string; // String! + } + Hackathon: { // field return type + cover_image: string; // String! + description: string; // String! + end_date: NexusGenScalars['Date']; // Date! + id: number; // Int! + location: string; // String! + start_date: NexusGenScalars['Date']; // Date! + tags: NexusGenRootTypes['Tag'][]; // [Tag!]! + title: string; // String! + website: string; // String! + } + LnurlDetails: { // field return type + commentAllowed: number | null; // Int + maxSendable: number | null; // Int + metadata: string | null; // String + minSendable: number | null; // Int + } + Mutation: { // field return type + confirmDonation: NexusGenRootTypes['Donation']; // Donation! + confirmVote: NexusGenRootTypes['Vote']; // Vote! + createStory: NexusGenRootTypes['Story'] | null; // Story + deleteStory: NexusGenRootTypes['Story'] | null; // Story + donate: NexusGenRootTypes['Donation']; // Donation! + updateProfileDetails: NexusGenRootTypes['MyProfile'] | null; // MyProfile + updateUserPreferences: NexusGenRootTypes['MyProfile']; // MyProfile! + vote: NexusGenRootTypes['Vote']; // Vote! + } + MyProfile: { // field return type + avatar: string; // String! + bio: string | null; // String + email: string | null; // String + github: string | null; // String + id: number; // Int! + jobTitle: string | null; // String + join_date: NexusGenScalars['Date']; // Date! + lightning_address: string | null; // String + linkedin: string | null; // String + location: string | null; // String + name: string; // String! + nostr_prv_key: string | null; // String + nostr_pub_key: string | null; // String + role: string | null; // String + stories: NexusGenRootTypes['Story'][]; // [Story!]! + twitter: string | null; // String + walletsKeys: NexusGenRootTypes['WalletKey'][]; // [WalletKey!]! + website: string | null; // String + } + PostComment: { // field return type + author: NexusGenRootTypes['Author']; // Author! + body: string; // String! + created_at: NexusGenScalars['Date']; // Date! + id: number; // Int! + parentId: number | null; // Int + votes_count: number; // Int! + } + Project: { // field return type + awards: NexusGenRootTypes['Award'][]; // [Award!]! + category: NexusGenRootTypes['Category']; // Category! + cover_image: string; // String! + description: string; // String! + id: number; // Int! + lightning_address: string | null; // String + lnurl_callback_url: string | null; // String + screenshots: string[]; // [String!]! + tags: NexusGenRootTypes['Tag'][]; // [Tag!]! + thumbnail_image: string; // String! + title: string; // String! + votes_count: number; // Int! + website: string; // String! + } + Query: { // field return type + allCategories: NexusGenRootTypes['Category'][]; // [Category!]! + allProjects: NexusGenRootTypes['Project'][]; // [Project!]! + getAllHackathons: NexusGenRootTypes['Hackathon'][]; // [Hackathon!]! + getCategory: NexusGenRootTypes['Category']; // Category! + getDonationsStats: NexusGenRootTypes['DonationsStats']; // DonationsStats! + getFeed: NexusGenRootTypes['Post'][]; // [Post!]! + getLnurlDetailsForProject: NexusGenRootTypes['LnurlDetails']; // LnurlDetails! + getMyDrafts: NexusGenRootTypes['Post'][]; // [Post!]! + getPostById: NexusGenRootTypes['Post']; // Post! + getProject: NexusGenRootTypes['Project']; // Project! + getTrendingPosts: NexusGenRootTypes['Post'][]; // [Post!]! + hottestProjects: NexusGenRootTypes['Project'][]; // [Project!]! + me: NexusGenRootTypes['MyProfile'] | null; // MyProfile + newProjects: NexusGenRootTypes['Project'][]; // [Project!]! + officialTags: NexusGenRootTypes['Tag'][]; // [Tag!]! + popularTags: NexusGenRootTypes['Tag'][]; // [Tag!]! + profile: NexusGenRootTypes['User'] | null; // User + projectsByCategory: NexusGenRootTypes['Project'][]; // [Project!]! + searchProjects: NexusGenRootTypes['Project'][]; // [Project!]! + } + Question: { // field return type + author: NexusGenRootTypes['Author']; // Author! + body: string; // String! + createdAt: NexusGenScalars['Date']; // Date! + excerpt: string; // String! + id: number; // Int! + is_published: boolean | null; // Boolean + tags: NexusGenRootTypes['Tag'][]; // [Tag!]! + title: string; // String! + type: string; // String! + updatedAt: NexusGenScalars['Date']; // Date! + votes_count: number; // Int! + } + Story: { // field return type + author: NexusGenRootTypes['Author']; // Author! + body: string; // String! + comments: NexusGenRootTypes['PostComment'][]; // [PostComment!]! + comments_count: number; // Int! + cover_image: string | null; // String + createdAt: NexusGenScalars['Date']; // Date! + excerpt: string; // String! + id: number; // Int! + is_published: boolean | null; // Boolean + tags: NexusGenRootTypes['Tag'][]; // [Tag!]! + title: string; // String! + type: string; // String! + updatedAt: NexusGenScalars['Date']; // Date! + votes_count: number; // Int! + } + Tag: { // field return type + description: string | null; // String + icon: string | null; // String + id: number; // Int! + isOfficial: boolean | null; // Boolean + title: string; // String! + } + User: { // field return type + avatar: string; // String! + bio: string | null; // String + email: string | null; // String + github: string | null; // String + id: number; // Int! + jobTitle: string | null; // String + join_date: NexusGenScalars['Date']; // Date! + lightning_address: string | null; // String + linkedin: string | null; // String + location: string | null; // String + name: string; // String! + role: string | null; // String + stories: NexusGenRootTypes['Story'][]; // [Story!]! + twitter: string | null; // String + website: string | null; // String + } + Vote: { // field return type + amount_in_sat: number; // Int! + id: number; // Int! + item_id: number; // Int! + item_type: NexusGenEnums['VOTE_ITEM_TYPE']; // VOTE_ITEM_TYPE! + paid: boolean; // Boolean! + payment_hash: string; // String! + payment_request: string; // String! + } + WalletKey: { // field return type + key: string; // String! + name: string; // String! + } + BaseUser: { // field return type + avatar: string; // String! + bio: string | null; // String + email: string | null; // String + github: string | null; // String + id: number; // Int! + jobTitle: string | null; // String + join_date: NexusGenScalars['Date']; // Date! + lightning_address: string | null; // String + linkedin: string | null; // String + location: string | null; // String + name: string; // String! + role: string | null; // String + stories: NexusGenRootTypes['Story'][]; // [Story!]! + twitter: string | null; // String + website: string | null; // String + } + PostBase: { // field return type + body: string; // String! + createdAt: NexusGenScalars['Date']; // Date! + excerpt: string; // String! + id: number; // Int! + is_published: boolean | null; // Boolean + title: string; // String! + updatedAt: NexusGenScalars['Date']; // Date! + votes_count: number; // Int! + } +} + +export interface NexusGenFieldTypeNames { + Author: { // field return type name + avatar: 'String' + id: 'Int' + join_date: 'Date' + lightning_address: 'String' + name: 'String' + } + Award: { // field return type name + id: 'Int' + image: 'String' + project: 'Project' + title: 'String' + url: 'String' + } + Bounty: { // field return type name + applicants_count: 'Int' + applications: 'BountyApplication' + author: 'Author' + body: 'String' + cover_image: 'String' + createdAt: 'Date' + deadline: 'String' + excerpt: 'String' + id: 'Int' + is_published: 'Boolean' + reward_amount: 'Int' + tags: 'Tag' + title: 'String' + type: 'String' + updatedAt: 'Date' + votes_count: 'Int' + } + BountyApplication: { // field return type name + author: 'Author' + date: 'String' + id: 'Int' + workplan: 'String' + } + Category: { // field return type name + apps_count: 'Int' + cover_image: 'String' + icon: 'String' + id: 'Int' + project: 'Project' + title: 'String' + votes_sum: 'Int' + } + Donation: { // field return type name + amount: 'Int' + by: 'User' + createdAt: 'Date' + id: 'Int' + paid: 'Boolean' + payment_hash: 'String' + payment_request: 'String' + } + DonationsStats: { // field return type name + applications: 'String' + donations: 'String' + prizes: 'String' + touranments: 'String' + } + Hackathon: { // field return type name + cover_image: 'String' + description: 'String' + end_date: 'Date' + id: 'Int' + location: 'String' + start_date: 'Date' + tags: 'Tag' + title: 'String' + website: 'String' + } + LnurlDetails: { // field return type name + commentAllowed: 'Int' + maxSendable: 'Int' + metadata: 'String' + minSendable: 'Int' + } + Mutation: { // field return type name + confirmDonation: 'Donation' + confirmVote: 'Vote' + createStory: 'Story' + deleteStory: 'Story' + donate: 'Donation' + updateProfileDetails: 'MyProfile' + updateUserPreferences: 'MyProfile' + vote: 'Vote' + } + MyProfile: { // field return type name + avatar: 'String' + bio: 'String' + email: 'String' + github: 'String' + id: 'Int' + jobTitle: 'String' + join_date: 'Date' + lightning_address: 'String' + linkedin: 'String' + location: 'String' + name: 'String' + nostr_prv_key: 'String' + nostr_pub_key: 'String' + role: 'String' + stories: 'Story' + twitter: 'String' + walletsKeys: 'WalletKey' + website: 'String' + } + PostComment: { // field return type name + author: 'Author' + body: 'String' + created_at: 'Date' + id: 'Int' + parentId: 'Int' + votes_count: 'Int' + } + Project: { // field return type name + awards: 'Award' + category: 'Category' + cover_image: 'String' + description: 'String' + id: 'Int' + lightning_address: 'String' + lnurl_callback_url: 'String' + screenshots: 'String' + tags: 'Tag' + thumbnail_image: 'String' + title: 'String' + votes_count: 'Int' + website: 'String' + } + Query: { // field return type name + allCategories: 'Category' + allProjects: 'Project' + getAllHackathons: 'Hackathon' + getCategory: 'Category' + getDonationsStats: 'DonationsStats' + getFeed: 'Post' + getLnurlDetailsForProject: 'LnurlDetails' + getMyDrafts: 'Post' + getPostById: 'Post' + getProject: 'Project' + getTrendingPosts: 'Post' + hottestProjects: 'Project' + me: 'MyProfile' + newProjects: 'Project' + officialTags: 'Tag' + popularTags: 'Tag' + profile: 'User' + projectsByCategory: 'Project' + searchProjects: 'Project' + } + Question: { // field return type name + author: 'Author' + body: 'String' + createdAt: 'Date' + excerpt: 'String' + id: 'Int' + is_published: 'Boolean' + tags: 'Tag' + title: 'String' + type: 'String' + updatedAt: 'Date' + votes_count: 'Int' + } + Story: { // field return type name + author: 'Author' + body: 'String' + comments: 'PostComment' + comments_count: 'Int' + cover_image: 'String' + createdAt: 'Date' + excerpt: 'String' + id: 'Int' + is_published: 'Boolean' + tags: 'Tag' + title: 'String' + type: 'String' + updatedAt: 'Date' + votes_count: 'Int' + } + Tag: { // field return type name + description: 'String' + icon: 'String' + id: 'Int' + isOfficial: 'Boolean' + title: 'String' + } + User: { // field return type name + avatar: 'String' + bio: 'String' + email: 'String' + github: 'String' + id: 'Int' + jobTitle: 'String' + join_date: 'Date' + lightning_address: 'String' + linkedin: 'String' + location: 'String' + name: 'String' + role: 'String' + stories: 'Story' + twitter: 'String' + website: 'String' + } + Vote: { // field return type name + amount_in_sat: 'Int' + id: 'Int' + item_id: 'Int' + item_type: 'VOTE_ITEM_TYPE' + paid: 'Boolean' + payment_hash: 'String' + payment_request: 'String' + } + WalletKey: { // field return type name + key: 'String' + name: 'String' + } + BaseUser: { // field return type name + avatar: 'String' + bio: 'String' + email: 'String' + github: 'String' + id: 'Int' + jobTitle: 'String' + join_date: 'Date' + lightning_address: 'String' + linkedin: 'String' + location: 'String' + name: 'String' + role: 'String' + stories: 'Story' + twitter: 'String' + website: 'String' + } + PostBase: { // field return type name + body: 'String' + createdAt: 'Date' + excerpt: 'String' + id: 'Int' + is_published: 'Boolean' + title: 'String' + updatedAt: 'Date' + votes_count: 'Int' + } +} + +export interface NexusGenArgTypes { + Mutation: { + confirmDonation: { // args + payment_request: string; // String! + preimage: string; // String! + } + confirmVote: { // args + payment_request: string; // String! + preimage: string; // String! + } + createStory: { // args + data?: NexusGenInputs['StoryInputType'] | null; // StoryInputType + } + deleteStory: { // args + id: number; // Int! + } + donate: { // args + amount_in_sat: number; // Int! + } + updateProfileDetails: { // args + data?: NexusGenInputs['ProfileDetailsInput'] | null; // ProfileDetailsInput + } + updateUserPreferences: { // args + userKeys?: NexusGenInputs['UserKeyInputType'][] | null; // [UserKeyInputType!] + } + vote: { // args + amount_in_sat: number; // Int! + item_id: number; // Int! + item_type: NexusGenEnums['VOTE_ITEM_TYPE']; // VOTE_ITEM_TYPE! + } + } + Query: { + allProjects: { // args + skip?: number | null; // Int + take: number | null; // Int + } + getAllHackathons: { // args + sortBy?: string | null; // String + tag?: number | null; // Int + } + getCategory: { // args + id: number; // Int! + } + getFeed: { // args + skip?: number | null; // Int + sortBy?: string | null; // String + tag?: number | null; // Int + take: number | null; // Int + } + getLnurlDetailsForProject: { // args + project_id: number; // Int! + } + getMyDrafts: { // args + type: NexusGenEnums['POST_TYPE']; // POST_TYPE! + } + getPostById: { // args + id: number; // Int! + type: NexusGenEnums['POST_TYPE']; // POST_TYPE! + } + getProject: { // args + id: number; // Int! + } + hottestProjects: { // args + skip?: number | null; // Int + take: number | null; // Int + } + newProjects: { // args + skip?: number | null; // Int + take: number | null; // Int + } + profile: { // args + id: number; // Int! + } + projectsByCategory: { // args + category_id: number; // Int! + skip?: number | null; // Int + take: number | null; // Int + } + searchProjects: { // args + search: string; // String! + skip?: number | null; // Int + take: number | null; // Int + } + } +} + +export interface NexusGenAbstractTypeMembers { + Post: "Bounty" | "Question" | "Story" + BaseUser: "MyProfile" | "User" + PostBase: "Bounty" | "Question" | "Story" +} + +export interface NexusGenTypeInterfaces { + Bounty: "PostBase" + MyProfile: "BaseUser" + Question: "PostBase" + Story: "PostBase" + User: "BaseUser" +} + +export type NexusGenObjectNames = keyof NexusGenObjects; + +export type NexusGenInputNames = keyof NexusGenInputs; + +export type NexusGenEnumNames = keyof NexusGenEnums; + +export type NexusGenInterfaceNames = keyof NexusGenInterfaces; + +export type NexusGenScalarNames = keyof NexusGenScalars; + +export type NexusGenUnionNames = keyof NexusGenUnions; + +export type NexusGenObjectsUsingAbstractStrategyIsTypeOf = never; + +export type NexusGenAbstractsUsingStrategyResolveType = "BaseUser" | "Post" | "PostBase"; + +export type NexusGenFeaturesConfig = { + abstractTypeStrategies: { + isTypeOf: false + resolveType: true + __typename: false + } +} + +export interface NexusGenTypes { + context: any; + inputTypes: NexusGenInputs; + rootTypes: NexusGenRootTypes; + inputTypeShapes: NexusGenInputs & NexusGenEnums & NexusGenScalars; + argTypes: NexusGenArgTypes; + fieldTypes: NexusGenFieldTypes; + fieldTypeNames: NexusGenFieldTypeNames; + allTypes: NexusGenAllTypes; + typeInterfaces: NexusGenTypeInterfaces; + objectNames: NexusGenObjectNames; + inputNames: NexusGenInputNames; + enumNames: NexusGenEnumNames; + interfaceNames: NexusGenInterfaceNames; + scalarNames: NexusGenScalarNames; + unionNames: NexusGenUnionNames; + allInputTypes: NexusGenTypes['inputNames'] | NexusGenTypes['enumNames'] | NexusGenTypes['scalarNames']; + allOutputTypes: NexusGenTypes['objectNames'] | NexusGenTypes['enumNames'] | NexusGenTypes['unionNames'] | NexusGenTypes['interfaceNames'] | NexusGenTypes['scalarNames']; + allNamedTypes: NexusGenTypes['allInputTypes'] | NexusGenTypes['allOutputTypes'] + abstractTypes: NexusGenTypes['interfaceNames'] | NexusGenTypes['unionNames']; + abstractTypeMembers: NexusGenAbstractTypeMembers; + objectsUsingAbstractStrategyIsTypeOf: NexusGenObjectsUsingAbstractStrategyIsTypeOf; + abstractsUsingStrategyResolveType: NexusGenAbstractsUsingStrategyResolveType; + features: NexusGenFeaturesConfig; +} + + +declare global { + interface NexusGenPluginTypeConfig { + } + interface NexusGenPluginInputTypeConfig { + } + interface NexusGenPluginFieldConfig { + } + interface NexusGenPluginInputFieldConfig { + } + interface NexusGenPluginSchemaConfig { + } + interface NexusGenPluginArgConfig { + } +} \ No newline at end of file diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx index 62ad3be..cd60575 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx @@ -10,6 +10,7 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form'; import SaveChangesCard from '../SaveChangesCard/SaveChangesCard'; import { toast } from 'react-toastify'; import { NotificationsService } from 'src/services'; +import { NetworkStatus } from '@apollo/client'; interface Props { @@ -38,11 +39,13 @@ export default function PreferencesTab() { const query = useMyProfilePreferencesQuery({ onCompleted: data => { if (data.me) reset(data.me) - } + }, + notifyOnNetworkStatusChange: true, }); + const [mutate, mutationStatus] = useUpdateUserPreferencesMutation(); - if (query.loading) + if (query.networkStatus === NetworkStatus.loading) return if (!query.data?.me) From 9d1a4785c01074cf861c084ab914f714f12e2fcb Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Wed, 17 Aug 2022 14:17:34 +0300 Subject: [PATCH 11/24] update: add leave prompt to preferneces page --- .../EditProfilePage/PreferencesTab/PreferencesTab.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx index cd60575..d62909f 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx @@ -11,6 +11,7 @@ import SaveChangesCard from '../SaveChangesCard/SaveChangesCard'; import { toast } from 'react-toastify'; import { NotificationsService } from 'src/services'; import { NetworkStatus } from '@apollo/client'; +import { usePrompt } from 'src/utils/hooks'; interface Props { @@ -28,7 +29,7 @@ const schema: yup.SchemaOf = yup.object({ export default function PreferencesTab() { - const { register, formState: { errors, isDirty, }, handleSubmit, reset, control } = useForm({ + const { formState: { isDirty, }, handleSubmit, reset, control } = useForm({ defaultValues: { walletsKeys: [] }, @@ -42,9 +43,11 @@ export default function PreferencesTab() { }, notifyOnNetworkStatusChange: true, }); - const [mutate, mutationStatus] = useUpdateUserPreferencesMutation(); + usePrompt('You may have some unsaved changes. You still want to leave?', isDirty) + + if (query.networkStatus === NetworkStatus.loading) return From fd69deeb5c7d9a149923485a12e4b619ef95e0bd Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Wed, 17 Aug 2022 14:21:18 +0300 Subject: [PATCH 12/24] update: rename 'linked accounts' to 'linked wallets' --- .../PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx index 1f36d6e..d4d179c 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx @@ -93,7 +93,7 @@ export default function LinkedAccountsCard({ value, onChange }: Props) { return ( -

πŸ” Linked Accounts

+

πŸ” Linked Wallets

These are the wallets that you can login to this account from. You can add a new wallet below.

From 3ebd12b0298c5773b60a3a75663a55d2f7b48ff2 Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Wed, 17 Aug 2022 16:36:19 +0300 Subject: [PATCH 13/24] feat: added skeletons to profile tabs, added api mocsk to profile --- .../PostCard/Header/Header.Skeleton.tsx | 3 +- .../pages/EditProfilePage/EditProfilePage.tsx | 6 +++ .../LinkedAccountsCard/LinkedAccountsCard.tsx | 49 ------------------- .../PreferencesTab.Skeleton.tsx | 45 +++++++++++++++++ .../PreferencesTab/PreferencesTab.tsx | 15 ++++-- .../PreferencesTab/profilePreferences.graphql | 2 + .../SaveChangesCard/SaveChangesCard.tsx | 1 - .../UpdateMyProfileTab.Skeleton.tsx | 30 ++++++++++++ .../UpdateMyProfileTab/UpdateMyProfileTab.tsx | 3 +- src/graphql/index.tsx | 6 ++- src/mocks/data/users.ts | 4 +- src/mocks/handlers.ts | 24 +++++++++ src/mocks/resolvers.ts | 11 +++-- 13 files changed, 134 insertions(+), 65 deletions(-) create mode 100644 src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.Skeleton.tsx create mode 100644 src/features/Profiles/pages/EditProfilePage/UpdateMyProfileTab/UpdateMyProfileTab.Skeleton.tsx diff --git a/src/features/Posts/Components/PostCard/Header/Header.Skeleton.tsx b/src/features/Posts/Components/PostCard/Header/Header.Skeleton.tsx index 9917276..41b4a0e 100644 --- a/src/features/Posts/Components/PostCard/Header/Header.Skeleton.tsx +++ b/src/features/Posts/Components/PostCard/Header/Header.Skeleton.tsx @@ -1,5 +1,4 @@ -import Avatar from 'src/features/Profiles/Components/Avatar/Avatar'; -import dayjs from 'dayjs' + import Skeleton from 'react-loading-skeleton'; interface Props { diff --git a/src/features/Profiles/pages/EditProfilePage/EditProfilePage.tsx b/src/features/Profiles/pages/EditProfilePage/EditProfilePage.tsx index 6a9a6a0..870bf14 100644 --- a/src/features/Profiles/pages/EditProfilePage/EditProfilePage.tsx +++ b/src/features/Profiles/pages/EditProfilePage/EditProfilePage.tsx @@ -29,6 +29,12 @@ export default function EditProfilePage() { const isMediumScreen = useMediaQuery(MEDIA_QUERIES.isMedium); + const user = useAppSelector(state => state.user.me) + + + if (!user) + return + return ( <> diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx index d4d179c..b05d1e3 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx @@ -17,59 +17,10 @@ interface Props { -// function reducer(state: State, action: Action): State { -// switch (action.type) { -// case 'set': -// return { -// hasNewChanges: false, -// keys: [...action.payload], -// oldKeys: [...action.payload], -// } -// case 'delete': -// if (state.keys.length === 1) -// return state; -// return { -// hasNewChanges: true, -// oldKeys: state.oldKeys, -// keys: [...state.keys.slice(0, action.payload.idx), ...state.keys.slice(action.payload.idx + 1)] -// }; -// case 'update': -// return { -// hasNewChanges: true, -// oldKeys: state.oldKeys, -// keys: state.keys.map((item, idx) => { -// if (idx === action.payload.idx) -// return { -// ...item, -// name: action.payload.value -// } -// return item; -// }), - -// } -// case 'cancel': -// return { -// hasNewChanges: false, -// keys: [...state.oldKeys], -// oldKeys: state.oldKeys, -// } -// } -// } - export default function LinkedAccountsCard({ value, onChange }: Props) { const dispatch = useAppDispatch(); const inputsRefs = useRef[]>([]); - // const [keysState, keysDispatch] = useReducer(reducer, { keys: [], oldKeys: [], hasNewChanges: false, }); - - // const [updateKeys, updatingKeysStatus] = useUpdateUserWalletsKeysMutation({ - // onCompleted: data => { - // keysDispatch({ - // type: "set", - // payload: data.updateUserWalletKeys - // }) - // } - // }) const connectNewWallet = () => { dispatch(openModal({ Modal: "LinkingAccountModal" })) diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.Skeleton.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.Skeleton.tsx new file mode 100644 index 0000000..2e236a1 --- /dev/null +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.Skeleton.tsx @@ -0,0 +1,45 @@ +import React from 'react' +import Card from 'src/Components/Card/Card'; +import Skeleton from 'react-loading-skeleton'; + +export default function PreferencesTabSkeleton() { + return ( +
+
+ +

+

+ + +

+ +
+
    + {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 d62909f..657d75d 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx @@ -1,9 +1,8 @@ import LinkedAccountsCard from './LinkedAccountsCard/LinkedAccountsCard'; import CommentsSettingsCard from './CommentsSettingsCard/CommentsSettingsCard'; import { UpdateUserPreferencesMutationVariables, useMyProfilePreferencesQuery, useUpdateUserPreferencesMutation } from 'src/graphql'; -import LoadingPage from 'src/Components/LoadingPage/LoadingPage'; import NotFoundPage from "src/features/Shared/pages/NotFoundPage/NotFoundPage"; - +import PreferencesTabSkeleton from './PreferencesTab.Skeleton' import * as yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; import { Controller, SubmitHandler, useForm } from 'react-hook-form'; @@ -12,6 +11,7 @@ import { toast } from 'react-toastify'; import { NotificationsService } from 'src/services'; import { NetworkStatus } from '@apollo/client'; import { usePrompt } from 'src/utils/hooks'; +import { useEffect } from 'react'; interface Props { @@ -45,11 +45,16 @@ export default function PreferencesTab() { }); const [mutate, mutationStatus] = useUpdateUserPreferencesMutation(); - usePrompt('You may have some unsaved changes. You still want to leave?', isDirty) + useEffect(() => { + console.log("MOUNTED"); + + }, []) + + // usePrompt('You may have some unsaved changes. You still want to leave?', isDirty) - if (query.networkStatus === NetworkStatus.loading) - return + if (query.loading) + return if (!query.data?.me) return diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/profilePreferences.graphql b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/profilePreferences.graphql index 6077304..0f23571 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/profilePreferences.graphql +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/profilePreferences.graphql @@ -1,5 +1,6 @@ query MyProfilePreferences { me { + id walletsKeys { key name @@ -11,6 +12,7 @@ query MyProfilePreferences { mutation UpdateUserPreferences($walletsKeys: [UserKeyInputType!]) { updateUserPreferences(userKeys: $walletsKeys) { + id walletsKeys { key name diff --git a/src/features/Profiles/pages/EditProfilePage/SaveChangesCard/SaveChangesCard.tsx b/src/features/Profiles/pages/EditProfilePage/SaveChangesCard/SaveChangesCard.tsx index 0011cd5..941814c 100644 --- a/src/features/Profiles/pages/EditProfilePage/SaveChangesCard/SaveChangesCard.tsx +++ b/src/features/Profiles/pages/EditProfilePage/SaveChangesCard/SaveChangesCard.tsx @@ -3,7 +3,6 @@ import { Link } from 'react-router-dom' import Button from 'src/Components/Button/Button' import Card from 'src/Components/Card/Card' import Avatar from 'src/features/Profiles/Components/Avatar/Avatar' -import { useProfileQuery } from 'src/graphql' import { trimText } from 'src/utils/helperFunctions' import { useAppSelector } from 'src/utils/hooks' import { createRoute } from 'src/utils/routing' diff --git a/src/features/Profiles/pages/EditProfilePage/UpdateMyProfileTab/UpdateMyProfileTab.Skeleton.tsx b/src/features/Profiles/pages/EditProfilePage/UpdateMyProfileTab/UpdateMyProfileTab.Skeleton.tsx new file mode 100644 index 0000000..51e7e45 --- /dev/null +++ b/src/features/Profiles/pages/EditProfilePage/UpdateMyProfileTab/UpdateMyProfileTab.Skeleton.tsx @@ -0,0 +1,30 @@ +import Card from 'src/Components/Card/Card'; +import Skeleton from 'react-loading-skeleton'; + +export default function UpdateProfileAboutTabSkeleton() { + return ( +
+
+ +
+
+
+
+
+ +
+ +

+

+ +

+
+
+
+
+
+ +
+
+ ) +} diff --git a/src/features/Profiles/pages/EditProfilePage/UpdateMyProfileTab/UpdateMyProfileTab.tsx b/src/features/Profiles/pages/EditProfilePage/UpdateMyProfileTab/UpdateMyProfileTab.tsx index 14dbe90..2af4dbe 100644 --- a/src/features/Profiles/pages/EditProfilePage/UpdateMyProfileTab/UpdateMyProfileTab.tsx +++ b/src/features/Profiles/pages/EditProfilePage/UpdateMyProfileTab/UpdateMyProfileTab.tsx @@ -12,6 +12,7 @@ import Card from "src/Components/Card/Card"; import LoadingPage from "src/Components/LoadingPage/LoadingPage"; import NotFoundPage from "src/features/Shared/pages/NotFoundPage/NotFoundPage"; import { setUser } from "src/redux/features/user.slice"; +import UpdateProfileAboutTabSkeleton from "./UpdateMyProfileTab.Skeleton"; interface Props { } @@ -75,7 +76,7 @@ export default function UpdateMyProfileTab() { if (profileQuery.loading) - return + return if (!profileQuery.data?.me) return diff --git a/src/graphql/index.tsx b/src/graphql/index.tsx index d6c2f98..f9f95ac 100644 --- a/src/graphql/index.tsx +++ b/src/graphql/index.tsx @@ -573,14 +573,14 @@ export type PostDetailsQuery = { __typename?: 'Query', getPostById: { __typename export type MyProfilePreferencesQueryVariables = Exact<{ [key: string]: never; }>; -export type MyProfilePreferencesQuery = { __typename?: 'Query', me: { __typename?: 'MyProfile', nostr_prv_key: string | null, nostr_pub_key: string | null, walletsKeys: Array<{ __typename?: 'WalletKey', key: string, name: string }> } | null }; +export type MyProfilePreferencesQuery = { __typename?: 'Query', me: { __typename?: 'MyProfile', id: number, nostr_prv_key: string | null, nostr_pub_key: string | null, walletsKeys: Array<{ __typename?: 'WalletKey', key: string, name: string }> } | null }; export type UpdateUserPreferencesMutationVariables = Exact<{ walletsKeys: InputMaybe | UserKeyInputType>; }>; -export type UpdateUserPreferencesMutation = { __typename?: 'Mutation', updateUserPreferences: { __typename?: 'MyProfile', nostr_pub_key: string | null, nostr_prv_key: string | null, walletsKeys: Array<{ __typename?: 'WalletKey', key: string, name: string }> } }; +export type UpdateUserPreferencesMutation = { __typename?: 'Mutation', updateUserPreferences: { __typename?: 'MyProfile', id: number, nostr_pub_key: string | null, nostr_prv_key: string | null, walletsKeys: Array<{ __typename?: 'WalletKey', key: string, name: string }> } }; export type MyProfileAboutQueryVariables = Exact<{ [key: string]: never; }>; @@ -1383,6 +1383,7 @@ export type PostDetailsQueryResult = Apollo.QueryResult new Promise((res) => setTimeout(res, ms + Math.random() * 1000)) @@ -195,6 +197,7 @@ export const handlers = [ graphql.query('Me', async (req, res, ctx) => { await delay() + console.log("ME"); return res( ctx.data({ @@ -203,6 +206,27 @@ export const handlers = [ ) }), + + graphql.query('MyProfileAbout', async (req, res, ctx) => { + await delay() + return res( + ctx.data({ + me: me(), + }) + ) + }), + + graphql.query('MyProfilePreferences', async (req, res, ctx) => { + await delay() + return res( + ctx.data({ + me: me(), + }) + ) + }), + + + graphql.query('profile', async (req, res, ctx) => { await delay() diff --git a/src/mocks/resolvers.ts b/src/mocks/resolvers.ts index f9802bf..d09f614 100644 --- a/src/mocks/resolvers.ts +++ b/src/mocks/resolvers.ts @@ -1,5 +1,5 @@ import { MOCK_DATA } from "./data"; -import { MyProfile, Query, QueryGetFeedArgs, QueryGetPostByIdArgs } from 'src/graphql' +import { MyProfile, Query, QueryGetFeedArgs, QueryGetPostByIdArgs, User } from 'src/graphql' import { Chance } from "chance"; import { tags } from "./data/tags"; import { hackathons } from "./data/hackathon"; @@ -72,11 +72,16 @@ export function getAllHackathons() { } export function me() { - return MOCK_DATA['user'] as MyProfile + return { + ...MOCK_DATA['user'], + __typename: "MyProfile", + } as MyProfile } + + export function profile() { - return MOCK_DATA['user'] + return { ...MOCK_DATA['user'], __typename: 'User' } as User } export function getMyDrafts(): Query['getMyDrafts'] { From 4f2688f35377ef2d3d688905d73301595fabe2de Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Wed, 17 Aug 2022 17:15:01 +0300 Subject: [PATCH 14/24] update: make lnurl QRs clickable --- src/features/Auth/pages/LoginPage/LoginPage.tsx | 12 +++++++----- .../LinkingAccountModal/LinkingAccountModal.tsx | 12 +++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/features/Auth/pages/LoginPage/LoginPage.tsx b/src/features/Auth/pages/LoginPage/LoginPage.tsx index f3b9ccb..012c9b6 100644 --- a/src/features/Auth/pages/LoginPage/LoginPage.tsx +++ b/src/features/Auth/pages/LoginPage/LoginPage.tsx @@ -157,11 +157,13 @@ export default function LoginPage() {

Login with lightning ⚑

- + + +

Scan this code or copy + paste it to your lightning wallet. Or click to login with your browser's wallet.

diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx index db40eba..019679d 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx @@ -99,11 +99,13 @@ export default function LinkingAccountModal({ onClose, direction, ...props }: Mo else content =
- + + +

Scan this QR code with your other lightning wallet & you will be able to use it to login to this account.
From dba13a3e92303330dd82ed36ac4a3c7d74068478 Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Thu, 18 Aug 2022 15:06:52 +0300 Subject: [PATCH 15/24] update: fix refreshing preferences page on key addition bug, add unsaved_changes prompt to prefs tab, udpate QR styling --- public/assets/icons/nut.svg | 21 +++++++++++++++++++ .../Auth/pages/LoginPage/LoginPage.tsx | 18 ++++++++++------ .../LinkingAccountModal.tsx | 11 ++++++++-- .../PreferencesTab/PreferencesTab.tsx | 9 ++------ 4 files changed, 44 insertions(+), 15 deletions(-) create mode 100644 public/assets/icons/nut.svg diff --git a/public/assets/icons/nut.svg b/public/assets/icons/nut.svg new file mode 100644 index 0000000..b7590c0 --- /dev/null +++ b/public/assets/icons/nut.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/features/Auth/pages/LoginPage/LoginPage.tsx b/src/features/Auth/pages/LoginPage/LoginPage.tsx index 012c9b6..8e92475 100644 --- a/src/features/Auth/pages/LoginPage/LoginPage.tsx +++ b/src/features/Auth/pages/LoginPage/LoginPage.tsx @@ -153,21 +153,27 @@ export default function LoginPage() {

else - content =
-

- Login with lightning ⚑ -

- + content =
+ +

Login with lightning ⚑

+

Scan this code or copy + paste it to your lightning wallet. Or click to login with your browser's wallet.

-
+
Click to connect diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx index 019679d..483eb6f 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx @@ -98,12 +98,19 @@ export default function LinkingAccountModal({ onClose, direction, ...props }: Mo else content = -
- +
+

diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx index 657d75d..87967b4 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx @@ -11,7 +11,6 @@ import { toast } from 'react-toastify'; import { NotificationsService } from 'src/services'; import { NetworkStatus } from '@apollo/client'; import { usePrompt } from 'src/utils/hooks'; -import { useEffect } from 'react'; interface Props { @@ -45,15 +44,11 @@ export default function PreferencesTab() { }); const [mutate, mutationStatus] = useUpdateUserPreferencesMutation(); - useEffect(() => { - console.log("MOUNTED"); - }, []) - - // usePrompt('You may have some unsaved changes. You still want to leave?', isDirty) + usePrompt('You may have some unsaved changes. You still want to leave?', isDirty) - if (query.loading) + if (query.networkStatus === NetworkStatus.loading) return if (!query.data?.me) From eb7eeb538226c3d9f86d9b797eaea87298db418f Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Thu, 18 Aug 2022 18:55:24 +0300 Subject: [PATCH 16/24] update: new component design --- public/assets/images/nut_3d.png | Bin 0 -> 9125 bytes src/Components/IconButton/IconButton.tsx | 5 +- .../Auth/pages/LoginPage/LoginPage.tsx | 15 +-- .../LinkedAccountsCard/LinkedAccountsCard.tsx | 45 +++------ .../LinkedAccountsCard/WalletKey.tsx | 94 ++++++++++++++++++ .../LinkingAccountModal.tsx | 25 ++--- .../PreferencesTab/PreferencesTab.tsx | 6 +- 7 files changed, 134 insertions(+), 56 deletions(-) create mode 100644 public/assets/images/nut_3d.png create mode 100644 src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/WalletKey.tsx diff --git a/public/assets/images/nut_3d.png b/public/assets/images/nut_3d.png new file mode 100644 index 0000000000000000000000000000000000000000..955694b66794ede819d84eaacf9c88f5f3e5f9ba GIT binary patch literal 9125 zcmV;WBU;>vP)%$f54`|q+K-taxo?97~h%9-yyUp-|-4$&bx zM2F}Q9isY?CI1g;=FFMR_U_%=v9hxAD4aGaFE6*Ns;cZ7P(?)rcQrUKFDolshr`b& zPo7*R2MK+v0r~jjk6Y~AxwAKzI~-eg0B|I>jyNvBUaG1prLwXL`zm$JJtJf6bLMBEK%n>}#gz{M!D ziRaz>lJP@=*7?Kg#pt>S^ZUBYg zECetw4;?ymR!&aNN3tL3pacZWw*bWXVBYySF2%linDJpiJgG1M(ExCx1XP7VFr>l+ zfycIu<7#ZSjWYgb%b-Dn>@&_d+Wag#JNq@+4|GrgvS!Viyo!pl8!IcT$AU4P zH8Xw2SBO?m8j!Gh(l(>Inp;g!B6#dF+yle~K`|ZI^PFJt-XJK>d3JV=oOar20z32D zf`Wp{`T6;qr2*-n0A%&*)g3A-s{RMQ&(-+we68Pu(%^%kkAqdxP24VWz9vI>(wURpJoq zGIi?I7TdRPpSo+;t}B{0Z8}@_kq$T@OP4Oa0v|XDOe#`LaG7UsEfzyA8`k+3W8%0AEm1LT7b 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 void; onKeyDown?: (v: any) => void href?: string; - children: JSX.Element className?: string size?: "sm" | 'md' | 'lg' variant?: 'blank' | 'fill' @@ -26,7 +25,7 @@ const baseBtnStyles: UnionToObjectKeys = { blank: "bg-gray-900 bg-opacity-0 hover:bg-opacity-5 active:bg-opacity-10 active:scale-95 !border-0" } -const IconButton = React.forwardRef(({ +const IconButton = React.forwardRef>(({ href, size = "md", className = "", diff --git a/src/features/Auth/pages/LoginPage/LoginPage.tsx b/src/features/Auth/pages/LoginPage/LoginPage.tsx index 8e92475..3407622 100644 --- a/src/features/Auth/pages/LoginPage/LoginPage.tsx +++ b/src/features/Auth/pages/LoginPage/LoginPage.tsx @@ -156,17 +156,18 @@ export default function LoginPage() { content =

Login with lightning ⚑

- + diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx index b05d1e3..6f38f33 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx @@ -3,16 +3,14 @@ import { useAppDispatch } from 'src/utils/hooks'; import { openModal } from 'src/redux/features/modals.slice'; import Card from 'src/Components/Card/Card'; import { MyProfile } from 'src/graphql'; -import Skeleton from 'react-loading-skeleton'; -import { useReducer, useRef } from 'react'; -import { Nullable } from 'remirror'; +import WalletKey from './WalletKey'; -type Value = MyProfile['walletsKeys'] +export type WalletKeyType = MyProfile['walletsKeys'][number] interface Props { - value: Value, - onChange: (newValue: Value) => void + value: WalletKeyType[], + onChange: (newValue: WalletKeyType[]) => void } @@ -20,7 +18,6 @@ interface Props { export default function LinkedAccountsCard({ value, onChange }: Props) { const dispatch = useAppDispatch(); - const inputsRefs = useRef[]>([]); const connectNewWallet = () => { dispatch(openModal({ Modal: "LinkingAccountModal" })) @@ -46,27 +43,18 @@ export default function LinkedAccountsCard({ value, onChange }: Props) {

πŸ” Linked Wallets

- These are the wallets that you can login to this account from. You can add a new wallet below. + These are the wallets that you can login to this account from. You can add up to 3 wallets.

    {value.map((item, idx) => -
  • - inputsRefs.current[idx] = el} - type="text" - value={item.name} - onChange={e => { - updateKeyName(idx, e.target.value) - }} - className='p-0 border-0 focus:border-0 focus:outline-none grow - focus:ring-0 placeholder:!text-gray-400' /> - -
    - - {value.length > 1 && } -
    -
  • + 1} + onRename={v => updateKeyName(idx, v)} + onDelete={() => deleteKey(idx)} + /> )}
{/*
@@ -88,12 +76,11 @@ export default function LinkedAccountsCard({ value, onChange }: Props) { Save Changes
*/} - {value.length < 3 && - } -
+ {value.length < 3 && + }
) } diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/WalletKey.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/WalletKey.tsx new file mode 100644 index 0000000..63990a5 --- /dev/null +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/WalletKey.tsx @@ -0,0 +1,94 @@ +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 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"; + +interface Props { + walletKey: WalletKeyType, + canDelete: boolean; + onRename: (newName: string) => void + onDelete: () => void +} + + + +export default function WalletKey({ walletKey, canDelete, onRename, onDelete }: Props) { + + const ref = useRef(null!); + const [name, setName] = useState(walletKey.name); + const [editMode, toggleEditMode] = useToggle(false); + const dispatch = useAppDispatch(); + + + const CONFIRM_DELETE_WALLET = useMemo(() => createAction<{ confirmed?: boolean }>(`CONFIRM_DELETE_WALLET_${walletKey.key.slice(0, 10)}`)({}), [walletKey.key]) + + const saveNameChanges = () => { + toggleEditMode(); + onRename(name); + } + + const onConfirmDelete = useCallback(({ payload: { confirmed } }: typeof CONFIRM_DELETE_WALLET) => { + if (confirmed) + onDelete() + }, [onDelete]) + + useReduxEffect(onConfirmDelete, CONFIRM_DELETE_WALLET.type); + + useEffect(() => { + if (editMode) + ref.current.focus() + }, [editMode]) + + const handleDelete = () => { + dispatch(openModal({ + Modal: "ConfirmModal", + props: { + callbackAction: { + type: CONFIRM_DELETE_WALLET.type, + payload: {} + }, + actionName: "Remove", + title: "Remove key", + message: "Are you sure you want to remove this key from your account? Once deleted, you won’t be able to recover it.", + color: "red" + } + })) + } + + return ( +
  • +
    + πŸ”‘ + setName(e.target.value)} + /> + {!editMode && } + {editMode && + } +
    + {canDelete && handleDelete()} + > } +
  • + ) +} diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx index 483eb6f..bfeb522 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal/LinkingAccountModal.tsx @@ -99,29 +99,24 @@ export default function LinkingAccountModal({ onClose, direction, ...props }: Mo else content =
    - +

    - Scan this QR code with your other lightning wallet & you will be able to use it to login to this account. -
    - When done, click the button below to close this modal. + Scan this code or copy + paste it to your lightning wallet to connect another account to your maker profile. You can also click the QR code to open your WebLN wallet. When done, click the button below to close this modal.

    - {/* Click to connect */}
    @@ -147,10 +142,10 @@ export default function LinkingAccountModal({ onClose, direction, ...props }: Mo initial='initial' animate="animate" exit='exit' - className="modal-card max-w-[364px] p-24 rounded-xl relative" + className="modal-card max-w-[442px] p-24 rounded-xl relative" > -

    Link new ⚑ wallet

    +

    Connect another ⚑️ wallet

    {content} ) diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx index 87967b4..1261e7f 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx @@ -33,7 +33,6 @@ export default function PreferencesTab() { walletsKeys: [] }, resolver: yupResolver(schema), - mode: 'onBlur', }); const query = useMyProfilePreferencesQuery({ @@ -86,7 +85,10 @@ export default function PreferencesTab() { control={control} name="walletsKeys" render={({ field: { onChange, value } }) => ( - + { + onChange(v); + handleSubmit(onSubmit)(); + }} /> )} /> From b929600d1f1d556acd7b4d9573fb81abefc012fd Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Thu, 18 Aug 2022 19:01:47 +0300 Subject: [PATCH 17/24] fix: hide the "save-changes" card on the prefernces profile tab --- .../pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx index 1261e7f..ac7ba73 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/PreferencesTab.tsx @@ -94,12 +94,12 @@ export default function PreferencesTab() {
    - reset()} - /> + /> */}
    ) From 1a73b403ae1beac46d069a74d61f0df289c5329a Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Fri, 19 Aug 2022 09:34:38 +0300 Subject: [PATCH 18/24] feat: built remove_key modal --- api/functions/graphql/types/users.js | 5 +- src/Components/Button/Button.tsx | 2 +- .../LinkedAccountsCard/WalletKey.tsx | 10 ++-- .../RemoveWalletKeyModal.tsx | 48 +++++++++++++++++++ .../RemoveWalletKeyModal/index.ts | 3 ++ src/redux/features/modals.slice.ts | 11 ++++- 6 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 src/features/Profiles/pages/EditProfilePage/PreferencesTab/RemoveWalletKeyModal/RemoveWalletKeyModal.tsx create mode 100644 src/features/Profiles/pages/EditProfilePage/PreferencesTab/RemoveWalletKeyModal/index.ts diff --git a/api/functions/graphql/types/users.js b/api/functions/graphql/types/users.js index 81f1231..1d796c1 100644 --- a/api/functions/graphql/types/users.js +++ b/api/functions/graphql/types/users.js @@ -56,8 +56,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) => { + const keys = await prisma.user.findUnique({ where: { id: parent.id } }).userKeys(); + return keys.map(k => ({ ...k, key: k.key.slice(0, 10) + '...' })) } }); } diff --git a/src/Components/Button/Button.tsx b/src/Components/Button/Button.tsx index 2c58f20..8d4c378 100644 --- a/src/Components/Button/Button.tsx +++ b/src/Components/Button/Button.tsx @@ -25,7 +25,7 @@ const btnStylesFill: UnionToObjectKeys = { gray: 'bg-gray-100 hover:bg-gray-200 text-gray-900 active:bg-gray-300', white: 'border border-gray-300 text-gray-900 bg-gray-25 hover:bg-gray-50', black: 'text-white bg-black hover:bg-gray-900', - red: "bg-red-600 hover:bg-red-500 active:bg-red-700 text-white", + red: "bg-red-500 hover:bg-red-600 active:bg-red-700 text-white", } const loadingColor: UnionToObjectKeys = { diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/WalletKey.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/WalletKey.tsx index 63990a5..de2f77d 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/WalletKey.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/WalletKey.tsx @@ -47,16 +47,12 @@ export default function WalletKey({ walletKey, canDelete, onRename, onDelete }: const handleDelete = () => { dispatch(openModal({ - Modal: "ConfirmModal", + Modal: "RemoveWalletKeyModal", props: { callbackAction: { type: CONFIRM_DELETE_WALLET.type, - payload: {} - }, - actionName: "Remove", - title: "Remove key", - message: "Are you sure you want to remove this key from your account? Once deleted, you won’t be able to recover it.", - color: "red" + payload: { confirmed: false } + } } })) } diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/RemoveWalletKeyModal/RemoveWalletKeyModal.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/RemoveWalletKeyModal/RemoveWalletKeyModal.tsx new file mode 100644 index 0000000..53f9d46 --- /dev/null +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/RemoveWalletKeyModal/RemoveWalletKeyModal.tsx @@ -0,0 +1,48 @@ +import { ModalCard, modalCardVariants } from 'src/Components/Modals/ModalsContainer/ModalsContainer' +import { motion } from 'framer-motion' +import { IoClose } from 'react-icons/io5' +import Button from 'src/Components/Button/Button' +import { useAppDispatch } from 'src/utils/hooks' +import { PayloadAction } from '@reduxjs/toolkit' + +interface Props extends ModalCard { + callbackAction: PayloadAction<{ confirmed: boolean }> +} + + + +export default function RemoveWalletKeyModal({ + onClose, direction, callbackAction, +}: Props) { + + const dispatch = useAppDispatch(); + + const handleConfirm = () => { + const action = Object.assign({}, callbackAction); + action.payload = { confirmed: true } + dispatch(action) + onClose?.(); + } + + return ( + + +

    Remove key?

    +
    +

    πŸ”‘

    +

    Are you sure you want to remove this key from your account? Once deleted, you won’t be able to recover it.

    +
    + + +
    +
    +
    + ) +} diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/RemoveWalletKeyModal/index.ts b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/RemoveWalletKeyModal/index.ts new file mode 100644 index 0000000..c056033 --- /dev/null +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/RemoveWalletKeyModal/index.ts @@ -0,0 +1,3 @@ +import { lazyModal } from 'src/utils/helperFunctions'; + +export const { LazyComponent: RemoveWalletKeyModal } = lazyModal(() => import('./RemoveWalletKeyModal')) \ No newline at end of file diff --git a/src/redux/features/modals.slice.ts b/src/redux/features/modals.slice.ts index ed94a76..c718a4f 100644 --- a/src/redux/features/modals.slice.ts +++ b/src/redux/features/modals.slice.ts @@ -9,6 +9,7 @@ import { Claim_FundWithdrawCard, Claim_CopySignatureCard, Claim_GenerateSignatur import { ModalCard } from "src/Components/Modals/ModalsContainer/ModalsContainer"; import { ConfirmModal } from "src/Components/Modals/ConfirmModal"; import { LinkingAccountModal } from "src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkingAccountModal"; +import { RemoveWalletKeyModal } from "src/features/Profiles/pages/EditProfilePage/PreferencesTab/RemoveWalletKeyModal"; import { ComponentProps } from "react"; import { generateId } from "src/utils/helperFunctions"; @@ -24,19 +25,27 @@ export enum Direction { export const ALL_MODALS = { + //Projects ProjectDetailsCard, + + // Auth Login_ScanningWalletCard, Login_NativeWalletCard, Login_SuccessCard, Login_ExternalWalletCard, - VoteCard, Claim_GenerateSignatureCard, Claim_CopySignatureCard, Claim_SubmittedCard, Claim_FundWithdrawCard, + + // Misc ConfirmModal, + VoteCard, NoWeblnModal, + + // User Wallets Keys LinkingAccountModal, + RemoveWalletKeyModal, // Text Editor Modals InsertImageModal, From be06f553d2c8407aac00e1b062b61024b1980cb5 Mon Sep 17 00:00:00 2001 From: Mohammed Taher Ghazal <50504183+MTG2000@users.noreply.github.com> Date: Fri, 19 Aug 2022 19:06:10 +0300 Subject: [PATCH 19/24] Fix: return key --- api/functions/graphql/types/users.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/functions/graphql/types/users.js b/api/functions/graphql/types/users.js index 1d796c1..b633464 100644 --- a/api/functions/graphql/types/users.js +++ b/api/functions/graphql/types/users.js @@ -56,9 +56,9 @@ const MyProfile = objectType({ t.nonNull.list.nonNull.field('walletsKeys', { type: "WalletKey", - resolve: async (parent) => { - const keys = await prisma.user.findUnique({ where: { id: parent.id } }).userKeys(); - return keys.map(k => ({ ...k, key: k.key.slice(0, 10) + '...' })) + resolve: (parent) => { + return prisma.user.findUnique({ where: { id: parent.id } }).userKeys(); + } }); } @@ -238,4 +238,4 @@ module.exports = { // Mutations updateProfileDetails, updateUserPreferences, -} \ No newline at end of file +} From b8e6437a1e323f492a47f50dd3f51ab39cf695b7 Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Fri, 19 Aug 2022 20:29:28 +0300 Subject: [PATCH 20/24] feat: use old account for same wallet --- api/functions/login/login.js | 50 ++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/api/functions/login/login.js b/api/functions/login/login.js index 0475d89..2a30585 100644 --- a/api/functions/login/login.js +++ b/api/functions/login/login.js @@ -65,26 +65,44 @@ const loginHandler = async (req, res) => { const user = await getUserByPubKey(key) if (user === null) { - const nostr_prv_key = generatePrivateKey(); - const nostr_pub_key = getPublicKey(nostr_prv_key); + // Check if user had a previous account using this wallet - const createdUser = await prisma.user.create({ - data: { - pubKey: key, - name: key, - avatar: `https://avatars.dicebear.com/api/bottts/${key}.svg`, - nostr_prv_key, - nostr_pub_key, - }, - }) - await prisma.userKey.create({ - data: { - key, - name: "My original wallet key", - user_id: createdUser.id, + const oldAccount = await prisma.user.findFirst({ + where: { + pubKey: key } }); + if (oldAccount) { + await prisma.userKey.create({ + data: { + key, + name: "My original wallet key", + user_id: oldAccount.id, + } + }); + } else { + const nostr_prv_key = generatePrivateKey(); + const nostr_pub_key = getPublicKey(nostr_prv_key); + + const createdUser = await prisma.user.create({ + data: { + pubKey: key, + name: key, + avatar: `https://avatars.dicebear.com/api/bottts/${key}.svg`, + nostr_prv_key, + nostr_pub_key, + }, + }) + await prisma.userKey.create({ + data: { + key, + name: "My original wallet key", + user_id: createdUser.id, + } + }); + } + } // calc the hash of k1 From 6dd34dd253641879e4fb49c4ffcf93c707aca9d0 Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Sun, 21 Aug 2022 14:32:09 +0300 Subject: [PATCH 21/24] update: add warn msg to adding new account --- .../LinkedAccountsCard/LinkedAccountsCard.tsx | 1 + src/mocks/data/users.ts | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx index 6f38f33..207a4f6 100644 --- a/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx +++ b/src/features/Profiles/pages/EditProfilePage/PreferencesTab/LinkedAccountsCard/LinkedAccountsCard.tsx @@ -81,6 +81,7 @@ export default function LinkedAccountsCard({ value, onChange }: Props) { } +

    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/mocks/data/users.ts b/src/mocks/data/users.ts index 54c15f3..c292050 100644 --- a/src/mocks/data/users.ts +++ b/src/mocks/data/users.ts @@ -19,8 +19,14 @@ export const user: User & MyProfile = { stories: posts.stories, nostr_prv_key: "123123124asdfsadfsa8d7fsadfasdf", nostr_pub_key: "123124123123dfsadfsa8d7f11sadfasdf", - walletsKeys: [{ - key: "1645h234j2421zxvertw", - name: "My alby wallet key" - }] + walletsKeys: [ + { + key: "1645h234j2421zxvertw", + name: "My Alby wallet key" + }, + { + key: "6643534534534534543", + name: "My Phoenix wallet key" + }, + ] } From 0d8828860b56d1b81610f315fd43f9cb2a828b51 Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Tue, 23 Aug 2022 15:02:09 +0300 Subject: [PATCH 22/24] logs --- api/functions/login/login.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/functions/login/login.js b/api/functions/login/login.js index 2a30585..698c0bb 100644 --- a/api/functions/login/login.js +++ b/api/functions/login/login.js @@ -55,6 +55,7 @@ const loginHandler = async (req, res) => { .json({ status: "OK" }) } catch (error) { + console.log(error); return res.status(400).json({ status: 'ERROR', reason: 'Invalid User Token' }) } } @@ -126,6 +127,7 @@ const loginHandler = async (req, res) => { return res.status(200).json({ status: "OK" }) } catch (error) { + console.log(error); return res.status(400).json({ status: 'ERROR', reason: 'Unexpected error happened, please try again' }) } } From b1ea5ae80613c01af4ab3efbf045b84e3488fa02 Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Tue, 23 Aug 2022 15:11:19 +0300 Subject: [PATCH 23/24] fix: remove old keys before inserting new ones --- api/functions/login/login.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/api/functions/login/login.js b/api/functions/login/login.js index 698c0bb..e5b4b0c 100644 --- a/api/functions/login/login.js +++ b/api/functions/login/login.js @@ -37,19 +37,19 @@ const loginHandler = async (req, res) => { if (existingKeys.length >= 3) return res.status(400).json({ status: 'ERROR', reason: "Can only link up to 3 wallets" }) - if (!existingKeys.includes(key)) - await prisma.userKey.create({ - data: { - key, - user_id, - } - }); - // Remove old linking for this key if existing await prisma.userKey.deleteMany({ where: { key } }) + await prisma.userKey.create({ + data: { + key, + user_id, + } + }); + + return res .status(200) .json({ status: "OK" }) From f1f40aa5cb6dfc654acd38f46a4d2ee789211992 Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Tue, 23 Aug 2022 15:18:49 +0300 Subject: [PATCH 24/24] remove logs --- api/functions/login/login.js | 2 -- src/features/Auth/pages/LoginPage/LoginPage.tsx | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/api/functions/login/login.js b/api/functions/login/login.js index e5b4b0c..782f195 100644 --- a/api/functions/login/login.js +++ b/api/functions/login/login.js @@ -55,7 +55,6 @@ const loginHandler = async (req, res) => { .json({ status: "OK" }) } catch (error) { - console.log(error); return res.status(400).json({ status: 'ERROR', reason: 'Invalid User Token' }) } } @@ -127,7 +126,6 @@ const loginHandler = async (req, res) => { return res.status(200).json({ status: "OK" }) } catch (error) { - console.log(error); return res.status(400).json({ status: 'ERROR', reason: 'Unexpected error happened, please try again' }) } } diff --git a/src/features/Auth/pages/LoginPage/LoginPage.tsx b/src/features/Auth/pages/LoginPage/LoginPage.tsx index 3407622..475fb7e 100644 --- a/src/features/Auth/pages/LoginPage/LoginPage.tsx +++ b/src/features/Auth/pages/LoginPage/LoginPage.tsx @@ -175,7 +175,7 @@ export default function LoginPage() { Scan this code or copy + paste it to your lightning wallet. Or click to login with your browser's wallet.