feat: wired linking account api and form state, updated card ui

This commit is contained in:
MTG2000
2022-08-17 13:31:13 +03:00
parent 5a7bac8325
commit 1be2bb4fae
7 changed files with 150 additions and 104 deletions

View File

@@ -339,7 +339,7 @@ export interface NexusGenFieldTypes {
deleteStory: NexusGenRootTypes['Story'] | null; // Story
donate: NexusGenRootTypes['Donation']; // Donation!
updateProfileDetails: NexusGenRootTypes['MyProfile'] | null; // MyProfile
updateUserPreferences: NexusGenRootTypes['MyProfile'][]; // [MyProfile!]!
updateUserPreferences: NexusGenRootTypes['MyProfile']; // MyProfile!
vote: NexusGenRootTypes['Vote']; // Vote!
}
MyProfile: { // field return type

View File

@@ -118,7 +118,7 @@ type Mutation {
deleteStory(id: Int!): Story
donate(amount_in_sat: Int!): Donation!
updateProfileDetails(data: ProfileDetailsInput): MyProfile
updateUserPreferences(userKeys: [UserKeyInputType!]): [MyProfile!]!
updateUserPreferences(userKeys: [UserKeyInputType!]): MyProfile!
vote(amount_in_sat: Int!, item_id: Int!, item_type: VOTE_ITEM_TYPE!): Vote!
}

View File

@@ -161,7 +161,7 @@ const UserKeyInputType = inputObjectType({
const updateUserPreferences = extendType({
type: 'Mutation',
definition(t) {
t.nonNull.list.nonNull.field('updateUserPreferences', {
t.nonNull.field('updateUserPreferences', {
type: 'MyProfile',
args: { userKeys: list(nonNull(UserKeyInputType)) },
async resolve(_root, args, ctx) {
@@ -182,7 +182,7 @@ const updateUserPreferences = extendType({
equals: user.id,
},
key: {
in: args.data.map(i => i.key)
in: args.userKeys.map(i => i.key)
}
},
},
@@ -192,15 +192,15 @@ const updateUserPreferences = extendType({
})).map(i => i.key);
const newKeys = [];
for (let i = 0; i < args.data.length; i++) {
const item = args.data[i];
for (let i = 0; i < args.userKeys.length; i++) {
const item = args.userKeys[i];
if (userKeys.includes(item.key))
newKeys.push(item);
}
if (newKeys.length === 0)
throw new Error("You can't delete all your keys")
throw new Error("You can't delete all your wallets keys")
await prisma.userKey.deleteMany({
where: {

View File

@@ -4,84 +4,63 @@ 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';
import { useReducer, useRef } from 'react';
import { Nullable } from 'remirror';
type Value = MyProfile['walletsKeys']
interface Props {
walletsKeys: MyProfile['walletsKeys']
value: Value,
onChange: (newValue: Value) => void
}
type State = {
hasNewChanges: boolean,
keys: Array<{ key: string, name: string }>,
oldKeys: Array<{ key: string, name: string }>
}
// 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;
// }),
type Action =
| {
type: 'set'
payload: State['keys']
}
| {
type: 'delete',
payload: { idx: number }
}
| {
type: 'update',
payload: {
idx: number,
value: string,
}
}
| {
type: 'cancel'
}
// }
// case 'cancel':
// return {
// hasNewChanges: false,
// keys: [...state.oldKeys],
// oldKeys: state.oldKeys,
// }
// }
// }
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) {
export default function LinkedAccountsCard({ value, onChange }: Props) {
const dispatch = useAppDispatch();
const [keysState, keysDispatch] = useReducer(reducer, { keys: [], oldKeys: [], hasNewChanges: false, });
const inputsRefs = useRef<Nullable<HTMLInputElement>[]>([]);
// const [keysState, keysDispatch] = useReducer(reducer, { keys: [], oldKeys: [], hasNewChanges: false, });
// const [updateKeys, updatingKeysStatus] = useUpdateUserWalletsKeysMutation({
// onCompleted: data => {
@@ -96,16 +75,19 @@ export default function LinkedAccountsCard({ walletsKeys }: Props) {
dispatch(openModal({ Modal: "LinkingAccountModal" }))
}
const saveChanges = () => {
// updateKeys({
// variables: {
// data: keysState.keys.map(v => ({ key: v.key, name: v.name }))
// }
// })
const updateKeyName = (idx: number, newName: string) => {
onChange(value.map((item, i) => {
if (i === idx)
return {
...item,
name: newName
}
return item;
}))
}
const cancelChanges = () => {
keysDispatch({ type: 'cancel' });
const deleteKey = (idx: number,) => {
onChange([...value.slice(0, idx), ...value.slice(idx + 1)])
}
@@ -117,24 +99,22 @@ export default function LinkedAccountsCard({ walletsKeys }: Props) {
</p>
<div className='mt-24 flex flex-col gap-16'>
<ul className="mt-8 relative flex flex-col gap-8">
{walletsKeys.map((item, idx) =>
<li key={item.key} className="flex justify-between items-center text-body4 border-b py-12 px-16 border border-gray-200 rounded-16">
{value.map((item, idx) =>
<li key={item.key} className="flex flex-wrap gap-16 justify-between items-center text-body4 border-b py-12 px-16 border border-gray-200 rounded-16 focus-within:ring-1 ring-primary-200">
<input
ref={el => inputsRefs.current[idx] = el}
type="text"
value={item.name}
onChange={e => {
keysDispatch({
type: 'update',
payload: {
idx,
value: e.target.value
}
})
updateKeyName(idx, 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 && <Button size='sm' color='none' className='text-red-500 !p-0' onClick={() => keysDispatch({ type: 'delete', payload: { idx } })}>Delete key</Button>}
<div className='flex gap-8 ml-auto'>
<Button size='sm' color='none' className='text-blue-400 !p-0' onClick={() => inputsRefs.current[idx]?.focus()}>Rename</Button>
{value.length > 1 && <Button size='sm' color='none' className='text-red-500 !p-0' onClick={() => deleteKey(idx)}>Delete key</Button>}
</div>
</li>
)}
</ul>
@@ -157,7 +137,7 @@ export default function LinkedAccountsCard({ walletsKeys }: Props) {
Save Changes
</Button>
</div> */}
{walletsKeys.length < 3 &&
{value.length < 3 &&
<Button color='white' className='mt-16' onClick={connectNewWallet}>
Connect new wallet
</Button>}

View File

@@ -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<UpdateUserPreferencesMutationVariables>;
const schema: yup.SchemaOf<IProfilePreferencesForm> = 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<IProfilePreferencesForm>({
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 <NotFoundPage />
const onSubmit: SubmitHandler<IProfilePreferencesForm> = 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 (
<div className="grid grid-cols-1 md:grid-cols-3 gap-24">
<div className="col-span-2 flex flex-col gap-24">
<LinkedAccountsCard walletsKeys={query.data.me.walletsKeys} />
<Controller
control={control}
name="walletsKeys"
render={({ field: { onChange, value } }) => (
<LinkedAccountsCard value={value as any} onChange={onChange} />
)}
/>
<CommentsSettingsCard nostr_prv_key={query.data.me.nostr_prv_key} nostr_pub_key={query.data.me.nostr_pub_key} />
</div>
<div className="self-start sticky-side-element">
<SaveChangesCard
isLoading={mutationStatus.loading}
isDirty={isDirty}
onSubmit={handleSubmit(onSubmit)}
onCancel={() => reset()}
/>
</div>
</div>
)
}

View File

@@ -9,8 +9,8 @@ query MyProfilePreferences {
}
}
mutation UpdateUserPreferences($userKeys: [UserKeyInputType!]) {
updateUserPreferences(userKeys: $userKeys) {
mutation UpdateUserPreferences($walletsKeys: [UserKeyInputType!]) {
updateUserPreferences(userKeys: $walletsKeys) {
walletsKeys {
key
name

View File

@@ -140,7 +140,7 @@ export type Mutation = {
deleteStory: Maybe<Story>;
donate: Donation;
updateProfileDetails: Maybe<MyProfile>;
updateUserPreferences: Array<MyProfile>;
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<Array<UserKeyInputType> | UserKeyInputType>;
walletsKeys: InputMaybe<Array<UserKeyInputType> | 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<typeof useMyProfile
export type MyProfilePreferencesLazyQueryHookResult = ReturnType<typeof useMyProfilePreferencesLazyQuery>;
export type MyProfilePreferencesQueryResult = Apollo.QueryResult<MyProfilePreferencesQuery, MyProfilePreferencesQueryVariables>;
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<UpdateUser
* @example
* const [updateUserPreferencesMutation, { data, loading, error }] = useUpdateUserPreferencesMutation({
* variables: {
* userKeys: // value for 'userKeys'
* walletsKeys: // value for 'walletsKeys'
* },
* });
*/