Browse Source

feat: User profile settings (again)

style/lint-staged
Dale 3 weeks ago
parent
commit
891d624270
Signed by: Deiru GPG Key ID: AA250C0277B927E1
  1. 1
      module.nix
  2. 1
      next.config.js
  3. 9
      package.json
  4. 3
      src/client/components/ImageUploader.tsx
  5. 19
      src/client/components/auth/Redirect.tsx
  6. 4
      src/client/components/form/FormGroup.tsx
  7. 34
      src/client/components/system/Page.tsx
  8. 19
      src/client/components/ui/Panel.tsx
  9. 2
      src/client/components/ui/ThemeSwitcher.tsx
  10. 7
      src/client/components/ui/UserMenu.tsx
  11. 26
      src/client/hooks/auth.tsx
  12. 29
      src/client/store/index.ts
  13. 28
      src/client/store/reducers.ts
  14. 56
      src/pages/_app.tsx
  15. 9
      src/pages/index.tsx
  16. 200
      src/pages/settings/profile.tsx
  17. 7
      src/server/graphql/directives/auth.tsx
  18. 39
      src/shared/graphql.ts
  19. 96
      src/shared/hooks.ts
  20. 7
      src/shared/media.ts
  21. 6
      src/shared/theme/components/index.ts
  22. 16
      src/shared/theme/components/input.ts
  23. 40
      src/shared/theme/components/textarea.ts
  24. 2
      src/shared/theme/index.ts
  25. 9
      yarn.lock

1
module.nix

@ -99,6 +99,7 @@ in {
serviceConfig.ExecStart = "${miracle-tv}/bin/miracle-client -p ${toString cfg.settings.client.port} -H ${cfg.settings.client.hostname}";
environment = {
NEXT_PUBLIC_API_URL = "https://${cfg.settings.url}/api/graphql";
NEXT_PUBLIC_MEDIA_URL = "https://${cfg.settings.url}/media";
NODE_ENV = cfg.settings.nodeEnv;
};
};

1
next.config.js

@ -4,5 +4,6 @@ module.exports = {
publicRuntimeConfig: {
// Will be available on both server and client
apiUrl: process.env.NEXT_PUBLIC_API_URL,
mediaUrl: process.env.NEXT_PUBLIC_MEDIA_URL,
},
};

9
package.json

@ -25,10 +25,6 @@
"@emotion/styled": "^11.3.0",
"@graphql-tools/schema": "^8.1.2",
"@graphql-tools/utils": "^8.1.2",
"@types/apollo-upload-client": "^14.1.0",
"@types/graphql-upload": "^8.0.6",
"@types/node": "^16.3.3",
"@types/uuid": "^8.3.1",
"apollo-server": "^2.24.0",
"apollo-server-express": "^2.24.0",
"apollo-upload-client": "^16.0.0",
@ -68,15 +64,19 @@
"@graphql-codegen/typescript-operations": "^1.18.4",
"@graphql-codegen/typescript-react-apollo": "^2.3.1",
"@graphql-codegen/typescript-resolvers": "1.19.1",
"@types/apollo-upload-client": "^14.1.0",
"@types/async": "^3.2.6",
"@types/bcrypt": "^5.0.0",
"@types/classnames": "^2.3.1",
"@types/express": "^4.17.11",
"@types/express-graphql": "^0.9.0",
"@types/glob": "^7.1.3",
"@types/graphql": "^14.5.0",
"@types/graphql-upload": "^8.0.6",
"@types/luxon": "^1.26.5",
"@types/md5": "^2.3.0",
"@types/module-alias": "^2.0.0",
"@types/node": "^16.3.3",
"@types/ramda": "^0.27.40",
"@types/react": "^17.0.14",
"@types/react-dom": "^17.0.9",
@ -85,6 +85,7 @@
"@types/redux-actions": "^2.6.2",
"@types/redux-persist": "^4.3.1",
"@types/rethinkdb": "^2.3.16",
"@types/uuid": "^8.3.1",
"@vercel/ncc": "^0.28.6",
"nodemon": "^2.0.7",
"prettier": "^2.3.0",

3
src/client/components/ImageUploader.tsx

