feat: add OAuth2 authentication (#16)

This commit is contained in:
Divlo 2022-03-16 12:18:09 +01:00 committed by GitHub
parent 8f74263daa
commit c595d42313
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 2740 additions and 35106 deletions

View File

@ -10,4 +10,3 @@ temp
.DS_Store .DS_Store
.lighthouseci .lighthouseci
.vercel .vercel
storybook-static

View File

@ -1,6 +1,5 @@
.next .next
.lighthouseci .lighthouseci
storybook-static
coverage coverage
node_modules node_modules
next-env.d.ts next-env.d.ts

View File

@ -1,10 +1,5 @@
{ {
"extends": [ "extends": ["conventions", "next/core-web-vitals", "prettier"],
"conventions",
"next/core-web-vitals",
"plugin:storybook/recommended",
"prettier"
],
"plugins": ["prettier"], "plugins": ["prettier"],
"parserOptions": { "parserOptions": {
"project": "./tsconfig.json" "project": "./tsconfig.json"
@ -16,7 +11,6 @@
}, },
"rules": { "rules": {
"prettier/prettier": "error", "prettier/prettier": "error",
"@next/next/no-img-element": "off", "@next/next/no-img-element": "off"
"@typescript-eslint/no-misused-promises": "off"
} }
} }

View File

@ -23,6 +23,3 @@ jobs:
- name: 'Build' - name: 'Build'
run: 'npm run build' run: 'npm run build'
- name: 'Build Storybook'
run: 'npm run storybook:build'

1
.gitignore vendored
View File

@ -48,5 +48,4 @@ npm-debug.log*
# misc # misc
.DS_Store .DS_Store
.lighthouseci .lighthouseci
storybook-static
.vercel .vercel

View File

@ -1,6 +1,5 @@
.next .next
.lighthouseci .lighthouseci
storybook-static
coverage coverage
node_modules node_modules
**/workbox-*.js **/workbox-*.js

View File

@ -1,31 +0,0 @@
const path = require('path')
module.exports = {
core: {
builder: 'webpack5'
},
staticDirs: ['../public'],
stories: ['../components/**/*.stories.@(ts|tsx|js|jsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-postcss',
'storybook-tailwind-dark-mode'
],
webpackFinal: async (config) => {
config.module.rules.push({
test: /\,css&/,
use: [
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [require('tailwindcss'), require('autoprefixer')]
}
}
],
include: path.resolve(__dirname, '../')
})
return config
}
}

View File

@ -1,39 +0,0 @@
import * as NextImage from 'next/image'
import { addDecorator } from '@storybook/react'
import I18nProvider from 'next-translate/I18nProvider'
import i18n from '../i18n.json'
import common from '../locales/en/common.json'
import authentication from '../locales/en/authentication.json'
import application from '../locales/en/application.json'
import '../styles/global.css'
import '@fontsource/montserrat/400.css'
import '@fontsource/montserrat/500.css'
import '@fontsource/montserrat/600.css'
import '@fontsource/montserrat/700.css'
import '@fontsource/roboto/400.css'
import '@fontsource/roboto/700.css'
addDecorator((story) => (
<I18nProvider
lang='en'
namespaces={{
common,
authentication,
application
}}
config={i18n}
>
<div id='preview-storybook'>{story()}</div>
</I18nProvider>
))
const OriginalNextImage = NextImage.default
Object.defineProperty(NextImage, 'default', {
configurable: true,
value: (props) => <OriginalNextImage {...props} unoptimized />
})

View File

@ -1,16 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { channelExample } from '../../../../cypress/fixtures/channels/channel'
import { Channel as Component, ChannelProps } from './Channel'
const Stories: Meta = {
title: 'Channel',
component: Component
}
export default Stories
export const Channel: Story<ChannelProps> = (arguments_) => {
return <Component {...arguments_} />
}
Channel.args = { path: { channelId: 1, guildId: 1 }, channel: channelExample }

View File

@ -1,15 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { CreateGuild as Component } from './CreateGuild'
const Stories: Meta = {
title: 'CreateGuild',
component: Component
}
export default Stories
export const CreateGuild: Story = (arguments_) => {
return <Component {...arguments_} />
}
CreateGuild.args = {}

View File

@ -6,10 +6,9 @@ import { PhotographIcon } from '@heroicons/react/solid'
import { Form } from 'react-component-form' import { Form } from 'react-component-form'
import useTranslation from 'next-translate/useTranslation' import useTranslation from 'next-translate/useTranslation'
import { HandleSubmitCallback, useForm } from 'hooks/useForm' import { HandleSubmitCallback, useForm } from '../../../hooks/useForm'
import { guildSchema } from 'models/Guild' import { guildSchema } from '../../../models/Guild'
import { FormState } from 'components/design/FormState' import { FormState } from '../../design/FormState'
import { API_URL } from '../../../tools/api' import { API_URL } from '../../../tools/api'
import { useGuildMember } from '../../../contexts/GuildMember' import { useGuildMember } from '../../../contexts/GuildMember'
import { Textarea } from '../../design/Textarea' import { Textarea } from '../../design/Textarea'

View File

@ -1,21 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { Guild as Component, GuildProps } from './Guild'
import { guildExample } from '../../../../cypress/fixtures/guilds/guild'
const Stories: Meta = {
title: 'Guild',
component: Component
}
export default Stories
export const Guild: Story<GuildProps> = (arguments_) => {
return <Component {...arguments_} />
}
Guild.args = {
guild: {
...guildExample,
defaultChannelId: 1
}
}

View File

