feat(contexts): add GuildMember

This commit is contained in:
Divlo
2021-12-28 19:18:47 +01:00
parent 3f6ab4a0ea
commit d8667b12a0
7 changed files with 137 additions and 66 deletions

View File

@ -1,13 +1,7 @@
import { useState, useEffect, useMemo } from 'react' import { useState, useEffect, useMemo } from 'react'
import Image from 'next/image' import Image from 'next/image'
import useTranslation from 'next-translate/useTranslation' import useTranslation from 'next-translate/useTranslation'
import { import { PlusIcon, MenuIcon, UsersIcon, XIcon } from '@heroicons/react/solid'
CogIcon,
PlusIcon,
MenuIcon,
UsersIcon,
XIcon
} from '@heroicons/react/solid'
import classNames from 'classnames' import classNames from 'classnames'
import { useMediaQuery } from 'react-responsive' import { useMediaQuery } from 'react-responsive'
import { useSwipeable } from 'react-swipeable' import { useSwipeable } from 'react-swipeable'
@ -15,7 +9,6 @@ import { useSwipeable } from 'react-swipeable'
import { Sidebar, DirectionSidebar } from './Sidebar' import { Sidebar, DirectionSidebar } from './Sidebar'
import { IconButton } from 'components/design/IconButton' import { IconButton } from 'components/design/IconButton'
import { IconLink } from 'components/design/IconLink' import { IconLink } from 'components/design/IconLink'
import { Channels } from './Channels'
import { Guilds } from './Guilds/Guilds' import { Guilds } from './Guilds/Guilds'
import { Divider } from '../design/Divider' import { Divider } from '../design/Divider'
import { Members } from './Members' import { Members } from './Members'
@ -27,17 +20,20 @@ export interface GuildsChannelsPath {
channelId: number channelId: number
} }
export type ApplicationPath =
| '/application'
| '/application/guilds/join'
| '/application/guilds/create'
| '/application/users/[userId]'
| GuildsChannelsPath
export interface ApplicationProps { export interface ApplicationProps {
path: path: ApplicationPath
| '/application' guildLeftSidebar?: React.ReactNode
| '/application/guilds/join'
| '/application/guilds/create'
| '/application/users/[userId]'
| GuildsChannelsPath
} }
export const Application: React.FC<ApplicationProps> = (props) => { export const Application: React.FC<ApplicationProps> = (props) => {
const { children, path } = props const { children, path, guildLeftSidebar } = props
const { t } = useTranslation() const { t } = useTranslation()
const { user } = useAuthentication() const { user } = useAuthentication()
@ -217,26 +213,7 @@ export const Application: React.FC<ApplicationProps> = (props) => {
<Guilds path={path} /> <Guilds path={path} />
</div> </div>
{typeof path !== 'string' && ( {guildLeftSidebar}
<div className='flex flex-col justify-between w-full mt-2'>
<div className='text-center p-2 mx-8 mt-2'>
<h2 className='text-xl'>Guild Name</h2>
</div>
<Divider />
<div className='scrollbar-firefox-support overflow-y-auto'>
<Channels path={path} />
</div>
<Divider />
<div className='flex justify-center items-center p-2 mb-1 space-x-6'>
<IconButton className='h-10 w-10' title='Add a Channel'>
<PlusIcon />
</IconButton>
<IconButton className='h-7 w-7' title='Settings'>
<CogIcon />
</IconButton>
</div>
</div>
)}
</Sidebar> </Sidebar>
<div <div

View File

@ -0,0 +1,38 @@
import { CogIcon, PlusIcon } from '@heroicons/react/solid'
import { useGuildMember } from 'contexts/GuildMember'
import { Divider } from 'components/design/Divider'
import { Channels } from 'components/Application/Channels'
import { IconButton } from 'components/design/IconButton'
import { GuildsChannelsPath } from '..'
export interface GuildLeftSidebarProps {
path: GuildsChannelsPath
}
export const GuildLeftSidebar: React.FC<GuildLeftSidebarProps> = (props) => {
const { path } = props
const { guild } = useGuildMember()
return (
<div className='flex flex-col justify-between w-full mt-2'>
<div className='text-center p-2 mx-8 mt-2'>
<h2 className='text-xl'>{guild.name}</h2>
</div>
<Divider />
<div className='scrollbar-firefox-support overflow-y-auto'>
<Channels path={path} />
</div>
<Divider />
<div className='flex justify-center items-center p-2 mb-1 space-x-6'>
<IconButton className='h-10 w-10' title='Add a Channel'>
<PlusIcon />
</IconButton>
<IconButton className='h-7 w-7' title='Settings'>
<CogIcon />
</IconButton>
</div>
</div>
)
}

View File

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

32
contexts/GuildMember.tsx Normal file
View File

@ -0,0 +1,32 @@
import { createContext, useContext } from 'react'
import { Guild } from 'models/Guild'
import { Member } from 'models/Member'
export interface GuildMember {
guild: Guild
member: Member
}
export interface GuildMemberProps {
guildMember: GuildMember
}
const defaultGuildMemberContext = {} as any
const GuildMemberContext = createContext<GuildMember>(defaultGuildMemberContext)
export const GuildMemberProvider: React.FC<GuildMemberProps> = (props) => {
return (
<GuildMemberContext.Provider value={props.guildMember}>
{props.children}
</GuildMemberContext.Provider>
)
}
export const useGuildMember = (): GuildMember => {
const guildMember = useContext(GuildMemberContext)
if (guildMember === defaultGuildMemberContext) {
throw new Error('useGuildMember must be used within GuildMemberProvider')
}
return guildMember
}

View File

@ -1,4 +1,4 @@
import { Type } from '@sinclair/typebox' import { Type, Static } from '@sinclair/typebox'
import { date, id } from './utils' import { date, id } from './utils'
@ -10,3 +10,5 @@ export const memberSchema = {
userId: id, userId: id,
guildId: id guildId: id
} }
const memberObjectSchema = Type.Object(memberSchema)
export type Member = Static<typeof memberObjectSchema>

View File

@ -8,33 +8,46 @@ import {
AuthenticationProvider, AuthenticationProvider,
PagePropsWithAuthentication PagePropsWithAuthentication
} from 'utils/authentication' } from 'utils/authentication'
import { GuildMember, GuildMemberProvider } from 'contexts/GuildMember'
import { GuildLeftSidebar } from 'components/Application/GuildLeftSidebar'
export interface ChannelPageProps extends PagePropsWithAuthentication { export interface ChannelPageProps extends PagePropsWithAuthentication {
channelId: number channelId: number
guildId: number guildId: number
guildMember: GuildMember
} }
const ChannelPage: NextPage<ChannelPageProps> = (props) => { const ChannelPage: NextPage<ChannelPageProps> = (props) => {
const { channelId, guildId, authentication } = props const { channelId, guildId, authentication, guildMember } = props
return ( return (
<AuthenticationProvider authentication={authentication}> <AuthenticationProvider authentication={authentication}>
<Head title='Thream | Application' /> <GuildMemberProvider guildMember={guildMember}>
<Application <Head title='Thream | Application' />
path={{ <Application
channelId, path={{
guildId channelId,
}} guildId
> }}
<Messages /> guildLeftSidebar={
</Application> <GuildLeftSidebar
path={{
channelId,
guildId
}}
/>
}
>
<Messages />
</Application>
</GuildMemberProvider>
</AuthenticationProvider> </AuthenticationProvider>
) )
} }
export const getServerSideProps = authenticationFromServerSide({ export const getServerSideProps = authenticationFromServerSide({
shouldBeAuthenticated: true, shouldBeAuthenticated: true,
fetchData: async (context) => { fetchData: async (context, api) => {
const channelId = Number(context?.params?.channelId) const channelId = Number(context?.params?.channelId)
const guildId = Number(context?.params?.guildId) const guildId = Number(context?.params?.guildId)
if (isNaN(channelId) || isNaN(guildId)) { if (isNaN(channelId) || isNaN(guildId)) {
@ -45,9 +58,11 @@ export const getServerSideProps = authenticationFromServerSide({
} }
} }
} }
const { data: guildMember } = await api.get(`/guilds/${guildId}`)
return { return {
channelId, channelId,
guildId guildId,
guildMember
} }
} }
}) })

