Compare commits

...

2 Commits

Author SHA1 Message Date
ryan aac4a2d14c chore: switch classnames for clsx 2023-08-31 00:42:21 +08:00
ryan d8d10f908e chore: update dependencies 2023-08-31 00:39:27 +08:00
33 changed files with 2686 additions and 2883 deletions

View File

@ -15,7 +15,7 @@
".microrc.development": "jsonc"
},
"tailwindCSS.experimental.classRegex": [
["classNames\\(([^)]*)\\)", "'([^']*)'", "\"([^']*)\""],
["clsx\\(([^)]*)\\)", "'([^']*)'", "\"([^']*)\""],
[":\\s*?[\"'`]([^\"'`]*).*?,"],
["baseStyle.=.[\"'`]([^\"'`]*)"],
// Enum = 'value'

View File

@ -24,10 +24,12 @@ RUN npm i -g pnpm
COPY --from=deps /usr/src/micro .
COPY . .
# install all deps
RUN pnpm install --offline --frozen-lockfile
# build everthing
RUN pnpm build
# RUN cd packages/api && pnpm prune --prod
# use "pnpm deploy" to prune the api into a smaller package
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
cd packages/api && pnpm --filter @ryanke/micro-api --prod deploy pruned
@ -46,11 +48,11 @@ RUN apk add --no-cache ffmpeg
COPY --from=builder /usr/src/micro/packages/web/public ./packages/web/public
COPY --from=builder /usr/src/micro/packages/web/next.config.js ./packages/web/next.config.js
# copy web server
# copy web
COPY --from=builder --chown=node:node /usr/src/micro/packages/web/.next/standalone/ ./
COPY --from=builder --chown=node:node /usr/src/micro/packages/web/.next/static ./packages/web/.next/static/
# # copy api
# copy api
COPY --from=builder --chown=node:node /usr/src/micro/packages/api/pruned ./packages/api

View File

@ -17,6 +17,6 @@
"clean": "rm -rf ./packages/*/{tsconfig.tsbuildinfo,lib,dist,yarn-error.log,.next}"
},
"devDependencies": {
"turbo": "1.8.8"
"turbo": "1.10.13"
}
}
}

View File

