8 Commits

16 changed files with 132 additions and 96 deletions

View File

@ -18,7 +18,7 @@
Thream's website to stay close with your friends and communities. Thream's website to stay close with your friends and communities.
It uses [Thream/api](https://github.com/Thream/api) [v1.0.0](https://github.com/Thream/api/releases/tag/v1.0.0). It uses [Thream/api](https://github.com/Thream/api) [v1.0.1](https://github.com/Thream/api/releases/tag/v1.0.1).
## ⚙️ Getting Started ## ⚙️ Getting Started

View File

@ -121,13 +121,15 @@ export const Application: React.FC<ApplicationProps> = (props) => {
}) })
}, },
onSwipedLeft: () => { onSwipedLeft: () => {
if (visibleSidebars.left) { if (isGuildsChannelsPath(path)) {
return setVisibleSidebars({ ...visibleSidebars, left: false }) if (visibleSidebars.left) {
return setVisibleSidebars({ ...visibleSidebars, left: false })
}
setVisibleSidebars({
...visibleSidebars,
right: true
})
} }
setVisibleSidebars({
...visibleSidebars,
right: true
})
} }
}) })

View File

@ -2,6 +2,7 @@ import { useRouter } from 'next/router'
import { useState } from 'react' import { useState } from 'react'
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 axios from 'axios'
import { HandleSubmitCallback, useForm } from '../../../hooks/useForm' import { HandleSubmitCallback, useForm } from '../../../hooks/useForm'
import { FormState } from '../../design/FormState' import { FormState } from '../../design/FormState'
@ -81,7 +82,11 @@ export const ChannelSettings: React.FC<ChannelSettingsProps> = (props) => {
await router.push(`/application/${guild.id}/${data.defaultChannelId}`) await router.push(`/application/${guild.id}/${data.defaultChannelId}`)
} catch (error) { } catch (error) {
setFetchState('error') setFetchState('error')
setMessageTranslationKey('errors:server-error') if (axios.isAxiosError(error) && error.response?.status === 400) {
setMessageTranslationKey('application:delete-channel-only-one')
} else {
setMessageTranslationKey('errors:server-error')
}
} }
} }

View File