View File

@ -1,5 +1,5 @@
import { AxiosInstance, AxiosResponse } from 'axios' import { AxiosInstance, AxiosResponse } from 'axios'
import { GetServerSideProps, GetServerSidePropsContext, Redirect } from 'next' import { GetServerSideProps, GetServerSidePropsContext } from 'next'
import { api } from '../api' import { api } from '../api'
import { Cookies } from '../cookies' import { Cookies } from '../cookies'
@ -25,7 +25,7 @@ interface AuthenticationFromServerSideOptions {
/** allows to fetch data server side with the authenticated user, the callback should returns the data that will be transfer to the component as props */ /** allows to fetch data server side with the authenticated user, the callback should returns the data that will be transfer to the component as props */
fetchData?: ( fetchData?: (
context: GetServerSidePropsContext, context: GetServerSidePropsContext,
api?: AxiosInstance api: AxiosInstance
) => Promise<{ [key: string]: any }> ) => Promise<{ [key: string]: any }>
} }
@ -56,9 +56,9 @@ export const authenticationFromServerSide = (
} else { } else {
let data = {} let data = {}
if (fetchData != null) { if (fetchData != null) {
data = await fetchData(context) data = await fetchData(context, api)
} }
return { props: { ...data } } return { props: data }
} }
} else { } else {
if (tokens == null) { if (tokens == null) {
@ -69,20 +69,26 @@ export const authenticationFromServerSide = (
} }
} }
} else { } else {
let data: Redirect | any = {} try {
const authentication = new Authentication(tokens) let data: any = {}
const { data: currentUser } = await authentication.api.get< const authentication = new Authentication(tokens)
unknown, const { data: currentUser } = await authentication.api.get<
AxiosResponse<UserCurrent> unknown,
>('/users/current') AxiosResponse<UserCurrent>
if (fetchData != null) { >('/users/current')
data = await fetchData(context, authentication.api) if (fetchData != null) {
} data = await fetchData(context, authentication.api)
if (data.redirect != null) { }
return data return {
} props: { authentication: { tokens, ...currentUser }, ...data }
return { }
props: { authentication: { tokens, ...currentUser }, ...data } } catch {
return {
redirect: {
destination: '/404',
permanent: false
}
}
} }
} }
} }