@ -5,10 +5,9 @@ import useTranslation from 'next-translate/useTranslation'
import classNames from 'classnames' import classNames from 'classnames'
import axios from 'axios' import axios from 'axios'
import { Emoji } from 'components/Emoji' import { Emoji } from '../../../Emoji'
import { ConfirmGuildJoin } from 'components/Application/ConfirmGuildJoin' import { ConfirmGuildJoin } from '../../ConfirmGuildJoin'
import { API_URL } from 'tools/api' import { API_URL } from '../../../../tools/api'
import { import {
GuildPublic as GuildPublicType, GuildPublic as GuildPublicType,
GuildWithDefaultChannelId GuildWithDefaultChannelId

View File

@ -1,16 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { Member as Component, MemberProps } from './Member'
import { memberExampleComplete } from '../../../../cypress/fixtures/members/member'
const Stories: Meta = {
title: 'Member',
component: Component
}
export default Stories
export const Member: Story<MemberProps> = (arguments_) => {
return <Component {...arguments_} />
}
Member.args = { member: memberExampleComplete }

View File

@ -1,16 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { Message as Component, MessageProps } from './Message'
import { messageExampleComplete } from '../../../../cypress/fixtures/messages/message'
const Stories: Meta = {
title: 'Message',
component: Component
}
export default Stories
export const Message: Story<MessageProps> = (arguments_) => {
return <Component {...arguments_} />
}
Message.args = { message: messageExampleComplete }

View File

@ -1,15 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { PopupGuild as Component, PopupGuildProps } from './PopupGuild'
const Stories: Meta = {
title: 'PopupGuild',
component: Component
}
export default Stories
export const PopupGuild: Story<PopupGuildProps> = (arguments_) => {
return <Component {...arguments_} />
}
PopupGuild.args = {}

View File

@ -1,36 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { PlusSmIcon } from '@heroicons/react/solid'
import Image from 'next/image'
import {
PopupGuildCard as Component,
PopupGuildCardProps
} from './PopupGuildCard'
const Stories: Meta = {
title: 'PopupGuildCard',
component: Component
}
export default Stories
export const PopupGuildCard: Story<PopupGuildCardProps> = (arguments_) => {
return <Component {...arguments_} />
}
PopupGuildCard.args = {
image: (
<Image
src='/images/svg/design/create-server.svg'
alt=''
width={230}
height={230}
/>
),
description:
'Create your own guild and manage everything within a few clicks !',
link: {
icon: <PlusSmIcon className='mr-2 h-8 w-8' />,
text: 'Create a server',
href: '/application/guilds/create'
}
}

View File

@ -1,19 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { channelExample } from '../../../cypress/fixtures/channels/channel'
import { guildExample } from '../../../cypress/fixtures/guilds/guild'
import { SendMessage as Component, SendMessageProps } from './SendMessage'
const Stories: Meta = {
title: 'SendMessage',
component: Component
}
export default Stories
export const SendMessage: Story<SendMessageProps> = (arguments_) => {
return <Component {...arguments_} />
}
SendMessage.args = {
path: { channelId: channelExample.id, guildId: guildExample.id }
}

View File

@ -1,14 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { Sidebar as Component, SidebarProps } from './Sidebar'
const Stories: Meta = {
title: 'Sidebar',
component: Component
}
export default Stories
export const Sidebar: Story<SidebarProps> = (arguments_) => {
return <Component {...arguments_} />
}

View File

@ -1,29 +0,0 @@
import { Meta, Story } from '@storybook/react'
import {
guildExample,
guildExample2
} from '../../../cypress/fixtures/guilds/guild'
import {
userExample,
userSettingsExample
} from '../../../cypress/fixtures/users/user'
import { UserProfile as Component, UserProfileProps } from './UserProfile'
const Stories: Meta = {
title: 'UserProfile',
component: Component
}
export default Stories
export const UserProfile: Story<UserProfileProps> = (arguments_) => {
return <Component {...arguments_} />
}
UserProfile.args = {
user: {
...userExample,
settings: userSettingsExample
},
guilds: [guildExample, guildExample2]
}

View File

@ -1,16 +1,10 @@
import Image from 'next/image' import Image from 'next/image'
import React, { useState } from 'react'
import classNames from 'classnames'
import date from 'date-and-time' import date from 'date-and-time'
import useTranslation from 'next-translate/useTranslation' import useTranslation from 'next-translate/useTranslation'
import { XIcon } from '@heroicons/react/solid'
import { API_URL } from '../../../tools/api' import { API_URL } from '../../../tools/api'
import { UserPublic } from '../../../models/User' import { UserPublic } from '../../../models/User'
import { UserProfileGuilds } from './UserProfileGuilds'
import { UserProfileGuild } from './UserProfileGuilds/UserProfileGuild'
import { Guild } from '../../../models/Guild' import { Guild } from '../../../models/Guild'
import { ConfirmGuildJoin } from '../ConfirmGuildJoin'
export interface UserProfileProps { export interface UserProfileProps {
className?: string className?: string
@ -19,27 +13,12 @@ export interface UserProfileProps {
} }
export const UserProfile: React.FC<UserProfileProps> = (props) => { export const UserProfile: React.FC<UserProfileProps> = (props) => {
const { user, guilds } = props const { user } = props
const { t } = useTranslation() const { t } = useTranslation()
const [showPopup, setShowPopup] = useState(false)
const [confirmation, setConfirmation] = useState(false)
const handleConfirmationState = (): void => {
setConfirmation((confirmation) => !confirmation)
}
const handlePopupVisibility = (): void => {
setShowPopup((showPopup) => !showPopup)
}
return ( return (
<div className='relative flex h-full flex-col items-center justify-center'> <div className='relative flex h-full flex-col items-center justify-center'>
<div <div className='transition'>
className={classNames('transition', {
'select-none blur-3xl': showPopup
})}
>
<div className='max-w-[1000px] px-12'> <div className='max-w-[1000px] px-12'>
<div className='flex items-center justify-between'> <div className='flex items-center justify-between'>
<div className='flex w-max items-center'> <div className='flex w-max items-center'>
@ -109,13 +88,6 @@ export const UserProfile: React.FC<UserProfileProps> = (props) => {
</div> </div>
</div> </div>
</div> </div>
<div className='py-8 px-4' onClick={handlePopupVisibility}>
<UserProfileGuilds
isPublicGuilds={user.settings.isPublicGuilds}
guilds={guilds}
/>
</div>
</div> </div>
<div className='mt-7'> <div className='mt-7'>
{user.biography != null && ( {user.biography != null && (
@ -124,46 +96,6 @@ export const UserProfile: React.FC<UserProfileProps> = (props) => {
</div> </div>
</div> </div>
</div> </div>
<div
className={classNames(
'pointer-events-none invisible absolute top-0 flex h-full w-full items-center justify-center bg-zinc-900/75 opacity-0 transition',
{
'pointer-events-auto !visible !opacity-100': showPopup
}
)}
>
<div
className={classNames(
'relative h-[400px] w-[400px] scale-0 overflow-y-auto overflow-x-hidden rounded-2xl bg-gray-200 py-2 shadow-xl transition dark:bg-gray-800',
{ 'scale-100': showPopup }
)}
>
<div
className={classNames('relative h-full transition', {
'-translate-x-[150%]': confirmation
})}
>
<UserProfileGuild
handleConfirmationState={handleConfirmationState}
/>
</div>
<ConfirmGuildJoin
className={classNames(
'absolute top-0 left-[150%] flex h-full w-full flex-col items-center justify-center transition-all',
{ 'left-[0%]': confirmation }
)}
handleYes={handleConfirmationState}
handleNo={() => {}}
/>
</div>
<XIcon
height={40}
onClick={handlePopupVisibility}
className='absolute top-8 right-8 cursor-pointer text-white transition hover:rotate-90'
/>
</div>
</div> </div>
) )
} }

View File

@ -1,18 +0,0 @@
import { Meta, Story } from '@storybook/react'
import {
UserProfileGuild as Component,
UserProfileGuildProps
} from './UserProfileGuild'
const Stories: Meta = {
title: 'UserProfileGuild',
component: Component
}
export default Stories
export const UserProfileGuild: Story<UserProfileGuildProps> = (arguments_) => {
return <Component {...arguments_} />
}
UserProfileGuild.args = { className: 'text-center' }

View File

@ -1,12 +0,0 @@
import { render } from '@testing-library/react'
import { UserProfileGuild } from './UserProfileGuild'
describe('<UserProfileGuild />', () => {
it('should render successfully', () => {
const { baseElement } = render(
<UserProfileGuild handleConfirmationState={() => {}} />
)
expect(baseElement).toBeTruthy()
})
})

View File

@ -1,50 +0,0 @@
import Image from 'next/image'
import { LoginIcon } from '@heroicons/react/solid'
import classNames from 'classnames'
export interface UserProfileGuildProps {
className?: string
handleConfirmationState: () => void
}
export const UserProfileGuild: React.FC<UserProfileGuildProps> = ({
...props
}) => {
return (
<div
className={classNames(
'group relative flex w-full cursor-pointer transition',
props.className
)}
onClick={props.handleConfirmationState}
>
<div className='relative w-full px-8 py-5 transition group-hover:-translate-x-20'>
<div className='flex transition group-hover:opacity-40'>
<div className='mr-8 flex min-h-[60px] min-w-[60px] select-none justify-center rounded-full drop-shadow-lg filter'>
<Image
className='rounded-full'
src='/images/guilds/Guild_1.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex flex-col'>
<h1 className='text-xl font-bold'>Guild Name</h1>
<p className='mt-2 text-gray-900 dark:text-gray-300'>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Debitis,
nam.
</p>
</div>
</div>
<div className='absolute top-0 right-[-80px] flex h-full w-[80px] items-center justify-center'>
<LoginIcon
height={40}
className='fill-green-600 drop-shadow-[0_0_15px_rgba(22,163,74,0.50)]'
/>
</div>
</div>
</div>
)
}

View File

@ -1 +0,0 @@
export * from './UserProfileGuild'

View File

@ -1,20 +0,0 @@
import { Meta, Story } from '@storybook/react'
import {
UserProfileGuilds as Component,
UserProfileGuildsProps
} from './UserProfileGuilds'
const Stories: Meta = {
title: 'UserProfileGuilds',
component: Component
}
export default Stories
export const UserProfileGuilds: Story<UserProfileGuildsProps> = (
arguments_
) => {
return <Component {...arguments_} />
}
UserProfileGuilds.args = {}

View File

@ -1,10 +0,0 @@
import { render } from '@testing-library/react'
import { UserProfileGuilds } from './UserProfileGuilds'
describe('<UserProfileGuilds />', () => {
it('should render successfully', () => {
const { baseElement } = render(<UserProfileGuilds />)
expect(baseElement).toBeTruthy()
})
})

View File

@ -1,107 +0,0 @@
import Image from 'next/image'
import classNames from 'classnames'
import { EyeOffIcon } from '@heroicons/react/solid'
import useTranslation from 'next-translate/useTranslation'
import { Guild } from '../../../../models/Guild'
export interface UserProfileGuildsProps {
isPublicGuilds?: boolean
guilds: Guild[]
}
export const UserProfileGuilds: React.FC<UserProfileGuildsProps> = (props) => {
const { isPublicGuilds = false } = props
const { t } = useTranslation()
return (
<div
className={classNames('relative cursor-pointer', {
'cursor-auto': !isPublicGuilds
})}
>
<div
className={classNames('flex -space-x-7', {
'select-none blur-lg': !isPublicGuilds
})}
>
<div className='flex items-center justify-center rounded-full drop-shadow-lg filter'>
<Image
className='rounded-full'
src='/images/guilds/Guild_1.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex items-center justify-center rounded-full drop-shadow-lg filter'>
<Image
className='rounded-full'
src='/images/guilds/Guild_2.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex items-center justify-center rounded-full drop-shadow-lg filter'>
<Image
className='rounded-full'
src='/images/guilds/Guild_3.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex items-center justify-center rounded-full drop-shadow-lg filter'>
<Image
className='rounded-full'
src='/images/guilds/Guild_4.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex items-center justify-center rounded-full drop-shadow-lg filter'>
<Image
className='rounded-full'
src='/images/guilds/Guild_5.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex items-center justify-center rounded-full drop-shadow-lg filter'>
<Image
className='rounded-full'
src='/images/guilds/Guild_6.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='z-10 flex h-[60px] w-[60px] items-center justify-center rounded-full bg-gray-300 drop-shadow-lg filter dark:bg-gray-800'>
<span className='select-none text-xl font-bold text-black dark:text-white'>
+4
</span>
</div>
</div>
<div
className={classNames(
'absolute top-1/2 flex -translate-y-1/2 items-center',
{ hidden: isPublicGuilds }
)}
>
<EyeOffIcon height={25} />
<p className='ml-4 drop-shadow-2xl'>
{t('application:private-user-guilds-list')}
</p>
</div>
</div>
)
}

View File

@ -1 +0,0 @@
export * from './UserProfileGuilds'

View File

@ -7,7 +7,6 @@ import { Type } from '@sinclair/typebox'
import axios from 'axios' import axios from 'axios'
import { API_URL } from '../../../tools/api' import { API_URL } from '../../../tools/api'
import { UserProfileGuilds } from '../UserProfile/UserProfileGuilds'
import { Input } from '../../design/Input' import { Input } from '../../design/Input'
import { Checkbox } from '../../design/Checkbox' import { Checkbox } from '../../design/Checkbox'
import { Textarea } from '../../design/Textarea' import { Textarea } from '../../design/Textarea'
@ -20,11 +19,9 @@ import { FormState } from '../../design/FormState'
import { useForm, HandleSubmitCallback } from '../../../hooks/useForm' import { useForm, HandleSubmitCallback } from '../../../hooks/useForm'
import { userCurrentSchema, userSchema } from '../../../models/User' import { userCurrentSchema, userSchema } from '../../../models/User'
import { userSettingsSchema } from '../../../models/UserSettings' import { userSettingsSchema } from '../../../models/UserSettings'
import { useGuilds } from '../../../contexts/Guilds'
export const UserSettings: React.FC = () => { export const UserSettings: React.FC = () => {
const { user, setUser, authentication } = useAuthentication() const { user, setUser, authentication } = useAuthentication()
const { guilds } = useGuilds()
const { t } = useTranslation() const { t } = useTranslation()
const [inputValues, setInputValues] = useState({ const [inputValues, setInputValues] = useState({
name: user.name, name: user.name,
@ -212,20 +209,6 @@ export const UserSettings: React.FC = () => {
/> />
</div> </div>
</div> </div>
<div className='mt-10 ml-0 flex flex-col items-center lg:ml-24 lg:mt-0'>
<UserProfileGuilds
isPublicGuilds={inputValues.isPublicGuilds}
guilds={guilds}
/>
<Checkbox
name='isPublicGuilds'
label={t('application:label-checkbox-guilds')}
onChange={onChangeCheckbox}
checked={inputValues.isPublicGuilds}
id='checkbox-public-guilds'
className='px-8'
/>
</div>
</div> </div>
<div className='mt-12 flex w-full flex-col items-center justify-between sm:w-fit lg:flex-row'> <div className='mt-12 flex w-full flex-col items-center justify-between sm:w-fit lg:flex-row'>
<div className='w-4/5 pr-0 sm:w-[450px] lg:border-r-[1px] lg:border-neutral-700 lg:pr-12'> <div className='w-4/5 pr-0 sm:w-[450px] lg:border-r-[1px] lg:border-neutral-700 lg:pr-12'>

View File

@ -4,7 +4,6 @@ import useTranslation from 'next-translate/useTranslation'
import { useTheme } from 'next-themes' import { useTheme } from 'next-themes'
import axios from 'axios' import axios from 'axios'
import { SocialMediaButton } from '../design/SocialMediaButton'
import { Main } from '../design/Main' import { Main } from '../design/Main'
import { Input } from '../design/Input' import { Input } from '../design/Input'
import { Button } from '../design/Button' import { Button } from '../design/Button'
@ -17,6 +16,7 @@ import {
Authentication as AuthenticationClass Authentication as AuthenticationClass
} from '../../tools/authentication' } from '../../tools/authentication'
import { useForm, HandleSubmitCallback } from '../../hooks/useForm' import { useForm, HandleSubmitCallback } from '../../hooks/useForm'
import { AuthenticationSocialMedia } from './AuthenticationSocialMedia'
export interface AuthenticationProps { export interface AuthenticationProps {
mode: 'signup' | 'signin' mode: 'signup' | 'signin'
@ -93,13 +93,7 @@ export const Authentication: React.FC<AuthenticationProps> = (props) => {
return ( return (
<Main> <Main>
<div className='flex flex-col sm:w-full sm:items-center'> <AuthenticationSocialMedia />
<div className='flex flex-col items-center justify-center space-y-6 sm:w-4/6 sm:flex-row sm:space-x-6 sm:space-y-0'>
<SocialMediaButton socialMedia='Google' />
<SocialMediaButton socialMedia='GitHub' />
<SocialMediaButton socialMedia='Discord' />
</div>
</div>
<div className='pt-8 text-center font-paragraph text-lg'> <div className='pt-8 text-center font-paragraph text-lg'>
{t('authentication:or')} {t('authentication:or')}
</div> </div>

View File

@ -0,0 +1,50 @@
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import { api } from '../../tools/api'
import { Authentication, isTokens } from '../../tools/authentication'
import { SocialMediaButton, SocialMedia } from '../design/SocialMediaButton'
export const AuthenticationSocialMedia: React.FC = () => {
const router = useRouter()
const handleAuthentication = (
socialMedia: SocialMedia
): (() => Promise<void>) => {
return async () => {
const redirect = window.location.href
const { data: url } = await api.get(
`/users/oauth2/${socialMedia.toLowerCase()}/signin?redirectURI=${redirect}`
)
window.location.href = url
}
}
useEffect(() => {
const data = router.query
if (isTokens(data)) {
const authentication = new Authentication(data)
authentication.signin()
router.push('/application').catch(() => {})
}
}, [router])
return (
<div className='flex flex-col sm:w-full sm:items-center'>
<div className='flex flex-col items-center justify-center space-y-6 sm:w-4/6 sm:flex-row sm:space-x-6 sm:space-y-0'>
<SocialMediaButton
socialMedia='Google'
onClick={handleAuthentication('Google')}
/>
<SocialMediaButton
socialMedia='GitHub'
onClick={handleAuthentication('GitHub')}
/>
<SocialMediaButton
socialMedia='Discord'
onClick={handleAuthentication('Discord')}
/>
</div>
</div>
)
}

View File

@ -1,15 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { Emoji as Component, EmojiProps } from './Emoji'
const Stories: Meta = {
title: 'Emoji',
component: Component
}
export default Stories
export const Emoji: Story<EmojiProps> = (arguments_) => {
return <Component {...arguments_} />
}
Emoji.args = { value: ':wave:', size: 20 }

View File

@ -1,15 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { EmojiPicker as Component, EmojiPickerProps } from './EmojiPicker'
const Stories: Meta = {
title: 'EmojiPicker',
component: Component
}
export default Stories
export const EmojiPicker: Story<EmojiPickerProps> = (arguments_) => {
return <Component {...arguments_} />
}
EmojiPicker.args = { onClick: (emoji, event) => console.log(emoji, event) }

View File

@ -1,15 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { ErrorPage as Component, ErrorPageProps } from './ErrorPage'
const Stories: Meta = {
title: 'ErrorPage',
component: Component
}
export default Stories
export const ErrorPage: Story<ErrorPageProps> = (arguments_) => {
return <Component {...arguments_} />
}
ErrorPage.args = { message: 'message content', statusCode: 404 }

View File

@ -1,15 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { Footer as Component, FooterProps } from '.'
const Stories: Meta = {
title: 'Footer',
component: Component
}
export default Stories
export const Footer: Story<FooterProps> = (arguments_) => (
<Component {...arguments_} />
)
Footer.args = { version: '1.0.0' }

View File

@ -1,12 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { Header as Component } from '.'
const Stories: Meta = {
title: 'Header',
component: Component
}
export default Stories
export const Header: Story = (arguments_) => <Component {...arguments_} />

View File

@ -1,15 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { Button as Component, ButtonProps } from './Button'
const Stories: Meta = {
title: 'Button',
component: Component
}
export default Stories
export const Button: Story<ButtonProps> = (arguments_) => (
<Component {...arguments_} />
)
Button.args = { children: 'Get started' }

View File

@ -1,15 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { Checkbox as Component, CheckboxProps } from './Checkbox'
const Stories: Meta = {
title: 'Checkbox',
component: Component
}
export default Stories
export const Checkbox: Story<CheckboxProps> = (arguments_) => {
return <Component {...arguments_} />
}
Checkbox.args = { label: 'Checkbox' }

View File

@ -1,12 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { Divider as Component } from '.'
const Stories: Meta = {
title: 'Divider',
component: Component
}
export default Stories
export const Divider: Story = (arguments_) => <Component {...arguments_} />

View File

@ -1,30 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { Input, InputProps } from './Input'
import { AuthenticationForm } from '../../Authentication/AuthenticationForm'
const Stories: Meta = {
title: 'Input',
component: Input
}
export default Stories
const Template: Story<InputProps> = (arguments_) => (
<AuthenticationForm>
<Input {...arguments_} />
</AuthenticationForm>
)
export const Text = Template.bind({})
Text.args = { label: 'Text', name: 'text', type: 'text' }
export const Password = Template.bind({})
Password.args = { label: 'Password', name: 'password', type: 'password' }
export const Error = Template.bind({})
Error.args = {
label: 'Error',
type: 'text',
error: 'Oops, this field is required 🙈.'
}

View File

@ -1,14 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { Loader as Component, LoaderProps } from './Loader'
const Stories: Meta = {
title: 'Loader',
component: Component
}
export default Stories
export const Loader: Story<LoaderProps> = (arguments_) => (
<Component {...arguments_} />
)

View File

@ -1,23 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { SocialMediaButton, SocialMediaButtonProps } from './SocialMediaButton'
const Stories: Meta = {
title: 'SocialMediaButton',
component: SocialMediaButton
}
export default Stories
const Template: Story<SocialMediaButtonProps> = (arguments_) => (
<SocialMediaButton {...arguments_} />
)
export const Github = Template.bind({})
Github.args = { socialMedia: 'GitHub' }
export const Discord = Template.bind({})
Discord.args = { socialMedia: 'Discord' }
export const Google = Template.bind({})
Google.args = { socialMedia: 'Google' }

View File

@ -1,15 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { Textarea as Component, TextareaProps } from './Textarea'
const Stories: Meta = {
title: 'Textarea',
component: Component
}
export default Stories
export const Textarea: Story<TextareaProps> = (arguments_) => {
return <Component {...arguments_} />
}
Textarea.args = { label: 'Textarea' }

View File

@ -1,11 +1,11 @@
import { createContext, useContext, useEffect } from 'react' import { createContext, useContext, useEffect } from 'react'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { NextPage, usePagination } from 'hooks/usePagination' import { NextPage, usePagination } from '../hooks/usePagination'
import { useAuthentication } from 'tools/authentication' import { useAuthentication } from '../tools/authentication'
import { Channel, ChannelWithDefaultChannelId } from 'models/Channel' import { Channel, ChannelWithDefaultChannelId } from '../models/Channel'
import { GuildsChannelsPath } from 'components/Application' import { GuildsChannelsPath } from '../components/Application'
import { handleSocketData, SocketData } from 'tools/handleSocketData' import { handleSocketData, SocketData } from '../tools/handleSocketData'
export interface Channels { export interface Channels {
channels: Channel[] channels: Channel[]

View File

@ -1,10 +1,10 @@
import { createContext, useContext, useEffect, useState } from 'react' import { createContext, useContext, useEffect, useState } from 'react'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { GuildWithDefaultChannelId } from 'models/Guild' import { GuildWithDefaultChannelId } from '../models/Guild'
import { Member } from 'models/Member' import { Member } from '../models/Member'
import { useAuthentication } from 'tools/authentication' import { useAuthentication } from '../tools/authentication'
import { SocketData } from 'tools/handleSocketData' import { SocketData } from '../tools/handleSocketData'
export interface GuildMember { export interface GuildMember {
guild: GuildWithDefaultChannelId guild: GuildWithDefaultChannelId

View File

@ -1,9 +1,9 @@
import { createContext, useContext, useEffect } from 'react' import { createContext, useContext, useEffect } from 'react'
import { NextPage, usePagination } from 'hooks/usePagination' import { NextPage, usePagination } from '../hooks/usePagination'
import { useAuthentication } from 'tools/authentication' import { useAuthentication } from '../tools/authentication'
import { GuildWithDefaultChannelId } from 'models/Guild' import { GuildWithDefaultChannelId } from '../models/Guild'
import { handleSocketData, SocketData } from 'tools/handleSocketData' import { handleSocketData, SocketData } from '../tools/handleSocketData'
export interface Guilds { export interface Guilds {
guilds: GuildWithDefaultChannelId[] guilds: GuildWithDefaultChannelId[]

View File

@ -1,11 +1,11 @@
import { createContext, useContext, useEffect } from 'react' import { createContext, useContext, useEffect } from 'react'
import { NextPage, usePagination } from 'hooks/usePagination' import { NextPage, usePagination } from '../hooks/usePagination'
import { useAuthentication } from 'tools/authentication' import { useAuthentication } from '../tools/authentication'
import { MemberWithPublicUser } from 'models/Member' import { MemberWithPublicUser } from '../models/Member'
import { GuildsChannelsPath } from 'components/Application' import { GuildsChannelsPath } from '../components/Application'
import { handleSocketData, SocketData } from 'tools/handleSocketData' import { handleSocketData, SocketData } from '../tools/handleSocketData'
import { User } from 'models/User' import { User } from '../models/User'
export interface Members { export interface Members {
members: MemberWithPublicUser[] members: MemberWithPublicUser[]

View File

@ -1,10 +1,10 @@
import { createContext, useContext, useEffect } from 'react' import { createContext, useContext, useEffect } from 'react'
import { NextPage, usePagination } from 'hooks/usePagination' import { NextPage, usePagination } from '../hooks/usePagination'
import { useAuthentication } from 'tools/authentication' import { useAuthentication } from '../tools/authentication'
import { MessageWithMember } from 'models/Message' import { MessageWithMember } from '../models/Message'
import { GuildsChannelsPath } from 'components/Application' import { GuildsChannelsPath } from '../components/Application'
import { handleSocketData, SocketData } from 'tools/handleSocketData' import { handleSocketData, SocketData } from '../tools/handleSocketData'
export interface Messages { export interface Messages {
messages: MessageWithMember[] messages: MessageWithMember[]

View File

@ -1,5 +1,4 @@
import { deleteLeaveMembersWithGuildIdHandler } from 'cypress/fixtures/guilds/[guildId]/members/leave' import { deleteLeaveMembersWithGuildIdHandler } from '../../../../fixtures/guilds/[guildId]/members/leave'
import { guildExample } from '../../../../fixtures/guilds/guild' import { guildExample } from '../../../../fixtures/guilds/guild'
import { import {
getGuildMemberNotOwnerWithGuildIdHandler, getGuildMemberNotOwnerWithGuildIdHandler,

View File

@ -1,15 +0,0 @@
import { Meta, Story } from '@storybook/react'
import { {{ properCase name }} as Component, {{ properCase name }}Props } from './{{ properCase name }}'
const Stories: Meta = {
title: '{{ properCase name }}',
component: Component
}
export default Stories
export const {{ properCase name }}: Story<{{ properCase name }}Props> = (arguments_) => {
return <Component {...arguments_} />
}
{{ properCase name }}.args = { className: 'text-center' }

36605
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -27,9 +27,6 @@
"test:lighthouse": "lhci autorun", "test:lighthouse": "lhci autorun",
"test:e2e": "start-server-and-test \"start\" \"http://localhost:3000\" \"cypress run\"", "test:e2e": "start-server-and-test \"start\" \"http://localhost:3000\" \"cypress run\"",
"test:e2e:dev": "start-server-and-test \"dev\" \"http://localhost:3000\" \"cypress open\"", "test:e2e:dev": "start-server-and-test \"dev\" \"http://localhost:3000\" \"cypress open\"",
"storybook": "start-storybook --port 6006",
"storybook:build": "build-storybook",
"storybook:serve": "serve -p 6006 storybook-static",
"release": "semantic-release", "release": "semantic-release",
"deploy": "vercel", "deploy": "vercel",
"postinstall": "husky install" "postinstall": "husky install"
@ -41,13 +38,13 @@
"@sinclair/typebox": "0.23.4", "@sinclair/typebox": "0.23.4",
"ajv": "8.10.0", "ajv": "8.10.0",
"ajv-formats": "2.1.1", "ajv-formats": "2.1.1",
"axios": "0.26.0", "axios": "0.26.1",
"classnames": "2.3.1", "classnames": "2.3.1",
"date-and-time": "2.2.1", "date-and-time": "2.3.0",
"emoji-mart": "3.0.1", "emoji-mart": "3.0.1",
"katex": "0.15.2", "katex": "0.15.3",
"next": "12.1.0", "next": "12.1.0",
"next-pwa": "5.4.5", "next-pwa": "5.4.6",
"next-themes": "0.1.1", "next-themes": "0.1.1",
"next-translate": "1.3.5", "next-translate": "1.3.5",
"pretty-bytes": "6.0.0", "pretty-bytes": "6.0.0",
@ -55,7 +52,7 @@
"react-component-form": "2.0.0", "react-component-form": "2.0.0",
"react-dom": "17.0.2", "react-dom": "17.0.2",
"react-infinite-scroll-component": "6.1.0", "react-infinite-scroll-component": "6.1.0",
"react-markdown": "8.0.0", "react-markdown": "8.0.1",
"react-responsive": "8.2.0", "react-responsive": "8.2.0",
"react-swipeable": "6.2.0", "react-swipeable": "6.2.0",
"react-textarea-autosize": "8.3.3", "react-textarea-autosize": "8.3.3",
@ -64,66 +61,56 @@
"remark-breaks": "3.0.2", "remark-breaks": "3.0.2",
"remark-gfm": "3.0.1", "remark-gfm": "3.0.1",
"remark-math": "5.1.1", "remark-math": "5.1.1",
"sharp": "0.30.2", "sharp": "0.30.3",
"socket.io-client": "4.4.1", "socket.io-client": "4.4.1",
"unified": "10.1.1", "unified": "10.1.2",
"unist-util-visit": "4.1.0", "unist-util-visit": "4.1.0",
"universal-cookie": "4.0.4" "universal-cookie": "4.0.4"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "16.2.1", "@commitlint/cli": "16.2.3",
"@commitlint/config-conventional": "16.2.1", "@commitlint/config-conventional": "16.2.1",
"@lhci/cli": "0.9.0", "@lhci/cli": "0.9.0",
"@saithodev/semantic-release-backmerge": "2.1.2", "@saithodev/semantic-release-backmerge": "2.1.2",
"@storybook/addon-essentials": "6.4.19",
"@storybook/addon-links": "6.4.19",
"@storybook/addon-postcss": "2.0.0",
"@storybook/builder-webpack5": "6.4.19",
"@storybook/manager-webpack5": "6.4.19",
"@storybook/react": "6.4.19",
"@testing-library/jest-dom": "5.16.2", "@testing-library/jest-dom": "5.16.2",
"@testing-library/react": "12.1.3", "@testing-library/react": "12.1.4",
"@types/date-and-time": "0.13.0",
"@types/emoji-mart": "3.0.9", "@types/emoji-mart": "3.0.9",
"@types/hast": "2.3.4", "@types/hast": "2.3.4",
"@types/jest": "27.4.1", "@types/jest": "27.4.1",
"@types/katex": "0.11.1", "@types/katex": "0.11.1",
"@types/node": "17.0.21", "@types/node": "17.0.21",
"@types/react": "17.0.39", "@types/react": "17.0.40",
"@types/react-responsive": "8.0.5", "@types/react-responsive": "8.0.5",
"@types/unist": "2.0.6", "@types/unist": "2.0.6",
"@typescript-eslint/eslint-plugin": "5.13.0", "@typescript-eslint/eslint-plugin": "5.15.0",
"@typescript-eslint/parser": "5.13.0", "@typescript-eslint/parser": "5.15.0",
"autoprefixer": "10.4.2", "autoprefixer": "10.4.4",
"cypress": "9.5.1", "cypress": "9.5.2",
"editorconfig-checker": "4.0.2", "editorconfig-checker": "4.0.2",
"eslint": "8.10.0", "eslint": "8.11.0",
"eslint-config-conventions": "1.1.0", "eslint-config-conventions": "1.1.1",
"eslint-config-next": "12.1.0", "eslint-config-next": "12.1.0",
"eslint-config-prettier": "8.5.0", "eslint-config-prettier": "8.5.0",
"eslint-plugin-import": "2.25.4", "eslint-plugin-import": "2.25.4",
"eslint-plugin-prettier": "4.0.0", "eslint-plugin-prettier": "4.0.0",
"eslint-plugin-promise": "6.0.0", "eslint-plugin-promise": "6.0.0",
"eslint-plugin-storybook": "0.5.7",
"eslint-plugin-unicorn": "41.0.0", "eslint-plugin-unicorn": "41.0.0",
"html-w3c-validator": "1.1.0", "html-w3c-validator": "1.1.0",
"husky": "7.0.4", "husky": "7.0.4",
"jest": "27.5.1", "jest": "27.5.1",
"lint-staged": "12.3.5", "lint-staged": "12.3.6",
"markdownlint-cli": "0.31.1", "markdownlint-cli": "0.31.1",
"mockttp": "2.6.0", "mockttp": "2.7.0",
"next-secure-headers": "2.2.0", "next-secure-headers": "2.2.0",
"plop": "3.0.5", "plop": "3.0.5",
"postcss": "8.4.7", "postcss": "8.4.12",
"prettier": "2.5.1", "prettier": "2.6.0",
"prettier-plugin-tailwindcss": "0.1.8", "prettier-plugin-tailwindcss": "0.1.8",
"semantic-release": "19.0.2", "semantic-release": "19.0.2",
"serve": "13.0.2", "serve": "13.0.2",
"start-server-and-test": "1.14.0", "start-server-and-test": "1.14.0",
"storybook-tailwind-dark-mode": "1.0.11",
"tailwindcss": "3.0.23", "tailwindcss": "3.0.23",
"typescript": "4.6.2", "typescript": "4.6.2",
"vercel": "24.0.0", "vercel": "24.0.0"
"webpack": "5.70.0"
} }
} }

View File

@ -1,10 +1,10 @@
import { GetStaticProps, NextPage } from 'next' import { GetStaticProps, NextPage } from 'next'
import useTranslation from 'next-translate/useTranslation' import useTranslation from 'next-translate/useTranslation'
import { ErrorPage } from 'components/ErrorPage' import { ErrorPage } from '../components/ErrorPage'
import { Head } from 'components/Head' import { Head } from '../components/Head'
import { Header } from 'components/Header' import { Header } from '../components/Header'
import { Footer, FooterProps } from 'components/Footer' import { Footer, FooterProps } from '../components/Footer'
const Error404: NextPage<FooterProps> = (props) => { const Error404: NextPage<FooterProps> = (props) => {
const { t } = useTranslation() const { t } = useTranslation()

View File

@ -1,10 +1,10 @@
import { GetStaticProps, NextPage } from 'next' import { GetStaticProps, NextPage } from 'next'
import useTranslation from 'next-translate/useTranslation' import useTranslation from 'next-translate/useTranslation'
import { ErrorPage } from 'components/ErrorPage' import { ErrorPage } from '../components/ErrorPage'
import { Head } from 'components/Head' import { Head } from '../components/Head'
import { Header } from 'components/Header' import { Header } from '../components/Header'
import { Footer, FooterProps } from 'components/Footer' import { Footer, FooterProps } from '../components/Footer'
const Error500: NextPage<FooterProps> = (props) => { const Error500: NextPage<FooterProps> = (props) => {
const { t } = useTranslation() const { t } = useTranslation()

View File

@ -3,7 +3,7 @@ import { AppProps } from 'next/app'
import { ThemeProvider } from 'next-themes' import { ThemeProvider } from 'next-themes'
import useTranslation from 'next-translate/useTranslation' import useTranslation from 'next-translate/useTranslation'
import 'styles/global.css' import '../styles/global.css'
import '@fontsource/montserrat/400.css' import '@fontsource/montserrat/400.css'
import '@fontsource/montserrat/500.css' import '@fontsource/montserrat/500.css'
@ -13,7 +13,7 @@ import '@fontsource/montserrat/700.css'
import '@fontsource/roboto/400.css' import '@fontsource/roboto/400.css'
import '@fontsource/roboto/700.css' import '@fontsource/roboto/700.css'
import { cookies } from 'tools/cookies' import { cookies } from '../tools/cookies'
const Application = ({ Component, pageProps }: AppProps): JSX.Element => { const Application = ({ Component, pageProps }: AppProps): JSX.Element => {
const { lang } = useTranslation() const { lang } = useTranslation()

View File

@ -1,21 +1,24 @@
import { NextPage } from 'next' import { NextPage } from 'next'
import { Head } from 'components/Head' import { Head } from '../../../../components/Head'
import { Application } from 'components/Application' import { Application } from '../../../../components/Application'
import { Messages } from 'components/Application/Messages' import { Messages } from '../../../../components/Application/Messages'
import { SendMessage } from 'components/Application/SendMessage' import { SendMessage } from '../../../../components/Application/SendMessage'
import { import {
authenticationFromServerSide, authenticationFromServerSide,
AuthenticationProvider, AuthenticationProvider,
PagePropsWithAuthentication PagePropsWithAuthentication
} from 'tools/authentication' } from '../../../../tools/authentication'
import { GuildMember, GuildMemberProvider } from 'contexts/GuildMember' import {
import { GuildLeftSidebar } from 'components/Application/GuildLeftSidebar' GuildMember,
import { ChannelsProvider } from 'contexts/Channels' GuildMemberProvider
import { GuildsProvider } from 'contexts/Guilds' } from '../../../../contexts/GuildMember'
import { Channel } from 'models/Channel' import { GuildLeftSidebar } from '../../../../components/Application/GuildLeftSidebar'
import { MessagesProvider } from 'contexts/Messages' import { ChannelsProvider } from '../../../../contexts/Channels'
import { MembersProviders } from 'contexts/Members' import { GuildsProvider } from '../../../../contexts/Guilds'
import { Channel } from '../../../../models/Channel'
import { MessagesProvider } from '../../../../contexts/Messages'
import { MembersProviders } from '../../../../contexts/Members'
export interface ChannelPageProps extends PagePropsWithAuthentication { export interface ChannelPageProps extends PagePropsWithAuthentication {
channelId: number channelId: number

View File

@ -1,19 +1,22 @@
import { NextPage } from 'next' import { NextPage } from 'next'
import { Head } from 'components/Head' import { Head } from '../../../../components/Head'
import { Application } from 'components/Application' import { Application } from '../../../../components/Application'
import { import {
authenticationFromServerSide, authenticationFromServerSide,
AuthenticationProvider, AuthenticationProvider,
PagePropsWithAuthentication PagePropsWithAuthentication
} from 'tools/authentication' } from '../../../../tools/authentication'
import { GuildMember, GuildMemberProvider } from 'contexts/GuildMember' import {
import { GuildLeftSidebar } from 'components/Application/GuildLeftSidebar' GuildMember,
import { ChannelSettings } from 'components/Application/ChannelSettings' GuildMemberProvider
import { ChannelsProvider } from 'contexts/Channels' } from '../../../../contexts/GuildMember'
import { GuildsProvider } from 'contexts/Guilds' import { GuildLeftSidebar } from '../../../../components/Application/GuildLeftSidebar'
import { Channel } from 'models/Channel' import { ChannelSettings } from '../../../../components/Application/ChannelSettings'
import { MembersProviders } from 'contexts/Members' import { ChannelsProvider } from '../../../../contexts/Channels'
import { GuildsProvider } from '../../../../contexts/Guilds'
import { Channel } from '../../../../models/Channel'
import { MembersProviders } from '../../../../contexts/Members'
export interface ChannelSettingsPageProps extends PagePropsWithAuthentication { export interface ChannelSettingsPageProps extends PagePropsWithAuthentication {
channelId: number channelId: number

View File

@ -1,15 +1,18 @@
import { NextPage } from 'next' import { NextPage } from 'next'
import { Head } from 'components/Head' import { Head } from '../../../../components/Head'
import { Application } from 'components/Application' import { Application } from '../../../../components/Application'
import { import {
authenticationFromServerSide, authenticationFromServerSide,
AuthenticationProvider, AuthenticationProvider,
PagePropsWithAuthentication PagePropsWithAuthentication
} from 'tools/authentication' } from '../../../../tools/authentication'
import { CreateChannel } from 'components/Application/CreateChannel' import { CreateChannel } from '../../../../components/Application/CreateChannel'
import { GuildsProvider } from 'contexts/Guilds' import { GuildsProvider } from '../../../../contexts/Guilds'
import { GuildMember, GuildMemberProvider } from 'contexts/GuildMember' import {
GuildMember,
GuildMemberProvider
} from '../../../../contexts/GuildMember'
export interface CreateChannelPageProps extends PagePropsWithAuthentication { export interface CreateChannelPageProps extends PagePropsWithAuthentication {
guildId: number guildId: number

View File

@ -1,15 +1,15 @@
import { NextPage } from 'next' import { NextPage } from 'next'
import { Head } from 'components/Head' import { Head } from '../../../components/Head'
import { Application } from 'components/Application' import { Application } from '../../../components/Application'
import { import {
authenticationFromServerSide, authenticationFromServerSide,
AuthenticationProvider, AuthenticationProvider,
PagePropsWithAuthentication PagePropsWithAuthentication
} from 'tools/authentication' } from '../../../tools/authentication'
import { GuildMember, GuildMemberProvider } from 'contexts/GuildMember' import { GuildMember, GuildMemberProvider } from '../../../contexts/GuildMember'
import { GuildsProvider } from 'contexts/Guilds' import { GuildsProvider } from '../../../contexts/Guilds'
import { GuildSettings } from 'components/Application/GuildSettings' import { GuildSettings } from '../../../components/Application/GuildSettings'
export interface GuildSettingsPageProps extends PagePropsWithAuthentication { export interface GuildSettingsPageProps extends PagePropsWithAuthentication {
guildId: number guildId: number

View File

@ -1,15 +1,15 @@
import { NextPage } from 'next' import { NextPage } from 'next'
import useTranslation from 'next-translate/useTranslation' import useTranslation from 'next-translate/useTranslation'
import { Head } from 'components/Head' import { Head } from '../../../components/Head'
import { Application } from 'components/Application' import { Application } from '../../../components/Application'
import { import {
authenticationFromServerSide, authenticationFromServerSide,
AuthenticationProvider, AuthenticationProvider,
PagePropsWithAuthentication PagePropsWithAuthentication
} from 'tools/authentication' } from '../../../tools/authentication'
import { CreateGuild } from 'components/Application/CreateGuild' import { CreateGuild } from '../../../components/Application/CreateGuild'
import { GuildsProvider } from 'contexts/Guilds' import { GuildsProvider } from '../../../contexts/Guilds'
const CreateGuildPage: NextPage<PagePropsWithAuthentication> = (props) => { const CreateGuildPage: NextPage<PagePropsWithAuthentication> = (props) => {
const { t } = useTranslation() const { t } = useTranslation()

View File

@ -1,15 +1,15 @@
import { NextPage } from 'next' import { NextPage } from 'next'
import useTranslation from 'next-translate/useTranslation' import useTranslation from 'next-translate/useTranslation'
import { Head } from 'components/Head' import { Head } from '../../../components/Head'
import { Application } from 'components/Application' import { Application } from '../../../components/Application'
import { import {
authenticationFromServerSide, authenticationFromServerSide,
AuthenticationProvider, AuthenticationProvider,
PagePropsWithAuthentication PagePropsWithAuthentication
} from 'tools/authentication' } from '../../../tools/authentication'
import { JoinGuildsPublic } from 'components/Application/JoinGuildsPublic' import { JoinGuildsPublic } from '../../../components/Application/JoinGuildsPublic'
import { GuildsProvider } from 'contexts/Guilds' import { GuildsProvider } from '../../../contexts/Guilds'
const JoinGuildPage: NextPage<PagePropsWithAuthentication> = (props) => { const JoinGuildPage: NextPage<PagePropsWithAuthentication> = (props) => {
const { t } = useTranslation() const { t } = useTranslation()

View File

@ -1,14 +1,14 @@
import { NextPage } from 'next' import { NextPage } from 'next'
import { Head } from 'components/Head' import { Head } from '../../components/Head'
import { Application } from 'components/Application' import { Application } from '../../components/Application'
import { PopupGuild } from 'components/Application/PopupGuild' import { PopupGuild } from '../../components/Application/PopupGuild'
import { import {
authenticationFromServerSide, authenticationFromServerSide,
AuthenticationProvider, AuthenticationProvider,
PagePropsWithAuthentication PagePropsWithAuthentication
} from 'tools/authentication' } from '../../tools/authentication'
import { GuildsProvider } from 'contexts/Guilds' import { GuildsProvider } from '../../contexts/Guilds'
const ApplicationPage: NextPage<PagePropsWithAuthentication> = (props) => { const ApplicationPage: NextPage<PagePropsWithAuthentication> = (props) => {
return ( return (

View File

@ -1,16 +1,16 @@
import { NextPage } from 'next' import { NextPage } from 'next'
import { Head } from 'components/Head' import { Head } from '../../../../components/Head'
import { Application } from 'components/Application' import { Application } from '../../../../components/Application'
import { import {
authenticationFromServerSide, authenticationFromServerSide,
AuthenticationProvider, AuthenticationProvider,
PagePropsWithAuthentication PagePropsWithAuthentication
} from 'tools/authentication' } from '../../../../tools/authentication'
import { UserProfile } from 'components/Application/UserProfile' import { UserProfile } from '../../../../components/Application/UserProfile'
import { GuildsProvider } from 'contexts/Guilds' import { GuildsProvider } from '../../../../contexts/Guilds'
import { UserPublic } from 'models/User' import { UserPublic } from '../../../../models/User'
import { Guild } from 'models/Guild' import { Guild } from '../../../../models/Guild'
export interface UserProfilePageProps extends PagePropsWithAuthentication { export interface UserProfilePageProps extends PagePropsWithAuthentication {
user: UserPublic user: UserPublic

View File

@ -1,14 +1,14 @@
import { NextPage } from 'next' import { NextPage } from 'next'
import { Head } from 'components/Head' import { Head } from '../../../components/Head'
import { Application } from 'components/Application' import { Application } from '../../../components/Application'
import { import {
authenticationFromServerSide, authenticationFromServerSide,
AuthenticationProvider, AuthenticationProvider,
PagePropsWithAuthentication PagePropsWithAuthentication
} from 'tools/authentication' } from '../../../tools/authentication'
import { UserSettings } from 'components/Application/UserSettings' import { UserSettings } from '../../../components/Application/UserSettings'
import { GuildsProvider } from 'contexts/Guilds' import { GuildsProvider } from '../../../contexts/Guilds'
const UserSettingsPage: NextPage<PagePropsWithAuthentication> = (props) => { const UserSettingsPage: NextPage<PagePropsWithAuthentication> = (props) => {
return ( return (

View File

@ -3,19 +3,19 @@ import Link from 'next/link'
import useTranslation from 'next-translate/useTranslation' import useTranslation from 'next-translate/useTranslation'
import axios from 'axios' import axios from 'axios'
import { AuthenticationForm } from 'components/Authentication' import { AuthenticationForm } from '../../components/Authentication'
import { Head } from 'components/Head' import { Head } from '../../components/Head'
import { Header } from 'components/Header' import { Header } from '../../components/Header'
import { Main } from 'components/design/Main' import { Main } from '../../components/design/Main'
import { Footer, FooterProps } from 'components/Footer' import { Footer, FooterProps } from '../../components/Footer'
import { Input } from 'components/design/Input' import { Input } from '../../components/design/Input'
import { Button } from 'components/design/Button' import { Button } from '../../components/design/Button'
import { FormState } from 'components/design/FormState' import { FormState } from '../../components/design/FormState'
import { authenticationFromServerSide } from 'tools/authentication' import { authenticationFromServerSide } from '../../tools/authentication'
import { ScrollableBody } from 'components/ScrollableBody' import { ScrollableBody } from '../../components/ScrollableBody'
import { userSchema } from 'models/User' import { userSchema } from '../../models/User'
import { api } from 'tools/api' import { api } from '../../tools/api'
import { HandleSubmitCallback, useForm } from 'hooks/useForm' import { HandleSubmitCallback, useForm } from '../../hooks/useForm'
const ForgotPassword: NextPage<FooterProps> = (props) => { const ForgotPassword: NextPage<FooterProps> = (props) => {
const { t } = useTranslation() const { t } = useTranslation()

View File

@ -3,19 +3,19 @@ import { useRouter } from 'next/router'
import useTranslation from 'next-translate/useTranslation' import useTranslation from 'next-translate/useTranslation'
import axios from 'axios' import axios from 'axios'
import { Head } from 'components/Head' import { Head } from '../../components/Head'
import { Header } from 'components/Header' import { Header } from '../../components/Header'
import { Main } from 'components/design/Main' import { FormState } from '../../components/design/FormState'
import { Footer, FooterProps } from 'components/Footer' import { Main } from '../../components/design/Main'
import { Input } from 'components/design/Input' import { Footer, FooterProps } from '../../components/Footer'
import { Button } from 'components/design/Button' import { Input } from '../../components/design/Input'
import { FormState } from 'components/design/FormState' import { Button } from '../../components/design/Button'
import { authenticationFromServerSide } from 'tools/authentication' import { authenticationFromServerSide } from '../../tools/authentication'
import { AuthenticationForm } from 'components/Authentication' import { AuthenticationForm } from '../../components/Authentication'
import { ScrollableBody } from 'components/ScrollableBody/ScrollableBody' import { ScrollableBody } from '../../components/ScrollableBody/ScrollableBody'
import { HandleSubmitCallback, useForm } from 'hooks/useForm' import { HandleSubmitCallback, useForm } from '../../hooks/useForm'
import { api } from 'tools/api' import { api } from '../../tools/api'
import { userSchema } from 'models/User' import { userSchema } from '../../models/User'
const ResetPassword: NextPage<FooterProps> = (props) => { const ResetPassword: NextPage<FooterProps> = (props) => {
const { t } = useTranslation() const { t } = useTranslation()

View File

@ -1,12 +1,12 @@
import { NextPage } from 'next' import { NextPage } from 'next'
import useTranslation from 'next-translate/useTranslation' import useTranslation from 'next-translate/useTranslation'
import { Head } from 'components/Head' import { Head } from '../../components/Head'
import { Authentication } from 'components/Authentication' import { Authentication } from '../../components/Authentication'
import { Header } from 'components/Header' import { Header } from '../../components/Header'
import { Footer, FooterProps } from 'components/Footer' import { Footer, FooterProps } from '../../components/Footer'
import { authenticationFromServerSide } from 'tools/authentication' import { authenticationFromServerSide } from '../../tools/authentication'
import { ScrollableBody } from 'components/ScrollableBody' import { ScrollableBody } from '../../components/ScrollableBody'
const Signin: NextPage<FooterProps> = (props) => { const Signin: NextPage<FooterProps> = (props) => {
const { version } = props const { version } = props

View File

@ -1,12 +1,12 @@
import { NextPage } from 'next' import { NextPage } from 'next'
import useTranslation from 'next-translate/useTranslation' import useTranslation from 'next-translate/useTranslation'
import { Head } from 'components/Head' import { Head } from '../../components/Head'
import { Authentication } from 'components/Authentication' import { Authentication } from '../../components/Authentication'
import { Header } from 'components/Header' import { Header } from '../../components/Header'
import { Footer, FooterProps } from 'components/Footer' import { Footer, FooterProps } from '../../components/Footer'
import { authenticationFromServerSide } from 'tools/authentication' import { authenticationFromServerSide } from '../../tools/authentication'
import { ScrollableBody } from 'components/ScrollableBody' import { ScrollableBody } from '../../components/ScrollableBody'
const Signup: NextPage<FooterProps> = (props) => { const Signup: NextPage<FooterProps> = (props) => {
const { version } = props const { version } = props

View File

@ -4,13 +4,13 @@ import Image from 'next/image'
import Translation from 'next-translate/Trans' import Translation from 'next-translate/Trans'
import useTranslation from 'next-translate/useTranslation' import useTranslation from 'next-translate/useTranslation'
import { Head } from 'components/Head' import { Head } from '../components/Head'
import { Header } from 'components/Header' import { Header } from '../components/Header'
import { Main } from 'components/design/Main' import { Main } from '../components/design/Main'
import { Footer, FooterProps } from 'components/Footer' import { Footer, FooterProps } from '../components/Footer'
import { SocialMediaLink } from 'components/design/SocialMediaButton' import { SocialMediaLink } from '../components/design/SocialMediaButton'
import { ButtonLink } from 'components/design/Button' import { ButtonLink } from '../components/design/Button'
import { ScrollableBody } from 'components/ScrollableBody' import { ScrollableBody } from '../components/ScrollableBody'
const Home: NextPage<FooterProps> = (props) => { const Home: NextPage<FooterProps> = (props) => {
const { t } = useTranslation() const { t } = useTranslation()

View File

@ -6,16 +6,11 @@
@apply flex h-screen flex-col; @apply flex h-screen flex-col;
} }
body, body {
#preview-storybook {
@apply bg-white font-headline text-black dark:bg-black dark:text-white; @apply bg-white font-headline text-black dark:bg-black dark:text-white;
overflow: hidden; overflow: hidden;
} }
#preview-storybook {
@apply min-h-screen p-6;
}
.h-full-without-header { .h-full-without-header {
height: calc(100vh - 64px); height: calc(100vh - 64px);
} }

View File

@ -19,6 +19,15 @@ export interface PagePropsWithAuthentication {
} }
} }
export const isTokens = (data: { [key: string]: any }): data is Tokens => {
return (
'accessToken' in data &&
'refreshToken' in data &&
'type' in data &&
'expiresIn' in data
)
}
export * from './Authentication' export * from './Authentication'
export * from './authenticationFromServerSide' export * from './authenticationFromServerSide'
export * from './AuthenticationContext' export * from './AuthenticationContext'

View File

@ -11,7 +11,6 @@
"noEmit": true, "noEmit": true,
"strict": true, "strict": true,
"types": ["jest", "@testing-library/jest-dom", "@testing-library/react"], "types": ["jest", "@testing-library/jest-dom", "@testing-library/react"],
"baseUrl": ".",
"esModuleInterop": true, "esModuleInterop": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],