@ -16,84 +16,94 @@ export const UserProfile: React.FC<UserProfileProps> = (props) => {
const { t } = useTranslation() const { t } = useTranslation()
return ( return (
<div className='relative flex h-full flex-col items-center justify-center'> <>
<div className='transition'> <div className='relative flex h-full flex-col items-center justify-center'>
<div className='max-w-[1000px] px-12'> <div className='transition'>
<div className='flex items-center justify-between'> <div className='max-w-[1000px] px-12'>
<div className='flex w-max items-center'> <div className='flex items-center justify-between'>
<div className='relative flex items-center justify-center overflow-hidden rounded-full shadow-lg transition-all'> <div className='flex w-max flex-col items-center gap-7 md:flex-row'>
<Image <div className='relative flex items-center justify-center overflow-hidden rounded-full shadow-lg transition-all'>
quality={100} <Image
className='rounded-full' quality={100}
src={ className='rounded-full'
user.logo != null src={
? user.logo user.logo != null
: '/images/data/user-default.png' ? user.logo
} : '/images/data/user-default.png'
alt='Profil Picture' }
draggable='false' alt='Profil Picture'
height={125} draggable='false'
width={125} height={125}
/> width={125}
</div> />
<div className='ml-10 flex flex-col'>
<div className='mb-2 flex items-center'>
<p
className='space text-dark text-3xl font-bold tracking-wide dark:text-white'
data-cy='user-name'
>
{user.name}
</p>
<p
className='ml-8 select-none text-sm tracking-widest text-white opacity-40'
data-cy='user-createdAt'
>
{date.format(new Date(user.createdAt), 'DD/MM/YYYY')}
</p>
</div> </div>
<div className='my-2 text-left'> <div className='ml-10 flex flex-col'>
{user.email != null && ( <div className='mb-2 flex items-center'>
<p className='font-bold'> <p
Email:{' '} className='space text-dark text-3xl font-bold tracking-wide dark:text-white'
<a data-cy='user-name'
href={`mailto:${user.email}`} >
target='_blank' {user.name}
className='relative ml-2 font-normal tracking-wide no-underline opacity-80 transition-all after:absolute after:left-0 after:bottom-[-1px] after:h-[1px] after:w-0 after:bg-black after:transition-all hover:opacity-100 hover:after:w-full dark:after:bg-white'
rel='noreferrer'
data-cy='user-email'
>
{user.email}
</a>
</p> </p>
)} <p
{user.website != null && ( className='ml-8 select-none text-sm tracking-widest text-white opacity-40'
<p className='font-bold'> data-cy='user-createdAt'
{t('application:website')}:{' '} >
<a {date.format(new Date(user.createdAt), 'DD/MM/YYYY')}
href={user.website}
className='relative ml-2 font-normal tracking-wide no-underline opacity-80 transition-all after:absolute after:left-0 after:bottom-[-2px] after:h-[1px] after:w-0 after:bg-black after:transition-all hover:opacity-100 hover:after:w-full dark:after:bg-white'
>
{user.website}
</a>
</p> </p>
)} </div>
{user.status != null && ( <div className='my-2 text-left'>
<p className='flex font-bold'> {user.email != null && (
{t('application:status')}:{' '} <p className='font-bold'>
<span className='ml-2 font-normal tracking-wide'> Email:{' '}
{user.status} <a
</span> href={`mailto:${user.email}`}
</p> target='_blank'
)} className='relative ml-2 font-normal tracking-wide no-underline opacity-80 transition-all after:absolute after:left-0 after:bottom-[-1px] after:h-[1px] after:w-0 after:bg-black after:transition-all hover:opacity-100 hover:after:w-full dark:after:bg-white'
rel='noreferrer'
data-cy='user-email'
>
{user.email}
</a>
</p>
)}
{user.website != null && (
<p className='font-bold'>
{t('application:website')}:{' '}
<a
href={user.website}
className='relative ml-2 font-normal tracking-wide no-underline opacity-80 transition-all after:absolute after:left-0 after:bottom-[-2px] after:h-[1px] after:w-0 after:bg-black after:transition-all hover:opacity-100 hover:after:w-full dark:after:bg-white'
>
{user.website}
</a>
</p>
)}
{user.status != null && (
<p className='flex font-bold'>
{t('application:status')}:{' '}
<span className='ml-2 font-normal tracking-wide'>
{user.status}
</span>
</p>
)}
</div>
</div> </div>
</div> </div>
</div> </div>
</div> {user.biography != null && (
<div className='mt-7'> <div className='mt-7 text-center'>
{user.biography != null && <p>{user.biography}</p>} <p>{user.biography}</p>
</div>
)}
</div> </div>
</div> </div>
</div> </div>
</div>
<style jsx global>{`
#application-page-content {
overflow-x: hidden;
}
`}</style>
</>
) )
} }

View File