@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useRef } from "react";
import React, { useCallback, useRef } from "react";
import { gql } from "@apollo/client";
import {
AspectRatio,
@ -117,6 +117,7 @@ export const ImageUploader = ({
borderStyle="dashed"
borderColor={color}
bgColor={bgColor}
mb={4}
>
<Heading size="sm" mb={2} color={color}>
Upload

19
src/client/components/auth/Redirect.tsx

@ -1,6 +1,7 @@
import { useCurrentUser } from "miracle-tv-client/hooks/auth";
import { useRouter } from "next/dist/client/router";
import { useEffect } from "react";
import { Flex, Spinner } from "@chakra-ui/react";
type Props = {
children: React.ReactNode | React.ReactNode[];
@ -8,13 +9,23 @@ type Props = {
export const AuthRedirect = ({ children }: Props) => {
const { push } = useRouter();
const { user, isUserLoading } = useCurrentUser();
const { currentUser, isUserLoading } = useCurrentUser();
useEffect(() => {
if (!isUserLoading && !user) {
if (!isUserLoading && !currentUser) {
push("/auth/login");
}
}, [isUserLoading, user, push]);
}, [isUserLoading, currentUser, push]);
return !isUserLoading && !!user && <>{children}</>;
// return !isUserLoading && !!currentUser && <>{children}</>;
return (
<>
{isUserLoading && (
<Flex w="100%" h="100%" justify="center" align="center">
<Spinner />
</Flex>
)}
{!isUserLoading && currentUser && children}
</>
);
};

4
src/client/components/form/FormGroup.tsx

@ -32,9 +32,9 @@ export const FormGroup = ({
...controlProps
}: Props) => {
return (
<FormControl id={name} {...controlProps} pt={0} mt={0}>
<FormControl id={name} pt={0} mt={0} {...controlProps}>
{!!label && !hideLabel && (
<FormLabel {...labelProps} pt={0} mt={0}>
<FormLabel pt={0} mt={0} mb={3.5} {...labelProps}>
{label}
</FormLabel>
)}

34
src/client/components/system/Page.tsx

@ -1,10 +1,16 @@
import { Box, BoxProps, useColorModeValue } from "@chakra-ui/react";
import {
Box,
BoxProps,
Flex,
Spinner,
useColorModeValue,
} from "@chakra-ui/react";
import React from "react";
type Props = BoxProps;
export const PageWrapper = (props: Props) => {
const bgColor = useColorModeValue("white", "secondary.600");
const bgColor = useColorModeValue("white", "secondary.500");
const color = useColorModeValue("text.light", "text.dark");
return (
<Box
@ -14,11 +20,31 @@ export const PageWrapper = (props: Props) => {
overflowY="auto"
color={color}
bgColor={bgColor}
paddingBottom="50px"
{...props}
/>
);
};
export const Page = (props: Props) => {
return <Box px={15} py={5} {...props} />;
type PageProps = {
isLoading?: boolean;
} & BoxProps;
export const Page = ({ children, isLoading, ...props }: PageProps) => {
return (
<Box px={15} py={5} {...props} position="relative">
{isLoading && (
<Flex
position="absolute"
justify="center"
align="center"
w="100%"
h="100%"
>
<Spinner size="lg" />
</Flex>
)}
{children}
</Box>
);
};

19
src/client/components/ui/Panel.tsx

@ -8,25 +8,13 @@ type Props = {
colorScheme?: "primary" | "secondary";
} & BoxProps;
type Colors = Record<
Props["colorScheme"],
{
bgColor: string;
color: string;
}
>;
export const Panel = ({ ...boxProps }: Props) => {
const bgColor = useColorModeValue("secondary.200", "secondary.400");
const color = useColorModeValue("secondaryText.light", "secondaryText.dark");
const boxShadow = useColorModeValue(
"1px 0px 63px -23px rgba(0,0,0,0.3)",
"0px 0px 15px 10px rgba(0,0,0,0.3)"
);
const bgColor = useColorModeValue("white", "secondary.400");
const color = useColorModeValue("secondary.500", "white");
const theme = useTheme();
const borderColor = transparentize(
useColorModeValue("primary.400", "primary.600"),
0.5
useColorModeValue(0.3, 0.5)
)(theme);
return (
@ -38,7 +26,6 @@ export const Panel = ({ ...boxProps }: Props) => {
borderWidth="1px"
borderStyle="solid"
transition="all 0.2s ease"
boxShadow={boxShadow}
borderColor={borderColor}
{...boxProps}
/>

2
src/client/components/ui/ThemeSwitcher.tsx

@ -5,7 +5,7 @@ import React from "react";
export const ThemeSwitcher = () => {
const { colorMode, toggleColorMode } = useColorMode();
return (
<Box onClick={toggleColorMode}>
<Box onClick={toggleColorMode} cursor="pointer">
{colorMode === "dark" && <MoonIcon mr={2} />}
{colorMode !== "dark" && <SunIcon mr={2} />}
<Switch

7
src/client/components/ui/UserMenu.tsx

@ -2,7 +2,6 @@ import React from "react";
import {
Button,
Flex,
Text,
Menu,
MenuButton,
MenuItem,
@ -51,7 +50,7 @@ const MenuLink = ({ url, label, icon }: LinkProps) => {
//
export const UserMenu = () => {
const { user, logout } = useCurrentUser();
const { currentUser, logout } = useCurrentUser();
const { colorMode, toggleColorMode } = useColorMode();
const activeButtonColor = useColorModeValue("primary.300", "primary.400");
@ -70,11 +69,11 @@ export const UserMenu = () => {
bg: activeButtonColor,
}}
>
<UserInfo user={user} />
<UserInfo user={currentUser} />
</MenuButton>
<MenuList>
<MenuLink
url={`/users/${user?.id}`}
url={`/users/${currentUser?.id}`}
label="Profile"
icon={<SettingsIcon aria-label="Settings" variant="ghost" mr={2} />}
/>

26
src/client/hooks/auth.tsx

@ -1,44 +1,26 @@
import { useCallback, useEffect, useMemo } from "react";
import { gql } from "@apollo/client";
import {
useCurrentUserFullLazyQuery,
useCurrentUserFullQuery,
} from "miracle-tv-shared/hooks";
import { CurrentUserFullQuery, User } from "miracle-tv-shared/graphql";
import { DateTime } from "luxon";
import { useRouter } from "next/dist/client/router";
import { useDispatch, useSelector } from "react-redux";
import { identity, path, prop, props } from "ramda";
import { actions } from "miracle-tv-client/store/reducers";
import { useCurrentUserFullQuery } from "miracle-tv-shared/hooks";
import { CurrentUserFullQuery } from "miracle-tv-shared/graphql";
type CurrentUserInfo = CurrentUserFullQuery["self"];
type LocalUserStorage = {
loading: boolean;
expiresAt: Date;
user: CurrentUserInfo;
};
type CurrentUserHookReturn = {
isUserLoading: boolean;
isUserCalled: boolean;
user: CurrentUserInfo;
currentUser: CurrentUserInfo;
logout: () => void;
updateUser: (user: CurrentUserInfo) => void;
};
export const useCurrentUser = (): CurrentUserHookReturn => {
const { push } = useRouter();
const {
data: { self } = {},
loading: isUserLoading,
called: isUserCalled,
refetch: loadUser,
} = useCurrentUserFullQuery({});
return {
user: self || null,
currentUser: self || null,
isUserLoading: isUserLoading,
isUserCalled,
logout: () => {},

29
src/client/store/index.ts

@ -1,29 +0,0 @@
import { persistReducer, persistStore, Storage } from "redux-persist";
import storage from "redux-persist/lib/storage";
import { createStore } from "redux";
import { reducers } from "./reducers";
const persistConfig = {
key: "root",
storage,
};
export const persistedReducer = persistReducer(persistConfig, reducers);
let store = createStore(
reducers,
// @ts-ignore
process.browser &&
// @ts-ignore
window &&
// @ts-ignore
window.__REDUX_DEVTOOLS_EXTENSION__ &&
// @ts-ignore
window.__REDUX_DEVTOOLS_EXTENSION__()
);
let persistor = persistStore(store as any);
const setupStore = () => {
return { store, persistor };
};
export default setupStore;

28
src/client/store/reducers.ts

@ -1,28 +0,0 @@
import { DateTime } from "luxon";
import { User } from "miracle-tv-shared/graphql";
import { createActions, handleActions } from "redux-actions";
type State = {
currentUser?: { loading: boolean; user: User; expiresAt: DateTime };
};
export const actions = createActions({
SET_USER: (state: State["currentUser"]) => ({ state }),
SET_LOADING: (state: State["currentUser"]["loading"]) => ({ state }),
});
export const reducers = handleActions<State>(
{
SET_USER: (_, { payload: { state } }: any) => ({
currentUser: state,
}),
SET_LOADING: (state: any, { payload: { state: loading } }: any) => ({
...state,
currentUser: {
...state.currentUser,
loading,
},
}),
},
{ currentUser: null }
);

56
src/pages/_app.tsx

@ -1,11 +1,11 @@
import App from "next/app";
import { Box, ChakraProvider, Flex, Spinner } from "@chakra-ui/react";
import { Global, css } from "@emotion/react";
import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";
import { Box, ChakraProvider, Flex } from "@chakra-ui/react";
import { css, Global } from "@emotion/react";
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import Head from "next/head";
import getConfig from "next/config";
import { any, propOr } from "ramda";
import { propOr } from "ramda";
import theme from "miracle-tv-shared/theme";
import React from "react";
@ -13,12 +13,8 @@ import { useRouter } from "next/dist/client/router";
import { createUploadLink } from "apollo-upload-client";
import { ShowcaseWrapper } from "miracle-tv-client/components/showcase/Wrapper";
import { PageWrapper } from "miracle-tv-client/components/system/Page";
import { Provider } from "react-redux";
import configureStore from "miracle-tv-client/store";
import { ThemeSwitcher } from "miracle-tv-client/components/ui/ThemeSwitcher";
const { store, persistor } = configureStore();
const env = process.env.NEXT_PUBLIC_ENV;
const { publicRuntimeConfig } = getConfig();
@ -53,14 +49,14 @@ const client = new ApolloClient({
link: authLink.concat(uploadLink),
});
const noNavbarRoutes = ["/auth/login", "/docs", "/"];
// const noNavbarRoutes = ["/auth/login", "/docs", "/"];
function MyApp({ Component, pageProps }: any): JSX.Element {
const router = useRouter();
const showNavbar = !any(
(path) => router.asPath.startsWith(path),
noNavbarRoutes
);
// const showNavbar = !any(
// (path) => router.asPath.startsWith(path),
// noNavbarRoutes
// );
const isShowcase = router.asPath.startsWith("/docs");
return (
<>
@ -81,25 +77,23 @@ function MyApp({ Component, pageProps }: any): JSX.Element {
`}
/>
<ChakraProvider theme={theme}>
<Provider store={store}>
<ApolloProvider client={client}>
{!isShowcase && (
<Flex h="100%" w="100%" direction="column">
<PageWrapper>
<Component {...pageProps} />
</PageWrapper>
</Flex>
)}
{isShowcase && (
<ShowcaseWrapper>
<ApolloProvider client={client}>
{!isShowcase && (
<Flex h="100%" w="100%" direction="column">
<PageWrapper>
<Component {...pageProps} />
</ShowcaseWrapper>
)}
<Box position="fixed" right={0} bottom={0} pb={4} pr={4}>
<ThemeSwitcher />
</Box>
</ApolloProvider>
</Provider>
</PageWrapper>
</Flex>
)}
{isShowcase && (
<ShowcaseWrapper>
<Component {...pageProps} />
</ShowcaseWrapper>
)}
<Box position="fixed" right={0} bottom={0} pb={4} pr={4}>
<ThemeSwitcher />
</Box>
</ApolloProvider>
</ChakraProvider>
</>
);

9
src/pages/index.tsx

@ -1,9 +1,9 @@
import { AuthRedirect } from "miracle-tv-client/components/auth/Redirect";
import { Box, Flex, Button } from "@chakra-ui/react";
import { Box, Flex, Button, Link } from "@chakra-ui/react";
import { signOut, useCurrentUser } from "miracle-tv-client/hooks/auth";
const Home = () => {
const { user } = useCurrentUser();
const { currentUser } = useCurrentUser();
return (
<AuthRedirect>
<Flex
@ -13,7 +13,10 @@ const Home = () => {
align="center"
direction="column"
>
<Box>Lmao, {user?.username}</Box>
<Box>Lmao, {currentUser?.username}</Box>
<Flex justify="space-between">
<Link href="/settings/profile">Settings</Link>
</Flex>
<Button mt={6} onClick={signOut}>
Sign Out
</Button>

200
src/pages/settings/profile.tsx

@ -0,0 +1,200 @@
import { AuthRedirect } from "miracle-tv-client/components/auth/Redirect";
import { Page } from "miracle-tv-client/components/system/Page";
import { Heading } from "@chakra-ui/layout";
import { Panel } from "miracle-tv-client/components/ui/Panel";
import { Form } from "react-final-form";
import { FormGroup } from "miracle-tv-client/components/form/FormGroup";
import { FormTextarea } from "miracle-tv-client/components/form/FormTextarea";
import { FormInput } from "miracle-tv-client/components/form/FormInput";
import { gql } from "@apollo/client";
import {
useUpdateUserSettingsProfileMutation,
useUserSettingsProfileQuery,
} from "miracle-tv-shared/hooks";
import { Box, Button, Flex, Link, Spinner, useToast } from "@chakra-ui/react";
import { UpdateSelfInput } from "miracle-tv-shared/graphql";
import { useCallback, useMemo } from "react";
import { omit } from "ramda";
import { ImageUploader } from "miracle-tv-client/components/ImageUploader";
import { ChevronLeftIcon } from "@chakra-ui/icons";
const userFragment = gql`
fragment UserSettingsProfileFragment on User {
id
username
displayName
emailHash
bio
avatar {
id
filename
encoding
mimetype
}
streamThumbnail {
id
filename
encoding
mimetype
}
header {
id
filename
encoding
mimetype
}
}
`;
gql`
query UserSettingsProfile {
self {
...UserSettingsProfileFragment
}
}
mutation UpdateUserSettingsProfile($input: UpdateSelfInput!) {
updateSelf(input: $input) {
...UserSettingsProfileFragment
}
}
${userFragment}
`;
const ProfilePage = () => {
const toast = useToast();
const { data, loading } = useUserSettingsProfileQuery();
const [updateProfileMutation, { loading: isUpdating }] =
useUpdateUserSettingsProfileMutation({
onCompleted: () =>
toast({ status: "success", title: "Updated user info!" }),
onError: () =>
toast({ status: "error", title: "Error updating user info." }),
});
const formData = useMemo(
() =>
data?.self
? {
avatar: data?.self?.avatar?.id,
header: data?.self?.header?.id,
streamThumbnail: data?.self?.streamThumbnail?.id,
...omit(
[
"__typename",
"id",
"username",
"emailHash",
"avatar",
"header",
"streamThumbnail",
],
data.self
),
}
: {},
[data?.self]
);
const updateProfile = useCallback(
(values: UpdateSelfInput) => {
updateProfileMutation({ variables: { input: values } });
},
[updateProfileMutation]
);
return (
<AuthRedirect>
<Page px={20}>
<Heading as="h2" size="lg" mb={6}>
<Link href="/" aria-label="back">
<ChevronLeftIcon size="xl" />
</Link>
Settings
</Heading>
{loading && <Spinner />}
{!loading && data?.self && (
<Form<UpdateSelfInput>
onSubmit={updateProfile}
initialValues={formData}
>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Flex>
<Box flex={2} mr={4}>
<Heading as="h3" size="md" mb={6}>
Profile
</Heading>
<Panel>
<FormInput
id="displayName"
name="displayName"
label="Display Name"
mb={4}
/>
<FormTextarea id="bio" name="bio" label="Bio" />
</Panel>
</Box>
<Box flex={4}>
<Heading as="h3" size="md" mb={6}>
Icons and Headers
</Heading>
<Panel pb={12}>
<Flex justify="space-between">
<FormGroup
name="avatar"
label="Profile Picture"
flex={2}
>
<ImageUploader
id="avatar"
name="avatar"
ratio={1}
aspectMaxH="200px"
aspectMaxW="200px"
/>
</FormGroup>
<FormGroup
name="streamThumbnail"
label="Stream Thumbnail"
flex={4}
>
<ImageUploader
id="streamThumbnail"
name="streamThumbnail"
ratio={16 / 9}
aspectMaxH="100%"
aspectMaxW="100%"
/>
</FormGroup>
</Flex>
<FormGroup
name="header"
label="Profile Header"
width="100%"
>
<ImageUploader
id="header"
name="header"
ratio={16 / 6}
aspectMaxH="100%"
aspectMaxW="100%"
/>
</FormGroup>
</Panel>
</Box>
</Flex>
<Flex justify="flex-end">
<Button type="submit" mt={6} isLoading={isUpdating}>
Update profile
</Button>
</Flex>
</form>
)}
</Form>
)}
</Page>
</AuthRedirect>
);
};
export default ProfilePage;

7
src/server/graphql/directives/auth.tsx

@ -1,7 +1,7 @@
import { getDirective, MapperKind, mapSchema } from "@graphql-tools/utils";
import * as gqlUtils from "@graphql-tools/utils";
import { defaultFieldResolver, GraphQLFieldConfig } from "graphql";
import { ResolverContext } from "miracle-tv-server/types/resolver";
import { all, any, identity, prop } from "ramda";
import { any, identity, prop } from "ramda";
import { checkRight } from "miracle-tv-server/db/acl/roles";
import { ServerError } from "miracle-tv-server/graphql/errors/general";
import {
@ -9,6 +9,9 @@ import {
AuthorizationError,
} from "miracle-tv-server/graphql/errors/auth";
const { MapperKind, mapSchema } = gqlUtils;
const { getDirective } = gqlUtils as any;
const roleGuard =
(schema: any, directiveName: string) =>
(fieldConfig: GraphQLFieldConfig<any, any>) => {

39
src/shared/graphql.ts

@ -892,3 +892,42 @@ export type SignUpMutation = (
& Pick<User, 'id' | 'username'>
) }
);
export type UserSettingsProfileFragmentFragment = (
{ __typename?: 'User' }
& Pick<User, 'id' | 'username' | 'displayName' | 'emailHash' | 'bio'>
& { avatar?: Maybe<(
{ __typename?: 'File' }
& Pick<File, 'id' | 'filename' | 'encoding' | 'mimetype'>
)>, streamThumbnail?: Maybe<(
{ __typename?: 'File' }
& Pick<File, 'id' | 'filename' | 'encoding' | 'mimetype'>
)>, header?: Maybe<(
{ __typename?: 'File' }
& Pick<File, 'id' | 'filename' | 'encoding' | 'mimetype'>
)> }
);
export type UserSettingsProfileQueryVariables = Exact<{ [key: string]: never; }>;
export type UserSettingsProfileQuery = (
{ __typename?: 'Query' }
& { self: (
{ __typename?: 'User' }
& UserSettingsProfileFragmentFragment
) }
);
export type UpdateUserSettingsProfileMutationVariables = Exact<{
input: UpdateSelfInput;
}>;
export type UpdateUserSettingsProfileMutation = (
{ __typename?: 'Mutation' }
& { updateSelf?: Maybe<(
{ __typename?: 'User' }
& UserSettingsProfileFragmentFragment
)> }
);

96
src/shared/hooks.ts

@ -58,6 +58,33 @@ export const CurrentUserFragmentDoc = gql`
}
}
`;
export const UserSettingsProfileFragmentFragmentDoc = gql`
fragment UserSettingsProfileFragment on User {
id
username
displayName
emailHash
bio
avatar {
id
filename
encoding
mimetype
}
streamThumbnail {
id
filename
encoding
mimetype
}
header {
id
filename
encoding
mimetype
}
}
`;
export const GetFileForUploaderDocument = gql`
query GetFileForUploader($id: ID!) {
fileInfo(id: $id) {
@ -275,4 +302,71 @@ export function useSignUpMutation(baseOptions?: Apollo.MutationHookOptions<Types
}
export type SignUpMutationHookResult = ReturnType<typeof useSignUpMutation>;
export type SignUpMutationResult = Apollo.MutationResult<Types.SignUpMutation>;
export type SignUpMutationOptions = Apollo.BaseMutationOptions<Types.SignUpMutation, Types.SignUpMutationVariables>;
export type SignUpMutationOptions = Apollo.BaseMutationOptions<Types.SignUpMutation, Types.SignUpMutationVariables>;
export const UserSettingsProfileDocument = gql`
query UserSettingsProfile {
self {
...UserSettingsProfileFragment
}
}
${UserSettingsProfileFragmentFragmentDoc}`;
/**
* __useUserSettingsProfileQuery__
*
* To run a query within a React component, call `useUserSettingsProfileQuery` and pass it any options that fit your needs.
* When your component renders, `useUserSettingsProfileQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useUserSettingsProfileQuery({
* variables: {
* },
* });
*/
export function useUserSettingsProfileQuery(baseOptions?: Apollo.QueryHookOptions<Types.UserSettingsProfileQuery, Types.UserSettingsProfileQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<Types.UserSettingsProfileQuery, Types.UserSettingsProfileQueryVariables>(UserSettingsProfileDocument, options);
}
export function useUserSettingsProfileLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<Types.UserSettingsProfileQuery, Types.UserSettingsProfileQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<Types.UserSettingsProfileQuery, Types.UserSettingsProfileQueryVariables>(UserSettingsProfileDocument, options);
}
export type UserSettingsProfileQueryHookResult = ReturnType<typeof useUserSettingsProfileQuery>;
export type UserSettingsProfileLazyQueryHookResult = ReturnType<typeof useUserSettingsProfileLazyQuery>;
export type UserSettingsProfileQueryResult = Apollo.QueryResult<Types.UserSettingsProfileQuery, Types.UserSettingsProfileQueryVariables>;
export const UpdateUserSettingsProfileDocument = gql`
mutation UpdateUserSettingsProfile($input: UpdateSelfInput!) {
updateSelf(input: $input) {
...UserSettingsProfileFragment
}
}
${UserSettingsProfileFragmentFragmentDoc}`;
export type UpdateUserSettingsProfileMutationFn = Apollo.MutationFunction<Types.UpdateUserSettingsProfileMutation, Types.UpdateUserSettingsProfileMutationVariables>;
/**
* __useUpdateUserSettingsProfileMutation__
*
* To run a mutation, you first call `useUpdateUserSettingsProfileMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useUpdateUserSettingsProfileMutation` returns a tuple that includes:
* - A mutate function that you can call at any time to execute the mutation
* - An object with fields that represent the current status of the mutation's execution
*
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
*
* @example
* const [updateUserSettingsProfileMutation, { data, loading, error }] = useUpdateUserSettingsProfileMutation({
* variables: {
* input: // value for 'input'
* },
* });
*/
export function useUpdateUserSettingsProfileMutation(baseOptions?: Apollo.MutationHookOptions<Types.UpdateUserSettingsProfileMutation, Types.UpdateUserSettingsProfileMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<Types.UpdateUserSettingsProfileMutation, Types.UpdateUserSettingsProfileMutationVariables>(UpdateUserSettingsProfileDocument, options);
}
export type UpdateUserSettingsProfileMutationHookResult = ReturnType<typeof useUpdateUserSettingsProfileMutation>;
export type UpdateUserSettingsProfileMutationResult = Apollo.MutationResult<Types.UpdateUserSettingsProfileMutation>;
export type UpdateUserSettingsProfileMutationOptions = Apollo.BaseMutationOptions<Types.UpdateUserSettingsProfileMutation, Types.UpdateUserSettingsProfileMutationVariables>;

7
src/shared/media.ts

@ -1,3 +1,8 @@
import getConfig from "next/config";
const { publicRuntimeConfig } = getConfig();
const mediaUrl = publicRuntimeConfig.mediaUrl || "http://localhost:4000/media";
export const getMediaURL = (filename: string): string => {
return `${process.env.NEXT_PUBLIC_MEDIA_URL}/${filename}`;
return `${mediaUrl}/${filename}`;
};

6
src/shared/theme/components/index.ts

@ -1,5 +1,8 @@
import { buttonStyles as Button } from "miracle-tv-shared/theme/components/button";
import { inputStyles as Input } from "miracle-tv-shared/theme/components/input";
import {
inputStyles as Input,
formLabelStyles as FormLabel,
} from "miracle-tv-shared/theme/components/input";
import { textareaStyles as Textarea } from "miracle-tv-shared/theme/components/textarea";
import { menuStyles as Menu } from "miracle-tv-shared/theme/components/menu";
import { switchStyles as Switch } from "miracle-tv-shared/theme/components/switch";
@ -9,6 +12,7 @@ import { tabsStyles as Tabs } from "miracle-tv-shared/theme/components/tabs";
const components = {
Button,
Input,
FormLabel,
Menu,
Switch,
Textarea,

16
src/shared/theme/components/input.ts

@ -1,14 +1,22 @@
import { mode, transparentize } from "@chakra-ui/theme-tools";
export const formLabelStyles = {
baseStyles: (props: any) => ({
color: mode("secondary.500", "white")(props),
}),
};
export const inputStyles = {
variants: {
solid: (props: any) => {
const bgColor = mode("white", "secondary.500")(props);
const borderColor = mode("primary.500", "primary.200")(props);
const borderColorFocus = mode("secondary.500", "secondary.200")(props);
const shadowColor = transparentize(
const borderColorFocus = transparentize(
mode("primary.500", "primary.200")(props),
mode(0.9, 0.4)(props)
mode(0.3, 0.5)(props)
)(props.theme);
const borderColor = transparentize(
mode("primary.400", "secondary.200")(props),
mode(0.3, 0.5)(props)
)(props.theme);
return {
field: {

40
src/shared/theme/components/textarea.ts

@ -1,23 +1,33 @@
import cn from "classnames";
import { mode, transparentize } from "@chakra-ui/theme-tools";
export const textareaStyles = {
variants: {
solid: ({ colorScheme }: any) => {
solid: (props: any) => {
const bgColor = mode("white", "secondary.500")(props);
const borderColorFocus = transparentize(
mode("primary.500", "primary.200")(props),
mode(0.3, 0.5)(props)
)(props.theme);
const borderColor = transparentize(
mode("primary.400", "secondary.200")(props),
mode(0.3, 0.5)(props)
)(props.theme);
return {
bgColor: cn({
"secondary.400": colorScheme === "secondary",
"secondary.600": colorScheme === "primary",
}),
borderRadius: "0px",
borderWidth: "2px",
background: "none",
backgroundColor: bgColor,
borderColor,
borderRadius: "6px",
borderWidth: "1px",
borderStyle: "solid",
borderColor: cn({
"primary.500": colorScheme === "primary",
"secondary.400": colorScheme === "secondary",
}),
color: cn({
white: colorScheme === "primary" || colorScheme === "secondary",
}),
transition: "all 0.5s ease-in",
py: 4,
_placeholder: {
color: mode("secondary.500", "secondary.200")(props),
},
_focus: {
borderColor: borderColorFocus,
transition: "all 0.5s ease-out",
},
};
},
},

2
src/shared/theme/index.ts

@ -13,7 +13,7 @@ const breakpoints = createBreakpoints({
const theme = extendTheme({
colors,
components,
components: components as any,
breakpoints,
config: {
initialColorMode: "dark",

9
yarn.lock

@ -1988,6 +1988,13 @@
"@types/koa" "*"
graphql "^15.3.0"
"@types/graphql@^14.5.0":
version "14.5.0"
resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-14.5.0.tgz#a545fb3bc8013a3547cf2f07f5e13a33642b75d6"
integrity sha512-MOkzsEp1Jk5bXuAsHsUi6BVv0zCO+7/2PTiZMXWDSsMXvNU6w/PLMQT2vHn8hy2i0JqojPz1Sz6rsFjHtsU0lA==
dependencies:
graphql "*"
"@types/hoist-non-react-statics@^3.3.0":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
@ -4405,7 +4412,7 @@ graphql-ws@^4.4.1:
resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-4.9.0.tgz#5cfd8bb490b35e86583d8322f5d5d099c26e365c"
integrity sha512-sHkK9+lUm20/BGawNEWNtVAeJzhZeBg21VmvmLoT5NdGVeZWv5PdIhkcayQIAgjSyyQ17WMKmbDijIPG2On+Ag==
graphql@^15.3.0, graphql@^15.5.1:
graphql@*, graphql@^15.3.0, graphql@^15.5.1:
version "15.5.1"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.1.tgz#f2f84415d8985e7b84731e7f3536f8bb9d383aad"
integrity sha512-FeTRX67T3LoE3LWAxxOlW2K3Bz+rMYAC18rRguK4wgXaTZMiJwSUwDmPFo3UadAKbzirKIg5Qy+sNJXbpPRnQw==

Loading…
Cancel
Save