@ -16,72 +16,71 @@
"test": "vitest run"
},
"dependencies": {
"@fastify/cookie": "^8.3.0",
"@fastify/helmet": "^10.1.0",
"@fastify/multipart": "^7.5.0",
"@fastify/cookie": "^9.0.4",
"@fastify/helmet": "^11.0.0",
"@fastify/multipart": "^7.7.3",
"@jenyus-org/graphql-utils": "^1.5.0",
"@mercuriusjs/gateway": "^1.2.0",
"@mikro-orm/core": "^5.6.16",
"@mikro-orm/migrations": "^5.6.16",
"@mikro-orm/nestjs": "^5.1.8",
"@mikro-orm/postgresql": "^5.6.16",
"@nestjs/common": "^9.3.12",
"@nestjs/core": "^9.3.12",
"@nestjs/graphql": "^11.0.4",
"@nestjs/jwt": "^10.0.3",
"@nestjs/mercurius": "^11.0.3",
"@nestjs/passport": "^9.0.3",
"@nestjs/platform-fastify": "^9.3.12",
"@nestjs/schedule": "^2.2.1",
"@mikro-orm/core": "^5.7.14",
"@mikro-orm/migrations": "^5.7.14",
"@mikro-orm/nestjs": "^5.2.1",
"@mikro-orm/postgresql": "^5.7.14",
"@nestjs/common": "^10.2.2",
"@nestjs/core": "^10.2.2",
"@nestjs/graphql": "^12.0.8",
"@nestjs/jwt": "^10.1.0",
"@nestjs/mercurius": "^12.0.4",
"@nestjs/passport": "^10.0.1",
"@nestjs/platform-fastify": "^10.2.2",
"@nestjs/schedule": "^3.0.3",
"@ryanke/venera": "^1.0.5",
"fastify": "^4.15.0",
"passport": "^0.6.0",
"fluent-ffmpeg": "^2.1.2",
"rxjs": "^7.8.1",
"bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"bcryptjs": "^2.4.3",
"graphql": "^16.6.0",
"fastify": "^4.22.0",
"fluent-ffmpeg": "^2.1.2",
"graphql": "^16.8.0",
"mercurius": "^13.1.0",
"mime-types": "^2.1.35",
"nodemailer": "^6.9.1",
"nodemailer": "^6.9.4",
"otplib": "^12.0.1",
"mercurius": "^12.2.0",
"passport": "^0.6.0",
"passport-jwt": "^4.0.1",
"stream-size": "^0.0.6",
"sharp": "^0.32.0"
"sharp": "^0.32.5",
"stream-size": "^0.0.6"
},
"devDependencies": {
"@mikro-orm/cli": "^5.6.16",
"@swc/core": "^1.3.46",
"@mikro-orm/cli": "^5.7.14",
"@swc/core": "^1.3.80",
"@sylo-digital/scripts": "^1.0.12",
"@types/bcryptjs": "^2.4.2",
"@types/bcryptjs": "^2.4.3",
"@types/bytes": "^3.1.1",
"@types/dedent": "^0.7.0",
"@types/fluent-ffmpeg": "^2.1.21",
"@types/luxon": "^3.3.0",
"@types/luxon": "^3.3.1",
"@types/mime-types": "^2.1.1",
"@types/ms": "^0.7.31",
"@types/node": "^18.15.11",
"@types/nodemailer": "^6.4.7",
"@types/passport-jwt": "^3.0.8",
"@types/sharp": "^0.31.1",
"@types/nodemailer": "^6.4.9",
"@types/passport-jwt": "^3.0.9",
"bytes": "^3.1.2",
"content-range": "^2.0.2",
"dedent": "^0.7.0",
"dedent": "^1.5.1",
"escape-string-regexp": "^5.0.0",
"file-type": "^18.2.1",
"handlebars": "^4.7.7",
"file-type": "^18.5.0",
"handlebars": "^4.7.8",
"istextorbinary": "^6.0.0",
"luxon": "^3.3.0",
"luxon": "^3.4.2",
"ms": "^3.0.0-canary.1",
"nanoid": "^4.0.2",
"normalize-url": "^8.0.0",
"pretty-bytes": "^6.1.0",
"pretty-bytes": "^6.1.1",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.0",
"ts-node": "^10.9.1",
"tsup": "^6.7.0",
"typescript": "^5.0.3",
"vitest": "^0.29.8"
"tsup": "^7.2.0",
"typescript": "^5.2.2",
"vitest": "^0.34.3"
},
"mikro-orm": {
"useTsNode": true,

View File

@ -49,9 +49,9 @@ async function bootstrap() {
})
);
await app.register(fastifyCookie);
await app.register(fastifyHelmet.default);
await app.register(fastifyMultipart.default, {
await app.register(fastifyCookie as any);
await app.register(fastifyHelmet.default as any);
await app.register(fastifyMultipart.default as any, {
limits: {
fieldNameSize: 100,
fieldSize: 100,

View File

@ -6,6 +6,6 @@ import { getRequest } from '../../../helpers/get-request.js';
@Injectable()
export class JWTAuthGuard extends AuthGuard('jwt') {
getRequest(context: ExecutionContext) {
return getRequest(context);
return getRequest(context) as any;
}
}

View File

@ -5,6 +5,11 @@ module.exports = {
experimental: {
outputFileTracingRoot: path.join(__dirname, '../../'),
},
eslint: {
// todo: eslint is broken with typescript 5.2 and every file errors out,
// this is temporary to get it to build with eslint broken.
ignoreDuringBuilds: true,
},
async redirects() {
return [
{

View File

@ -15,45 +15,45 @@
"generate": "graphql-codegen --config codegen.yml"
},
"dependencies": {
"@apollo/client": "^3.7.10",
"@headlessui/react": "^1.7.13",
"@apollo/client": "^3.8.1",
"@headlessui/react": "^1.7.17",
"@ryanke/pandora": "^0.0.9",
"@tailwindcss/typography": "^0.5.9",
"autoprefixer": "^10.4.14",
"classnames": "^2.3.2",
"concurrently": "^8.0.1",
"autoprefixer": "^10.4.15",
"clsx": "^2.0.0",
"concurrently": "^8.2.1",
"copy-to-clipboard": "^3.3.3",
"dayjs": "^1.11.7",
"dayjs": "^1.11.9",
"deepmerge": "^4.3.1",
"formik": "^2.2.9",
"formik": "^2.4.3",
"generate-avatar": "1.4.10",
"graphql": "^16.6.0",
"graphql": "^16.8.0",
"http-status-codes": "^2.2.0",
"lodash": "^4.17.21",
"nanoid": "^4.0.2",
"next": "13.2.4",
"postcss": "^8.4.21",
"prism-react-renderer": "^1.3.5",
"next": "13.4.19",
"postcss": "^8.4.29",
"prism-react-renderer": "^2.0.6",
"qrcode.react": "^3.1.0",
"react": "18.2.0",
"react-dom": "^18.1.0",
"react-feather": "^2.0.9",
"react-markdown": "^8.0.6",
"react-markdown": "^8.0.7",
"remark-gfm": "^3.0.1",
"swr": "^2.1.1",
"tailwindcss": "^3.3.1",
"yup": "^1.0.2"
"swr": "^2.2.2",
"tailwindcss": "^3.3.3",
"yup": "^1.2.0"
},
"devDependencies": {
"@graphql-codegen/cli": "^3.2.2",
"@graphql-codegen/typescript": "3.0.2",
"@graphql-codegen/typescript-operations": "3.0.2",
"@graphql-codegen/cli": "^5.0.0",
"@graphql-codegen/typescript": "4.0.1",
"@graphql-codegen/typescript-operations": "4.0.1",
"@graphql-codegen/typescript-react-apollo": "3.3.7",
"@sylo-digital/scripts": "^1.0.12",
"@types/lodash": "^4.14.192",
"@types/lodash": "^4.14.197",
"@types/node": "^18.15.11",
"@types/react": "^18.0.31",
"prettier": "^2.8.7",
"typescript": "^5.0.3"
"@types/react": "^18.2.21",
"prettier": "^3.0.3",
"typescript": "^5.2.2"
}
}

View File

@ -1,5 +1,5 @@
/* eslint-disable react/no-danger */
import classNames from 'classnames';
import clsx from 'clsx';
import * as avatar from 'generate-avatar';
import type { FC } from 'react';
import { useEffect, useMemo, useRef } from 'react';
@ -10,7 +10,7 @@ export interface AvatarProps {
}
export const Avatar: FC<AvatarProps> = (props) => {
const classes = classNames('overflow-hidden rounded-full select-none', props.className);
const classes = clsx('overflow-hidden rounded-full select-none', props.className);
const containerRef = useRef<HTMLDivElement>(null);
const svg = useMemo(() => {
return avatar.generateFromString(props.userId);

View File

@ -1,5 +1,5 @@
import { Menu } from '@headlessui/react';
import classNames from 'classnames';
import clsx from 'clsx';
import type { FC, ReactNode } from 'react';
import { Fragment } from 'react';
import { Link } from '../link';
@ -13,12 +13,12 @@ export interface DropdownTabProps {
export const DropdownTab: FC<DropdownTabProps> = ({ href, className, children, onClick }) => {
const props = href ? { as: Link, href: href } : { as: Fragment };
const base = classNames('px-3 py-2 my-1 text-gray-400 transition ease-in-out border-none cursor-pointer', className);
const base = clsx('px-3 py-2 my-1 text-gray-400 transition ease-in-out border-none cursor-pointer', className);
return (
<Menu.Item {...(props as any)}>
{({ active }) => (
<div className={classNames(base, active && 'text-white bg-dark-800')} onClick={onClick}>
<div className={clsx(base, active && 'text-white bg-dark-800')} onClick={onClick}>
{children}
</div>
)}

View File

@ -1,5 +1,5 @@
import { Menu, Transition } from '@headlessui/react';
import classNames from 'classnames';
import clsx from 'clsx';
import type { FC, ReactNode } from 'react';
import React, { Fragment } from 'react';
@ -10,7 +10,7 @@ export interface DropdownProps {
}
export const Dropdown: FC<DropdownProps> = ({ trigger, children, className }) => {
const itemsClasses = classNames(
const itemsClasses = clsx(
'absolute right-0 mt-2 overflow-y-auto rounded-md shadow-2xl bg-dark-300 focus:outline-none max-h-56 min-w-[10em]',
className
);

View File

@ -1,9 +1,9 @@
import classNames from 'classnames';
import clsx from 'clsx';
import { BASE_EMBED_CLASSES, MAX_HEIGHT } from '../embed';
import type { Embeddable } from '../embeddable';
export const EmbedDefault = ({ data }: { data: Embeddable }) => {
const classes = classNames(
const classes = clsx(
'flex flex-col items-center justify-center w-full select-none h-44',
BASE_EMBED_CLASSES,
MAX_HEIGHT

View File

@ -1,12 +1,12 @@
import classNames from 'classnames';
import clsx from 'clsx';
import Head from 'next/head';
import { Fragment } from 'react';
import { BASE_EMBED_CLASSES, MAX_HEIGHT } from '../embed';
import type { Embeddable } from '../embeddable';
export const EmbedImage = ({ data }: { data: Embeddable }) => {
const imageClasses = classNames(`object-contain`, MAX_HEIGHT);
const containerClasses = classNames(
const imageClasses = clsx(`object-contain`, MAX_HEIGHT);
const containerClasses = clsx(
'flex items-center justify-center relative overflow-hidden',
BASE_EMBED_CLASSES,
MAX_HEIGHT

View File

@ -4,13 +4,13 @@ import { PageLoader } from '../../page-loader';
import { EmbedDefault } from './embed-default';
import type { Embeddable } from '../embeddable';
import { textFetcher } from '../text-fetcher';
import classNames from 'classnames';
import clsx from 'clsx';
import { BASE_EMBED_CLASSES } from '../embed';
export const EmbedMarkdown = ({ data }: { data: Embeddable }) => {
const swrContent = useSWR<string>(data.content ? null : data.paths.direct, { fetcher: textFetcher });
const content = data.content ?? swrContent;
const classes = classNames('p-4', BASE_EMBED_CLASSES);
const classes = clsx('p-4', BASE_EMBED_CLASSES);
if (content.error) {
return <EmbedDefault data={data} />;

View File

@ -1,9 +1,9 @@
import classNames from 'classnames';
import clsx from 'clsx';
import { BASE_EMBED_CLASSES, MAX_HEIGHT } from '../embed';
import type { Embeddable } from '../embeddable';
export const EmbedVideo = ({ file }: { file: Embeddable }) => {
const classes = classNames('outline-none', BASE_EMBED_CLASSES, MAX_HEIGHT);
const classes = clsx('outline-none', BASE_EMBED_CLASSES, MAX_HEIGHT);
return (
<video
controls

View File

@ -1,5 +1,5 @@
import { Button, ButtonStyle, Container, useAsync, useOnClickOutside, useToasts } from '@ryanke/pandora';
import classNames from 'classnames';
import clsx from 'clsx';
import { Fragment, memo, useRef, useState } from 'react';
import { Crop } from 'react-feather';
import { useResendVerificationEmailMutation } from '../../generated/graphql';
@ -19,7 +19,7 @@ export const Header = memo(() => {
const [email, setEmail] = useState('');
const createToast = useToasts();
const [resent, setResent] = useState(false);
const classes = classNames(
const classes = clsx(
'relative z-20 flex items-center justify-between h-16 my-auto transition',
paths.loading && 'pointer-events-none invisible'
);

View File

@ -1,4 +1,4 @@
import classNames from 'classnames';
import clsx from 'clsx';
import type { FormikContextType } from 'formik';
import { FormikContext } from 'formik';
import type { ReactNode } from 'react';
@ -41,7 +41,7 @@ export function InputContainer<T extends InputChildPropsBase>({
const formik = useContext<FormikContextType<any>>(FormikContext);
const errorMessage = !!(formik && id && formik.touched[id]) && (formik.errors[id] as string);
if (errorMessage) style = InputStyle.Error;
const childClasses = classNames(
const childClasses = clsx(
'w-full h-full px-3 py-2 rounded outline-none border transition duration-75',
maxHeight && 'max-h-[calc(2.65em-2px)]',
style,

View File

@ -1,4 +1,4 @@
import classNames from 'classnames';
import clsx from 'clsx';
import type { SelectHTMLAttributes } from 'react';
import React from 'react';
import { ChevronDown } from 'react-feather';
@ -11,7 +11,7 @@ export const Select = React.forwardRef<HTMLSelectElement, SelectProps>(({ classN
return (
<InputContainer className={className} childProps={delegated}>
{({ childClasses, ...rest }) => {
const classes = classNames(className, childClasses, 'appearance-none pr-8');
const classes = clsx(className, childClasses, 'appearance-none pr-8');
return (
<div className="relative select-none">
<select className={classes} ref={ref} {...rest}>

View File

@ -1,4 +1,4 @@
import classNames from 'classnames';
import clsx from 'clsx';
import type { TextareaHTMLAttributes } from 'react';
import React from 'react';
import type { InputChildProps } from './container';
@ -10,7 +10,7 @@ export const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(({
return (
<InputContainer className={className} maxHeight={false} childProps={delegated}>
{({ childClasses, ...rest }) => {
const classes = classNames(childClasses, 'h-[50vh]');
const classes = clsx(childClasses, 'h-[50vh]');
return <textarea {...rest} className={classes} ref={ref} />;
}}
</InputContainer>

View File

@ -1,4 +1,4 @@
import classNames from 'classnames';
import clsx from 'clsx';
import type { Language } from 'prism-react-renderer';
import { Fragment, memo } from 'react';
import ReactMarkdown from 'react-markdown';
@ -8,7 +8,7 @@ import { SyntaxHighlighter } from './syntax-highlighter/syntax-highlighter';
const LANGUAGE_REGEX = /(^| )language-(?<language>.+)$/u;
export const Markdown = memo<{ children: string; className?: string }>(({ children, className }) => {
const classes = classNames(
const classes = clsx(
'prose prose-invert max-w-none',
// remove "" quotes from blockquotes
'prose-p:before:content-none prose-p:after:content-none',

View File

@ -1,4 +1,4 @@
import classNames from 'classnames';
import clsx from 'clsx';
import type { FC } from 'react';
import { Fragment } from 'react';
import { Ping } from './ping';
@ -18,7 +18,7 @@ export const Steps: FC<StepsProps> = ({ steps, stepIndex }) => {
{index !== 0 && <div className="h-px w-8 mx-4 bg-gray-800" />}
<div className="flex items-center gap-2">
<Ping active={isActive} />
<div className={classNames(!isActive && 'text-gray-400')}>{step}</div>
<div className={clsx(!isActive && 'text-gray-400')}>{step}</div>
</div>
</Fragment>
);

View File

@ -20,35 +20,33 @@ export const SyntaxHighlighterControls = memo<SyntaxHighlighterControlsProps>(
};
return (
<div className="absolute right-0 top-0">
<div className="fixed -translate-x-full flex items-center gap-2 z-10 bg-black bg-opacity-75 hover:bg-opacity-100 rounded-bl-lg pl-2 pb-2">
<Menu as="div" className="relative">
<Menu.Button className="text-xs flex items-center gap-1 text-gray-500 hover:text-white transition pt-2 ">
{language} <ChevronDown className="h-4 w-4" />
</Menu.Button>
<Menu.Items className="absolute top-full mt-3 bg-gray-900 text-sm max-h-44 overflow-y-scroll text-xs right-0 rounded">
{languages.map((language) => (
<Menu.Item
as="div"
key={language.name}
className="text-gray-400 hover:text-white transition cursor-pointer truncate px-3 py-1 hover:bg-gray-800"
onClick={() => {
onLanguageChange(language.key as Language);
}}
>
{language.name}
</Menu.Item>
))}
</Menu.Items>
</Menu>
<button
type="button"
className="text-xs text-gray-500 hover:text-white transition pr-3 pt-2"
onClick={copyContent}
>
Copy
</button>
</div>
<div className="absolute right-0 top-0 flex items-center gap-2 z-10 bg-black bg-opacity-75 hover:bg-opacity-100 rounded-bl-lg pl-2 pb-2">
<Menu as="div" className="relative">
<Menu.Button className="text-xs flex items-center gap-1 text-gray-500 hover:text-white transition pt-2 ">
{language} <ChevronDown className="h-4 w-4" />
</Menu.Button>
<Menu.Items className="absolute top-full mt-3 bg-gray-900 text-sm max-h-44 overflow-y-scroll text-xs right-0 rounded">
{languages.map((language) => (
<Menu.Item
as="div"
key={language.name}
className="text-gray-400 hover:text-white transition cursor-pointer truncate px-3 py-1 hover:bg-gray-800"
onClick={() => {
onLanguageChange(language.key as Language);
}}
>
{language.name}
</Menu.Item>
))}
</Menu.Items>
</Menu>
<button
type="button"
className="text-xs text-gray-500 hover:text-white transition pr-3 pt-2"
onClick={copyContent}
>
Copy
</button>
</div>
);
}

View File

@ -1,6 +1,6 @@
import classNames from 'classnames';
import clsx from 'clsx';
import type { Language } from 'prism-react-renderer';
import Highlight, { defaultProps } from 'prism-react-renderer';
import { Highlight } from 'prism-react-renderer';
import type { HTMLProps } from 'react';
import { memo, useState } from 'react';
import { theme } from './prism-theme';
@ -18,36 +18,26 @@ export const SyntaxHighlighter = memo<SyntaxHighlighterProps>(
const trimmed = children.trim();
return (
<Highlight {...defaultProps} theme={theme} code={trimmed} language={language}>
<Highlight theme={theme} code={trimmed} language={language}>
{({ className: highlighterClasses, style, tokens, getLineProps, getTokenProps }) => {
const containerClasses = classNames(
console.log(highlighterClasses);
const containerClasses = clsx(
'text-left overflow-x-auto h-full relative',
highlighterClasses,
additionalClasses
);
return (
<pre className={containerClasses} style={{ ...style, whiteSpace: 'nowrap', overflowX: 'auto' }} {...rest}>
<pre className={containerClasses} style={{ background: 'black' }}>
<SyntaxHighlighterControls language={language} onLanguageChange={setLanguage} content={children} />
{tokens.map((line, index) => {
const props = getLineProps({ line, key: index });
const classes = classNames(props.className, 'table-row');
return (
// handled by getLineProps
// eslint-disable-next-line react/jsx-key
<div {...props} className={classes}>
<span className="table-cell px-1 text-sm text-gray-500 select-none">{index + 1}</span>
<span className="table-cell pl-1">
{line.map((token, key) => (
// handled by getTokenProps
// eslint-disable-next-line react/jsx-key
<span {...getTokenProps({ token, key })} />
))}
</span>
</div>
);
})}
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line })}>
<span className="text-sm text-gray-500 px-2">{i + 1}</span>
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token })} />
))}
</div>
))}
</pre>
);
}}

View File

@ -1,4 +1,4 @@
import classNames from 'classnames';
import clsx from 'clsx';
export interface ToggleOption<T = unknown> {
label: string;
@ -22,7 +22,7 @@ export function Toggle<T extends string | number | boolean>({
<div className="inline-flex items-center overflow-hidden select-none bg-black text-gray-400 rounded-full">
{options.map((item) => {
const active = item.value === selected;
const classes = classNames(
const classes = clsx(
'rounded-full px-4 py-1 text-sm cursor-pointer h-full',
active ? 'text-white' : 'hover:bg-dark-200',
active && backgroundColour

View File

@ -1,4 +1,4 @@
import classNames from 'classnames';
import clsx from 'clsx';
import type { HTMLAttributes } from 'react';
import { forwardRef } from 'react';
import { Avatar } from './avatar';
@ -10,7 +10,7 @@ export interface UserPillProps extends HTMLAttributes<HTMLDivElement> {
export const UserPill = forwardRef<HTMLDivElement, UserPillProps>(
({ userId: id, username, className, ...rest }, ref) => {
const classes = classNames(
const classes = clsx(
'flex items-center px-2 py-1 transition rounded-full shadow-lg cursor-pointer select-none align-center bg-dark-600 hover:bg-dark-900 hover:text-white',
className
);

View File

@ -1,5 +1,5 @@
import { Container, Spinner } from '@ryanke/pandora';
import classNames from 'classnames';
import clsx from 'clsx';
import { Fragment, useState } from 'react';
import { Download } from 'react-feather';
import { Section } from '../../components/section';
@ -93,7 +93,7 @@ export const ConfigGenerator = () => {
<div className="grid grid-cols-2 md:grid-cols-3 gap-2 mt-2">
{config.data.hosts.map((host) => {
const isSelected = selectedHosts.includes(host.normalised);
const classes = classNames(
const classes = clsx(
'rounded px-2 py-1 truncate transition border border-transparent',
isSelected && 'bg-purple-600 text-white',
!isSelected && 'text-gray-400 bg-dark-100 hover:bg-dark-200 hover:text-white'
@ -124,7 +124,7 @@ export const ConfigGenerator = () => {
<button
type="submit"
onClick={download}
className={classNames(
className={clsx(
'mt-8 ml-auto flex items-center gap-1',
downloadable ? 'text-purple-400 hover:underline' : 'text-gray-700 cursor-not-allowed'
)}

View File

@ -1,4 +1,4 @@
import classNames from 'classnames';
import clsx from 'clsx';
import { memo } from 'react';
import { Link } from '../../../components/link';
import { Time } from '../../../components/time';
@ -13,7 +13,7 @@ export const PasteCard = memo<PasteCardProps>(({ paste }) => {
const user = useUser();
const showUrl = !paste.encrypted && user.data;
const url = showUrl ? (paste.burn ? `${paste.urls.view}?burn_unless=${user.data.id}` : paste.urls.view) : '#';
const containerClasses = classNames(!showUrl && 'cursor-not-allowed');
const containerClasses = clsx(!showUrl && 'cursor-not-allowed');
const modifiers: string[] = [paste.type];
if (paste.burn) modifiers.push('burn');
if (paste.encrypted) modifiers.push('encrypted');

View File

@ -1,5 +1,5 @@
import { useAsync } from '@ryanke/pandora';
import classNames from 'classnames';
import clsx from 'clsx';
import { Form, Formik } from 'formik';
import { useRouter } from 'next/router';
import type { FC } from 'react';
@ -59,7 +59,7 @@ export const LoginForm: FC = () => {
login({ ...loginInfo, otp });
}}
/>
<span className={classNames(`text-xs text-center`, invalidOTP ? 'text-red-400' : 'text-gray-600')}>
<span className={clsx(`text-xs text-center`, invalidOTP ? 'text-red-400' : 'text-gray-600')}>
{invalidOTP ? 'Invalid OTP code' : 'Enter the OTP code from your authenticator app'}
</span>
</div>

View File

@ -5,138 +5,140 @@ export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };
export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
const defaultOptions = {} as const;
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: string;
String: string;
Boolean: boolean;
Int: number;
Float: number;
ID: { input: string; output: string };
String: { input: string; output: string };
Boolean: { input: boolean; output: boolean };
Int: { input: number; output: number };
Float: { input: number; output: number };
/** A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format. */
DateTime: any;
DateTime: { input: any; output: any };
};
export type Config = {
__typename?: 'Config';
allowTypes: Array<Scalars['String']>;
allowTypes: Array<Scalars['String']['output']>;
/** The host the request is being made to. This host may not be in the hosts list if the user is not authorized to access it. */
currentHost: ConfigHost;
/** A list of hosts the user can access. */
hosts: Array<ConfigHost>;
inquiriesEmail: Scalars['String'];
requireEmails: Scalars['Boolean'];
inquiriesEmail: Scalars['String']['output'];
requireEmails: Scalars['Boolean']['output'];
rootHost: ConfigHost;
uploadLimit: Scalars['Float'];
uploadLimit: Scalars['Float']['output'];
};
export type ConfigHost = {
__typename?: 'ConfigHost';
normalised: Scalars['String'];
redirect?: Maybe<Scalars['String']>;
url: Scalars['String'];
normalised: Scalars['String']['output'];
redirect?: Maybe<Scalars['String']['output']>;
url: Scalars['String']['output'];
};
export type CreatePasteDto = {
burn: Scalars['Boolean'];
content: Scalars['String'];
encrypted: Scalars['Boolean'];
expiresAt?: InputMaybe<Scalars['Float']>;
extension?: InputMaybe<Scalars['String']>;
hostname?: InputMaybe<Scalars['String']>;
paranoid: Scalars['Boolean'];
title?: InputMaybe<Scalars['String']>;
burn: Scalars['Boolean']['input'];
content: Scalars['String']['input'];
encrypted: Scalars['Boolean']['input'];
expiresAt?: InputMaybe<Scalars['Float']['input']>;
extension?: InputMaybe<Scalars['String']['input']>;
hostname?: InputMaybe<Scalars['String']['input']>;
paranoid: Scalars['Boolean']['input'];
title?: InputMaybe<Scalars['String']['input']>;
};
export type CreateUserDto = {
email?: InputMaybe<Scalars['String']>;
invite: Scalars['String'];
password: Scalars['String'];
username: Scalars['String'];
email?: InputMaybe<Scalars['String']['input']>;
invite: Scalars['String']['input'];
password: Scalars['String']['input'];
username: Scalars['String']['input'];
};
export type File = {
__typename?: 'File';
createdAt: Scalars['DateTime'];
displayName: Scalars['String'];
hash: Scalars['String'];
id: Scalars['ID'];
isOwner: Scalars['Boolean'];
createdAt: Scalars['DateTime']['output'];
displayName: Scalars['String']['output'];
hash: Scalars['String']['output'];
id: Scalars['ID']['output'];
isOwner: Scalars['Boolean']['output'];
metadata?: Maybe<FileMetadata>;
name?: Maybe<Scalars['String']>;
name?: Maybe<Scalars['String']['output']>;
paths: ResourceLocations;
size: Scalars['Float'];
sizeFormatted: Scalars['String'];
size: Scalars['Float']['output'];
sizeFormatted: Scalars['String']['output'];
thumbnail?: Maybe<Thumbnail>;
type: Scalars['String'];
type: Scalars['String']['output'];
urls: ResourceLocations;
};
export type FileMetadata = {
__typename?: 'FileMetadata';
height?: Maybe<Scalars['Float']>;
width?: Maybe<Scalars['Float']>;
height?: Maybe<Scalars['Float']['output']>;
width?: Maybe<Scalars['Float']['output']>;
};
export type FilePage = {
__typename?: 'FilePage';
edges: Array<FilePageEdge>;
pageInfo: PageInfo;
totalCount: Scalars['Int'];
totalCount: Scalars['Int']['output'];
};
export type FilePageEdge = {
__typename?: 'FilePageEdge';
cursor: Scalars['String'];
cursor: Scalars['String']['output'];
node: File;
};
export type Invite = {
__typename?: 'Invite';
consumed: Scalars['Boolean'];
createdAt: Scalars['DateTime'];
expired: Scalars['Boolean'];
expiresAt?: Maybe<Scalars['DateTime']>;
id: Scalars['ID'];
path: Scalars['String'];
permissions?: Maybe<Scalars['Float']>;
skipVerification: Scalars['Boolean'];
url: Scalars['String'];
consumed: Scalars['Boolean']['output'];
createdAt: Scalars['DateTime']['output'];
expired: Scalars['Boolean']['output'];
expiresAt?: Maybe<Scalars['DateTime']['output']>;
id: Scalars['ID']['output'];
path: Scalars['String']['output'];
permissions?: Maybe<Scalars['Float']['output']>;
skipVerification: Scalars['Boolean']['output'];
url: Scalars['String']['output'];
};
export type Link = {
__typename?: 'Link';
clicks: Scalars['Float'];
createdAt: Scalars['DateTime'];
destination: Scalars['String'];
id: Scalars['ID'];
clicks: Scalars['Float']['output'];
createdAt: Scalars['DateTime']['output'];
destination: Scalars['String']['output'];
id: Scalars['ID']['output'];
paths: ResourceLocations;
urls: ResourceLocations;
};
export type Mutation = {
__typename?: 'Mutation';
confirmOTP: Scalars['Boolean'];
confirmOTP: Scalars['Boolean']['output'];
createInvite: Invite;
createLink: Link;
createPaste: Paste;
createUser: User;
deleteFile: Scalars['Boolean'];
disableOTP: Scalars['Boolean'];
deleteFile: Scalars['Boolean']['output'];
disableOTP: Scalars['Boolean']['output'];
generateOTP: OtpEnabledDto;
login: User;
logout: Scalars['Boolean'];
logout: Scalars['Boolean']['output'];
refreshToken: User;
resendVerificationEmail: Scalars['Boolean'];
resendVerificationEmail: Scalars['Boolean']['output'];
};
export type MutationConfirmOtpArgs = {
otpCode: Scalars['String'];
otpCode: Scalars['String']['input'];
};
export type MutationCreateLinkArgs = {
destination: Scalars['String'];
host?: InputMaybe<Scalars['String']>;
destination: Scalars['String']['input'];
host?: InputMaybe<Scalars['String']['input']>;
};
export type MutationCreatePasteArgs = {
@ -148,18 +150,18 @@ export type MutationCreateUserArgs = {
};
export type MutationDeleteFileArgs = {
fileId: Scalars['ID'];
key?: InputMaybe<Scalars['String']>;
fileId: Scalars['ID']['input'];
key?: InputMaybe<Scalars['String']['input']>;
};
export type MutationDisableOtpArgs = {
otpCode: Scalars['String'];
otpCode: Scalars['String']['input'];
};
export type MutationLoginArgs = {
otpCode?: InputMaybe<Scalars['String']>;
password: Scalars['String'];
username: Scalars['String'];
otpCode?: InputMaybe<Scalars['String']['input']>;
password: Scalars['String']['input'];
username: Scalars['String']['input'];
};
export type MutationResendVerificationEmailArgs = {
@ -168,32 +170,32 @@ export type MutationResendVerificationEmailArgs = {
export type OtpEnabledDto = {
__typename?: 'OTPEnabledDto';
qrauthUrl: Scalars['String'];
recoveryCodes: Array<Scalars['String']>;
secret: Scalars['String'];
qrauthUrl: Scalars['String']['output'];
recoveryCodes: Array<Scalars['String']['output']>;
secret: Scalars['String']['output'];
};
export type PageInfo = {
__typename?: 'PageInfo';
endCursor?: Maybe<Scalars['String']>;
hasNextPage: Scalars['Boolean'];
hasPreviousPage: Scalars['Boolean'];
startCursor?: Maybe<Scalars['String']>;
endCursor?: Maybe<Scalars['String']['output']>;
hasNextPage: Scalars['Boolean']['output'];
hasPreviousPage: Scalars['Boolean']['output'];
startCursor?: Maybe<Scalars['String']['output']>;
};
export type Paste = {
__typename?: 'Paste';
burn: Scalars['Boolean'];
burnt?: Maybe<Scalars['Boolean']>;
content: Scalars['String'];
createdAt: Scalars['DateTime'];
encrypted: Scalars['Boolean'];
expiresAt?: Maybe<Scalars['DateTime']>;
extension?: Maybe<Scalars['String']>;
id: Scalars['ID'];
burn: Scalars['Boolean']['output'];
burnt?: Maybe<Scalars['Boolean']['output']>;
content: Scalars['String']['output'];
createdAt: Scalars['DateTime']['output'];
encrypted: Scalars['Boolean']['output'];
expiresAt?: Maybe<Scalars['DateTime']['output']>;
extension?: Maybe<Scalars['String']['output']>;
id: Scalars['ID']['output'];
paths: ResourceLocations;
title?: Maybe<Scalars['String']>;
type: Scalars['String'];
title?: Maybe<Scalars['String']['output']>;
type: Scalars['String']['output'];
urls: ResourceLocations;
};
@ -201,12 +203,12 @@ export type PastePage = {
__typename?: 'PastePage';
edges: Array<PastePageEdge>;
pageInfo: PageInfo;
totalCount: Scalars['Int'];
totalCount: Scalars['Int']['output'];
};
export type PastePageEdge = {
__typename?: 'PastePageEdge';
cursor: Scalars['String'];
cursor: Scalars['String']['output'];
node: Paste;
};
@ -221,66 +223,66 @@ export type Query = {
};
export type QueryFileArgs = {
fileId: Scalars['ID'];
fileId: Scalars['ID']['input'];
};
export type QueryInviteArgs = {
inviteId: Scalars['ID'];
inviteId: Scalars['ID']['input'];
};
export type QueryLinkArgs = {
linkId: Scalars['ID'];
linkId: Scalars['ID']['input'];
};
export type QueryPasteArgs = {
pasteId: Scalars['ID'];
pasteId: Scalars['ID']['input'];
};
export type ResendVerificationEmailDto = {
email: Scalars['String'];
email: Scalars['String']['input'];
};
export type ResourceLocations = {
__typename?: 'ResourceLocations';
delete?: Maybe<Scalars['String']>;
direct: Scalars['String'];
thumbnail?: Maybe<Scalars['String']>;
view: Scalars['String'];
delete?: Maybe<Scalars['String']['output']>;
direct: Scalars['String']['output'];
thumbnail?: Maybe<Scalars['String']['output']>;
view: Scalars['String']['output'];
};
export type Thumbnail = {
__typename?: 'Thumbnail';
createdAt: Scalars['DateTime'];
duration: Scalars['Float'];
height: Scalars['Float'];
size: Scalars['Float'];
type: Scalars['String'];
width: Scalars['Float'];
createdAt: Scalars['DateTime']['output'];
duration: Scalars['Float']['output'];
height: Scalars['Float']['output'];
size: Scalars['Float']['output'];
type: Scalars['String']['output'];
width: Scalars['Float']['output'];
};
export type User = {
__typename?: 'User';
aggregateFileSize: Scalars['Float'];
email?: Maybe<Scalars['String']>;
aggregateFileSize: Scalars['Float']['output'];
email?: Maybe<Scalars['String']['output']>;
files: FilePage;
id: Scalars['ID'];
otpEnabled: Scalars['Boolean'];
id: Scalars['ID']['output'];
otpEnabled: Scalars['Boolean']['output'];
pastes: PastePage;
permissions: Scalars['Float'];
tags: Array<Scalars['String']>;
token: Scalars['String'];
username: Scalars['String'];
verifiedEmail: Scalars['Boolean'];
permissions: Scalars['Float']['output'];
tags: Array<Scalars['String']['output']>;
token: Scalars['String']['output'];
username: Scalars['String']['output'];
verifiedEmail: Scalars['Boolean']['output'];
};
export type UserFilesArgs = {
after?: InputMaybe<Scalars['String']>;
first?: InputMaybe<Scalars['Float']>;
after?: InputMaybe<Scalars['String']['input']>;
first?: InputMaybe<Scalars['Float']['input']>;
};
export type UserPastesArgs = {
after?: InputMaybe<Scalars['String']>;
first?: InputMaybe<Scalars['Float']>;
after?: InputMaybe<Scalars['String']['input']>;
first?: InputMaybe<Scalars['Float']['input']>;
};
export type ResendVerificationEmailMutationVariables = Exact<{
@ -313,8 +315,8 @@ export type FileCardFragment = {
};
export type GetFilesQueryVariables = Exact<{
first?: InputMaybe<Scalars['Float']>;
after?: InputMaybe<Scalars['String']>;
first?: InputMaybe<Scalars['Float']['input']>;
after?: InputMaybe<Scalars['String']['input']>;
}>;
export type GetFilesQuery = {
@ -342,8 +344,8 @@ export type GetFilesQuery = {
};
export type GetPastesQueryVariables = Exact<{
first?: InputMaybe<Scalars['Float']>;
after?: InputMaybe<Scalars['String']>;
first?: InputMaybe<Scalars['Float']['input']>;
after?: InputMaybe<Scalars['String']['input']>;
}>;
export type GetPastesQuery = {
@ -412,9 +414,9 @@ export type RegularUserFragment = {
};
export type LoginMutationVariables = Exact<{
username: Scalars['String'];
password: Scalars['String'];
otp?: InputMaybe<Scalars['String']>;
username: Scalars['String']['input'];
password: Scalars['String']['input'];
otp?: InputMaybe<Scalars['String']['input']>;
}>;
export type LoginMutation = {
@ -441,13 +443,13 @@ export type GenerateOtpMutation = {
};
export type ConfirmOtpMutationVariables = Exact<{
otpCode: Scalars['String'];
otpCode: Scalars['String']['input'];
}>;
export type ConfirmOtpMutation = { __typename?: 'Mutation'; confirmOTP: boolean };
export type DisableOtpMutationVariables = Exact<{
otpCode: Scalars['String'];
otpCode: Scalars['String']['input'];
}>;
export type DisableOtpMutation = { __typename?: 'Mutation'; disableOTP: boolean };
@ -467,7 +469,7 @@ export type RefreshTokenMutation = {
};
export type GetFileQueryVariables = Exact<{
fileId: Scalars['ID'];
fileId: Scalars['ID']['input'];
}>;
export type GetFileQuery = {
@ -487,14 +489,14 @@ export type GetFileQuery = {
};
export type DeleteFileMutationVariables = Exact<{
fileId: Scalars['ID'];
deleteKey?: InputMaybe<Scalars['String']>;
fileId: Scalars['ID']['input'];
deleteKey?: InputMaybe<Scalars['String']['input']>;
}>;
export type DeleteFileMutation = { __typename?: 'Mutation'; deleteFile: boolean };
export type GetInviteQueryVariables = Exact<{
inviteId: Scalars['ID'];
inviteId: Scalars['ID']['input'];
}>;
export type GetInviteQuery = {
@ -518,7 +520,7 @@ export type CreatePasteMutation = {
};
export type GetPasteQueryVariables = Exact<{
pasteId: Scalars['ID'];
pasteId: Scalars['ID']['input'];
}>;
export type GetPasteQuery = {
@ -540,8 +542,8 @@ export type GetPasteQuery = {
};
export type ShortenMutationVariables = Exact<{
link: Scalars['String'];
host?: InputMaybe<Scalars['String']>;
link: Scalars['String']['input'];
host?: InputMaybe<Scalars['String']['input']>;
}>;
export type ShortenMutation = {
@ -618,12 +620,12 @@ export type ResendVerificationEmailMutationFn = Apollo.MutationFunction<
* });
*/
export function useResendVerificationEmailMutation(
baseOptions?: Apollo.MutationHookOptions<ResendVerificationEmailMutation, ResendVerificationEmailMutationVariables>
baseOptions?: Apollo.MutationHookOptions<ResendVerificationEmailMutation, ResendVerificationEmailMutationVariables>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useMutation<ResendVerificationEmailMutation, ResendVerificationEmailMutationVariables>(
ResendVerificationEmailDocument,
options
options,
);
}
export type ResendVerificationEmailMutationHookResult = ReturnType<typeof useResendVerificationEmailMutation>;
@ -720,7 +722,7 @@ export function useGetPastesQuery(baseOptions?: Apollo.QueryHookOptions<GetPaste
return Apollo.useQuery<GetPastesQuery, GetPastesQueryVariables>(GetPastesDocument, options);
}
export function useGetPastesLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<GetPastesQuery, GetPastesQueryVariables>
baseOptions?: Apollo.LazyQueryHookOptions<GetPastesQuery, GetPastesQueryVariables>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<GetPastesQuery, GetPastesQueryVariables>(GetPastesDocument, options);
@ -906,7 +908,7 @@ export type GenerateOtpMutationFn = Apollo.MutationFunction<GenerateOtpMutation,
* });
*/
export function useGenerateOtpMutation(
baseOptions?: Apollo.MutationHookOptions<GenerateOtpMutation, GenerateOtpMutationVariables>
baseOptions?: Apollo.MutationHookOptions<GenerateOtpMutation, GenerateOtpMutationVariables>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useMutation<GenerateOtpMutation, GenerateOtpMutationVariables>(GenerateOtpDocument, options);
@ -939,7 +941,7 @@ export type ConfirmOtpMutationFn = Apollo.MutationFunction<ConfirmOtpMutation, C
* });
*/
export function useConfirmOtpMutation(
baseOptions?: Apollo.MutationHookOptions<ConfirmOtpMutation, ConfirmOtpMutationVariables>
baseOptions?: Apollo.MutationHookOptions<ConfirmOtpMutation, ConfirmOtpMutationVariables>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useMutation<ConfirmOtpMutation, ConfirmOtpMutationVariables>(ConfirmOtpDocument, options);
@ -972,7 +974,7 @@ export type DisableOtpMutationFn = Apollo.MutationFunction<DisableOtpMutation, D
* });
*/
export function useDisableOtpMutation(
baseOptions?: Apollo.MutationHookOptions<DisableOtpMutation, DisableOtpMutationVariables>
baseOptions?: Apollo.MutationHookOptions<DisableOtpMutation, DisableOtpMutationVariables>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useMutation<DisableOtpMutation, DisableOtpMutationVariables>(DisableOtpDocument, options);
@ -1007,7 +1009,7 @@ export type RefreshTokenMutationFn = Apollo.MutationFunction<RefreshTokenMutatio
* });
*/
export function useRefreshTokenMutation(
baseOptions?: Apollo.MutationHookOptions<RefreshTokenMutation, RefreshTokenMutationVariables>
baseOptions?: Apollo.MutationHookOptions<RefreshTokenMutation, RefreshTokenMutationVariables>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useMutation<RefreshTokenMutation, RefreshTokenMutationVariables>(RefreshTokenDocument, options);
@ -1096,7 +1098,7 @@ export type DeleteFileMutationFn = Apollo.MutationFunction<DeleteFileMutation, D
* });
*/
export function useDeleteFileMutation(
baseOptions?: Apollo.MutationHookOptions<DeleteFileMutation, DeleteFileMutationVariables>
baseOptions?: Apollo.MutationHookOptions<DeleteFileMutation, DeleteFileMutationVariables>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useMutation<DeleteFileMutation, DeleteFileMutationVariables>(DeleteFileDocument, options);
@ -1134,7 +1136,7 @@ export function useGetInviteQuery(baseOptions: Apollo.QueryHookOptions<GetInvite
return Apollo.useQuery<GetInviteQuery, GetInviteQueryVariables>(GetInviteDocument, options);
}
export function useGetInviteLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<GetInviteQuery, GetInviteQueryVariables>
baseOptions?: Apollo.LazyQueryHookOptions<GetInviteQuery, GetInviteQueryVariables>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<GetInviteQuery, GetInviteQueryVariables>(GetInviteDocument, options);
@ -1169,7 +1171,7 @@ export type CreateUserMutationFn = Apollo.MutationFunction<CreateUserMutation, C
* });
*/
export function useCreateUserMutation(
baseOptions?: Apollo.MutationHookOptions<CreateUserMutation, CreateUserMutationVariables>
baseOptions?: Apollo.MutationHookOptions<CreateUserMutation, CreateUserMutationVariables>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useMutation<CreateUserMutation, CreateUserMutationVariables>(CreateUserDocument, options);
@ -1207,7 +1209,7 @@ export type CreatePasteMutationFn = Apollo.MutationFunction<CreatePasteMutation,
* });
*/
export function useCreatePasteMutation(
baseOptions?: Apollo.MutationHookOptions<CreatePasteMutation, CreatePasteMutationVariables>
baseOptions?: Apollo.MutationHookOptions<CreatePasteMutation, CreatePasteMutationVariables>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useMutation<CreatePasteMutation, CreatePasteMutationVariables>(CreatePasteDocument, options);
@ -1293,7 +1295,7 @@ export type ShortenMutationFn = Apollo.MutationFunction<ShortenMutation, Shorten
* });
*/
export function useShortenMutation(
baseOptions?: Apollo.MutationHookOptions<ShortenMutation, ShortenMutationVariables>
baseOptions?: Apollo.MutationHookOptions<ShortenMutation, ShortenMutationVariables>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useMutation<ShortenMutation, ShortenMutationVariables>(ShortenDocument, options);

View File

@ -1,5 +1,5 @@
import { Button, ButtonStyle, Container, useAsync, useToasts } from '@ryanke/pandora';
import classNames from 'classnames';
import clsx from 'clsx';
import { useRouter } from 'next/router';
import { QRCodeSVG } from 'qrcode.react';
import { Fragment, useCallback, useEffect, useMemo, useState } from 'react';
@ -146,7 +146,7 @@ export default function Generate() {
type="button"
onClick={() => setCurrentStep((prev) => prev - 1)}
disabled={currentStep === 0}
className={classNames(
className={clsx(
`text-gray-400 flex items-center gap-1 hover:underline`,
currentStep === 0 && 'opacity-0 pointer-events-none'
)}

View File

@ -1,5 +1,5 @@
import { Container, Spinner, useAsync, useToasts } from '@ryanke/pandora';
import classNames from 'classnames';
import clsx from 'clsx';
import copyToClipboard from 'copy-to-clipboard';
import type { GetServerSidePropsContext } from 'next';
import { useRouter } from 'next/router';
@ -20,7 +20,7 @@ const FileOption: FC<{ children: ReactNode; className?: string; onClick: () => v
className,
onClick,
}) => {
const classes = classNames(
const classes = clsx(
'flex items-center gap-2 shrink-0 transition-colors duration-100 hover:text-gray-300',
className
);

View File

@ -1,9 +1,9 @@
import classNames from 'classnames';
import clsx from 'clsx';
import { type FC, type ReactNode } from 'react';
import { Info } from 'react-feather';
export const Warning: FC<{ children: ReactNode; className?: string }> = ({ children, className }) => {
const classes = classNames(
const classes = clsx(
'bg-purple-400 bg-opacity-40 border border-purple-400 px-2 py-1 rounded text-sm flex items-center gap-2',
className
);

File diff suppressed because it is too large Load Diff