@ -84,7 +84,10 @@ export const UserSettings: React.FC = () => {
return { return {
...oldUser, ...oldUser,
...userCurrentData, ...userCurrentData,
settings: userCurrentSettings.settings settings: {
...oldUser.settings,
...userCurrentSettings.settings
}
} }
}) })
return { return {

View File

@ -2,8 +2,12 @@
"website": "Website", "website": "Website",
"create": "Create", "create": "Create",
"status": "Status", "status": "Status",
"create-a-channel": "Create a channel",
"delete-channel-only-one": "The guild should have at least one channel. You can't delete the only channel of the guild.",
"create-a-guild": "Create a Guild", "create-a-guild": "Create a Guild",
"create-a-guild-description": "Create your own guild and manage everything.", "create-a-guild-description": "Create your own guild and manage everything.",
"user-settings": "User settings",
"guild-settings": "Guild settings",
"join-a-guild": "Join a Guild", "join-a-guild": "Join a Guild",
"join-the-guild": "Join the guild", "join-the-guild": "Join the guild",
"join-a-guild-description": "Talk, collaborate, share and have fun with your friends by joining an already existing guild!", "join-a-guild-description": "Talk, collaborate, share and have fun with your friends by joining an already existing guild!",

View File

@ -2,8 +2,12 @@
"website": "Site web", "website": "Site web",
"create": "Créer", "create": "Créer",
"status": "Statut", "status": "Statut",
"create-a-channel": "Créer un channel",
"delete-channel-only-one": "La guilde doit avoir au moins un channel. Vous ne pouvez pas supprimer le seul channel de la guilde.",
"create-a-guild": "Créer une Guilde", "create-a-guild": "Créer une Guilde",
"create-a-guild-description": "Créez votre propre guilde et gérez tout.", "create-a-guild-description": "Créez votre propre guilde et gérez tout.",
"user-settings": "Paramètres utilisateur",
"guild-settings": "Paramètres de la guilde",
"join-a-guild": "Rejoindre une Guilde", "join-a-guild": "Rejoindre une Guilde",
"join-the-guild": "Rejoindre la guilde", "join-the-guild": "Rejoindre la guilde",
"join-a-guild-description": "Discutez, collaborez, partagez et amusez-vous avec vos amis en rejoignant une guilde déjà existante!", "join-a-guild-description": "Discutez, collaborez, partagez et amusez-vous avec vos amis en rejoignant une guilde déjà existante!",

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "@thream/website", "name": "@thream/website",
"version": "1.0.0", "version": "1.0.2",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@thream/website", "name": "@thream/website",
"version": "1.0.0", "version": "1.0.2",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@fontsource/montserrat": "4.5.7", "@fontsource/montserrat": "4.5.7",

View File

@ -1,6 +1,6 @@
{ {
"name": "@thream/website", "name": "@thream/website",
"version": "1.0.0", "version": "1.0.2",
"private": true, "private": true,
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -43,7 +43,7 @@ const ChannelPage: NextPage<ChannelPageProps> = (props) => {
<MembersProviders path={path}> <MembersProviders path={path}>
<ChannelsProvider path={path}> <ChannelsProvider path={path}>
<MessagesProvider path={path}> <MessagesProvider path={path}>
<Head title='Thream | Application' /> <Head title={`Thream | ${selectedChannel.name}`} />
<Application <Application
path={path} path={path}
guildLeftSidebar={<GuildLeftSidebar path={path} />} guildLeftSidebar={<GuildLeftSidebar path={path} />}

View File

@ -40,7 +40,7 @@ const ChannelSettingsPage: NextPage<ChannelSettingsPageProps> = (props) => {
<GuildMemberProvider guildMember={guildMember} path={path}> <GuildMemberProvider guildMember={guildMember} path={path}>
<MembersProviders path={path}> <MembersProviders path={path}>
<ChannelsProvider path={path}> <ChannelsProvider path={path}>
<Head title='Thream | Application' /> <Head title={`Thream | ${selectedChannel.name}`} />
<Application <Application
path={path} path={path}
guildLeftSidebar={<GuildLeftSidebar path={path} />} guildLeftSidebar={<GuildLeftSidebar path={path} />}

View File

@ -1,4 +1,5 @@
import { NextPage } from 'next' import { NextPage } from 'next'
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'
@ -21,6 +22,7 @@ export interface CreateChannelPageProps extends PagePropsWithAuthentication {
const CreateChannelPage: NextPage<CreateChannelPageProps> = (props) => { const CreateChannelPage: NextPage<CreateChannelPageProps> = (props) => {
const { guildId, authentication, guildMember } = props const { guildId, authentication, guildMember } = props
const { t } = useTranslation()
const path = { guildId } const path = { guildId }
@ -29,10 +31,10 @@ const CreateChannelPage: NextPage<CreateChannelPageProps> = (props) => {
<GuildsProvider> <GuildsProvider>
<GuildMemberProvider guildMember={guildMember} path={path}> <GuildMemberProvider guildMember={guildMember} path={path}>
<Head <Head
title={`Thream | Crée un channel`} title={`Thream | ${t('application:create-a-channel')}`}
description={'Crée un nouveau channel'} description={t('application:create-a-channel')}
/> />
<Application path={path} title={'Crée un channel'}> <Application path={path} title={t('application:create-a-channel')}>
<CreateChannel /> <CreateChannel />
</Application> </Application>
</GuildMemberProvider> </GuildMemberProvider>

View File

@ -1,4 +1,5 @@
import { NextPage } from 'next' import { NextPage } from 'next'
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'
@ -18,6 +19,7 @@ export interface GuildSettingsPageProps extends PagePropsWithAuthentication {
const GuildSettingsPage: NextPage<GuildSettingsPageProps> = (props) => { const GuildSettingsPage: NextPage<GuildSettingsPageProps> = (props) => {
const { guildId, authentication, guildMember } = props const { guildId, authentication, guildMember } = props
const { t } = useTranslation()
const path = { guildId } const path = { guildId }
@ -25,8 +27,8 @@ const GuildSettingsPage: NextPage<GuildSettingsPageProps> = (props) => {
<AuthenticationProvider authentication={authentication}> <AuthenticationProvider authentication={authentication}>
<GuildsProvider> <GuildsProvider>
<GuildMemberProvider guildMember={guildMember} path={path}> <GuildMemberProvider guildMember={guildMember} path={path}>
<Head title='Thream | Guild settings' /> <Head title={`Thream | ${t('application:guild-settings')}`} />
<Application path={path} title='Guild settings'> <Application path={path} title={t('application:guild-settings')}>
<GuildSettings /> <GuildSettings />
</Application> </Application>
</GuildMemberProvider> </GuildMemberProvider>

View File

@ -1,4 +1,5 @@
import { NextPage } from 'next' import { NextPage } from 'next'
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'
@ -11,11 +12,16 @@ 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) => {
const { t } = useTranslation()
return ( return (
<AuthenticationProvider authentication={props.authentication}> <AuthenticationProvider authentication={props.authentication}>
<GuildsProvider> <GuildsProvider>
<Head title='Thream | Settings' /> <Head title={`Thream | ${t('application:user-settings')}`} />
<Application path={`/application/users/settings`} title='Settings'> <Application
path='/application/users/settings'
title={t('application:user-settings')}
>
<UserSettings /> <UserSettings />
</Application> </Application>
</GuildsProvider> </GuildsProvider>

View File

@ -1,6 +1,6 @@
import axios from 'axios' import axios from 'axios'
export const API_VERSION = '1.0.0' export const API_VERSION = '1.0.1'
export const API_DEFAULT_PORT = 8080 export const API_DEFAULT_PORT = 8080

View File

@ -5,7 +5,6 @@ import useTranslation from 'next-translate/useTranslation'
import { Authentication, PagePropsWithAuthentication } from '.' import { Authentication, PagePropsWithAuthentication } from '.'
import { UserCurrent } from '../../models/User' import { UserCurrent } from '../../models/User'
import { Language, Theme } from '../../models/UserSettings'
export interface AuthenticationValue { export interface AuthenticationValue {
authentication: Authentication authentication: Authentication
@ -41,14 +40,13 @@ export const AuthenticationProvider: React.FC<PagePropsWithAuthentication> = (
useEffect(() => { useEffect(() => {
authentication.api authentication.api
.put('/users/current/settings', { theme, language: lang }) .put('/users/current/settings', { theme, language: lang })
.then(() => { .then(({ data: userCurrentSettings }) => {
setUser((oldUser) => { setUser((oldUser) => {
return { return {
...oldUser, ...oldUser,
settings: { settings: {
...oldUser.settings, ...oldUser.settings,
theme: theme as Theme, ...userCurrentSettings.settings
language: lang as Language
} }
} }
}) })