This project uses Apollo GraphQL, a comprehensive GraphQL platform that provides a unified, graph-based layer to interact with your services.
Access Apollo Studio Dashboard to get full insight into the project's schema.
Using Codegen to generate types in a norm in Geyser, it saves tons of time spent on creating input and response types for all graphQL queries and mutations, specially as a single graphql query could have multiple instances with different expected repsonses. Codegen takes care of it all, generating easy to use pre-typed hooks for queries, lazyqueries, mutation and subscriptions.
Following command helps you in creating types and react hooks for every document.
yarn graph:generate staging
For example, running the command as yarn graph:generate staging
indicates staging
as environment.
This would use https://api.staging.geyser.fund/graphql
to validate and generate types for fragments, and functions for query, mutations and subscriptions.
Note: This process generates types and hooks for all documents located at src/graphql into src/types/generated
Using fragments during Codegen helps in generating types for fragments as well. This enhances development and ensures the availability of queried fields in React component props. To learn more about fragments click here.
Here is a sample fragment:
export const FRAGMENT_USER_ME = gql`
fragment UserMe on User {
id
username
email
}
`
The fragment above would generate a counterpart type like:
export type UserMeFragment = {
__typename?: 'User'
id: any
username: string
email?: string | null
}
A query called Me
using the previous fragment like this:
export const QUERY_ME = gql`
${FRAGMENT_USER_ME}
query Me {
me {
...UserMe
}
}
`
-
Will generate a hook named
useMeQuery
and anotheruseMeLazyQuery
. -
These can be used as alternatives for
useQuery(QUERY_ME)
anduseLazyQuery(useMeLazyQuery)
along with type benefits. -
Both will return responses typed with
UserMeFragment
, with the exact same parameters we put up in the query, saving time to create and map types.
A mutation called UpdateUser
like this:
export const MUTATION_UPDATE_USER = gql`
${FRAGMENT_USER_ME}
mutation UpdateUser($input: UpdateUserInput!) {
updateUser(input: $input) {
...UserMe
}
}
`
-
Will generate a hook named
useUpdateUserMutation
, and types namedUpdateUserInput
for input andUserMeFragment
for the response. -
This hook is a replacement for
useMutation(MUTATION_UPDATE_USER)
along with type benefits. -
This hook will already know what the input types should be. Similarly the response of the mutation will also be typed.
Sometimes, the GraphQL schema from our back-end staging
environment will be late to adopt changes from its development
environment that we know we'll want to be leveraging on the front-end. Apollo GraphQL allows us to account for this via client-side mocking.
This PR contains an example of how mocking can be achieved -- with the important caveat that the file at src/config/apollo-client/customClientTypeDefs
is intended for local use only, and is part of the project's .gitignore
specification.
This project has react-hook-form
installed, and it's transitioning to use on all the forms that require validation and other logic.
Some forms still have custom react state and validation, and it's okay for some features like search where it's only one or two fields.
Other forms that still use react state should be migrated to react-hook-form, given the nature that it handles validation out of the box using yup, and it has the form error and touched and dirty states available so we don't have to re-invent the wheel on this.
To setup a form you first need to use the form state from react-hook-form
:
const form = useForm({})
Values can be passed like
const form = useForm({ values: isEdit ? myCurrentData : DEFAULT_VALUES })
or default values like
const form = useForm({
values: isEdit ? myCurrentData : undefined,
defaultValues: DEFAULT_VALUES,
})
Import necessary things:
import * as yup from 'yup'
import { yupResolver } from '@hookform/resolvers/yup'
import { useForm } from 'react-hook-form'
Create a schema
const schema = yup.object({
myField: yup.string().required('This is required, pleb!'),
})
Create a component and use the schema inside yupResolver
const Component = () => {
const { handleSubmit } = useForm({ resolver: yupResolver(schema) })
const onSubmit = (values) => doSomething(values)
const onError = () => hadErrors()
return <form onSubmit={handleSubmit(onSubmit, onError)} />
}
We can then use the submit like this:
return (
<form onSubmit={form.handleSubmit((values) => doSomething(values))}>
<button type="submit" />
</form>
)
This button with type submit inside a form will make it submit on ENTER key pressed as well as clicking it and it's better for SEO as well.
Form components located at src/forms
are already setup to use with react-hook-form
together with chakra
by passing only the form's control
We've recently included Jotai as one of the modular tools for handling state managment. Jotai creates modular atoms and derived atoms that are accessible throughout the app through the atom.
Our strategy is to migrate a large part of context overheads to Jotai for more effective state management. This move aims to boost the speed of state updates and renders.
In any situation where a new state is needed across multiple scopes, we recommend using Jotai Atoms
. Jotai’s primitive and declarative API makes handling complex state needs more manageable. It facilitates splitting the global state into a collection of distributed but well synchronized atoms, bringing more efficient and maintainable state management to the project.
Images uploaded by creators in Geyser are automatically resized into three distinct sizes: small, medium, and large. These images share the same URL, differentiated by postfixes such as image_small.webp
, image_medium.webp
and image_large.webp
.
For instance, if a user uploads an image named 'profile.png', it's uploaded, and the returned value is the small image in the format https://geyserbucket...../profile_image_small.webp
. This process is automatic, allowing us to add the image directly to the database without manual manipulation.
However, there are situations where a larger image size is required, like in a project thumbnail popup. In such cases, the small image URL needs to be converted to a large image URL, i.e., https://geyserbucket...../profile_image_large.webp
.
To facilitate this, we've provided functions in the utils folder: toLargeImageUrl
, toMediumImageUrl
, and toSmallImageUrl
. These functions can be used to display images of different sizes as necessary. Even if the incoming image size is unknown, these functions act as filters, ensuring the correct image size renders.
We use storybook for showcasing all of the components, starting out with the base UI components like buttons, inputs, to complex ones like activity panel. This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
We use cypress end-to-end tests that run on the CI.
All the tests related code are housed inside the cypress
folder in the root.
e2e
folder contain the definition of the tests.
actions
folder houses, all of the actions that the user takes through the test.
assertions
folder houses, all of the validations that we do based on what is rendered by the react app.
fixtures
folder contains, any raw data thta might be used during the tests.
utils
folder has common utility functions.
support
folder contains configuration for supporting packages that we use along with cypress for running tests.