diff --git a/components/Application/Application.tsx b/components/Application/Application.tsx index 964294c..ee318a0 100644 --- a/components/Application/Application.tsx +++ b/components/Application/Application.tsx @@ -12,7 +12,6 @@ import { Guilds } from './Guilds/Guilds' import { Divider } from '../design/Divider' import { Members } from './Members' import { useAuthentication } from '../../tools/authentication' -import { API_URL } from '../../tools/api' export interface ChannelsPath { channelId: number @@ -189,7 +188,7 @@ export const Application: React.FC = (props) => { src={ user.logo == null ? '/images/data/user-default.png' - : API_URL + user.logo + : user.logo } alt={"Users's profil picture"} draggable={false} diff --git a/components/Application/GuildSettings/GuildSettings.tsx b/components/Application/GuildSettings/GuildSettings.tsx index 275034c..deb93aa 100644 --- a/components/Application/GuildSettings/GuildSettings.tsx +++ b/components/Application/GuildSettings/GuildSettings.tsx @@ -9,7 +9,6 @@ import useTranslation from 'next-translate/useTranslation' import { HandleSubmitCallback, useForm } from '../../../hooks/useForm' import { guildSchema } from '../../../models/Guild' import { FormState } from '../../design/FormState' -import { API_URL } from '../../../tools/api' import { useGuildMember } from '../../../contexts/GuildMember' import { Textarea } from '../../design/Textarea' import { Input } from '../../design/Input' @@ -135,7 +134,7 @@ export const GuildSettings: React.FC = () => { src={ guild.icon == null ? '/images/data/guild-default.png' - : API_URL + guild.icon + : guild.icon } alt='Profil Picture' draggable='false' diff --git a/components/Application/Guilds/Guild/Guild.tsx b/components/Application/Guilds/Guild/Guild.tsx index 23d88d7..45b81bb 100644 --- a/components/Application/Guilds/Guild/Guild.tsx +++ b/components/Application/Guilds/Guild/Guild.tsx @@ -1,6 +1,5 @@ import Image from 'next/image' -import { API_URL } from '../../../../tools/api' import { GuildWithDefaultChannelId } from '../../../../models/Guild' import { IconLink } from '../../../design/IconLink' @@ -24,9 +23,7 @@ export const Guild: React.FC = (props) => { quality={100} className='rounded-full' src={ - guild.icon != null - ? API_URL + guild.icon - : '/images/data/guild-default.png' + guild.icon != null ? guild.icon : '/images/data/guild-default.png' } alt='logo' width={48} diff --git a/components/Application/JoinGuildsPublic/GuildPublic/GuildPublic.tsx b/components/Application/JoinGuildsPublic/GuildPublic/GuildPublic.tsx index b4576f1..69f8a49 100644 --- a/components/Application/JoinGuildsPublic/GuildPublic/GuildPublic.tsx +++ b/components/Application/JoinGuildsPublic/GuildPublic/GuildPublic.tsx @@ -7,7 +7,6 @@ import axios from 'axios' import { Emoji } from '../../../Emoji' import { ConfirmGuildJoin } from '../../ConfirmGuildJoin' -import { API_URL } from '../../../../tools/api' import { GuildPublic as GuildPublicType, GuildWithDefaultChannelId @@ -64,9 +63,7 @@ export const GuildPublic: React.FC = (props) => { quality={100} className='rounded-full' src={ - guild.icon != null - ? API_URL + guild.icon - : '/images/data/guild-default.png' + guild.icon != null ? guild.icon : '/images/data/guild-default.png' } alt='logo' width={80} diff --git a/components/Application/Members/Member/Member.tsx b/components/Application/Members/Member/Member.tsx index e3faec5..701053b 100644 --- a/components/Application/Members/Member/Member.tsx +++ b/components/Application/Members/Member/Member.tsx @@ -2,7 +2,6 @@ import Image from 'next/image' import Link from 'next/link' import { MemberWithPublicUser } from '../../../../models/Member' -import { API_URL } from '../../../../tools/api' export interface MemberProps { member: MemberWithPublicUser @@ -20,7 +19,7 @@ export const Member: React.FC = (props) => { src={ member.user.logo == null ? '/images/data/user-default.png' - : API_URL + member.user.logo + : member.user.logo } alt={"Users's profil picture"} height={50} diff --git a/components/Application/Messages/Message/Message.tsx b/components/Application/Messages/Message/Message.tsx index 244ad68..042f46f 100644 --- a/components/Application/Messages/Message/Message.tsx +++ b/components/Application/Messages/Message/Message.tsx @@ -3,7 +3,6 @@ import Link from 'next/link' import date from 'date-and-time' import { MessageWithMember } from '../../../../models/Message' -import { API_URL } from '../../../../tools/api' import { MessageText } from './MessageText' import { Loader } from '../../../design/Loader' import { MessageFile } from './MessageFile' @@ -30,7 +29,7 @@ export const Message: React.FC = (props) => { src={ message.member.user.logo == null ? '/images/data/user-default.png' - : API_URL + message.member.user.logo + : message.member.user.logo } alt={"Users's profil picture"} width={50} diff --git a/components/Application/Messages/Message/MessageFile/MessageFile.tsx b/components/Application/Messages/Message/MessageFile/MessageFile.tsx index 953c8dc..3925a0e 100644 --- a/components/Application/Messages/Message/MessageFile/MessageFile.tsx +++ b/components/Application/Messages/Message/MessageFile/MessageFile.tsx @@ -3,10 +3,10 @@ import axios from 'axios' import prettyBytes from 'pretty-bytes' import { DownloadIcon } from '@heroicons/react/solid' -import { useAuthentication } from '../../../../../tools/authentication' import { MessageWithMember } from '../../../../../models/Message' import { Loader } from '../../../../design/Loader' import { FileIcon } from './FileIcon' +import { api } from '../../../../../tools/api' const supportedImageMimetype = [ 'image/png', @@ -27,14 +27,13 @@ export interface MessageContentProps { export const MessageFile: React.FC = (props) => { const { message } = props - const { authentication } = useAuthentication() const [file, setFile] = useState(null) useEffect(() => { const ourRequest = axios.CancelToken.source() const fetchData = async (): Promise => { - const { data } = await authentication.api.get(message.value, { + const { data } = await api.get(message.value, { responseType: 'blob', cancelToken: ourRequest.token }) @@ -46,7 +45,7 @@ export const MessageFile: React.FC = (props) => { return () => { ourRequest.cancel() } - }, [message.value, authentication.api]) + }, [message.value]) if (file == null) { return diff --git a/components/Application/UserProfile/UserProfile.tsx b/components/Application/UserProfile/UserProfile.tsx index 2a09b8b..4d626a5 100644 --- a/components/Application/UserProfile/UserProfile.tsx +++ b/components/Application/UserProfile/UserProfile.tsx @@ -2,7 +2,6 @@ import Image from 'next/image' import date from 'date-and-time' import useTranslation from 'next-translate/useTranslation' -import { API_URL } from '../../../tools/api' import { UserPublic } from '../../../models/User' import { Guild } from '../../../models/Guild' @@ -28,7 +27,7 @@ export const UserProfile: React.FC = (props) => { className='rounded-full' src={ user.logo != null - ? API_URL + user.logo + ? user.logo : '/images/data/user-default.png' } alt='Profil Picture' @@ -91,9 +90,7 @@ export const UserProfile: React.FC = (props) => {
- {user.biography != null && ( -

{user.biography}

- )} + {user.biography != null &&

{user.biography}

}
diff --git a/components/Application/UserSettings/UserSettings.tsx b/components/Application/UserSettings/UserSettings.tsx index 9e5fd24..f56c544 100644 --- a/components/Application/UserSettings/UserSettings.tsx +++ b/components/Application/UserSettings/UserSettings.tsx @@ -1,13 +1,12 @@ import Image from 'next/image' import useTranslation from 'next-translate/useTranslation' -import { useState } from 'react' +import { useState, useMemo } from 'react' import { Form } from 'react-component-form' import { EyeIcon, PhotographIcon } from '@heroicons/react/solid' import { Type } from '@sinclair/typebox' import axios from 'axios' import Link from 'next/link' -import { API_URL } from '../../../tools/api' import { Input } from '../../design/Input' import { Checkbox } from '../../design/Checkbox' import { Textarea } from '../../design/Textarea' @@ -20,6 +19,7 @@ import { FormState } from '../../design/FormState' import { useForm, HandleSubmitCallback } from '../../../hooks/useForm' import { userSchema } from '../../../models/User' import { userSettingsSchema } from '../../../models/UserSettings' +import { ProviderOAuth, providers } from '../../../models/OAuth' export const UserSettings: React.FC = () => { const { user, setUser, authentication } = useAuthentication() @@ -56,6 +56,10 @@ export const UserSettings: React.FC = () => { resetOnSuccess: false }) + const hasAllProviders = useMemo(() => { + return providers.every((provider) => user.strategies.includes(provider)) + }, [user.strategies]) + const onSubmit: HandleSubmitCallback = async (formData) => { try { const { isPublicGuilds, isPublicEmail, ...userData } = formData @@ -173,6 +177,41 @@ export const UserSettings: React.FC = () => { } } + const handleDeletionProvider = ( + provider: ProviderOAuth + ): (() => Promise) => { + return async () => { + try { + setFetchState('loading') + await authentication.api.delete(`/users/oauth2/${provider}`) + setUser((oldUser) => { + return { + ...oldUser, + strategies: oldUser.strategies.filter( + (strategy) => strategy !== provider + ) + } + }) + setMessageTranslationKey('application:success-deleted-provider') + } catch (error) { + setFetchState('error') + setMessageTranslationKey('errors:server-error') + } + } + } + + const handleAddProvider = ( + provider: ProviderOAuth + ): (() => Promise) => { + return async () => { + const redirect = window.location.href.replace(location.search, '') + const { data: url } = await authentication.api.get( + `/users/oauth2/${provider.toLowerCase()}/add-strategy?redirectURI=${redirect}` + ) + window.location.href = url + } + } + return (
{ className='rounded-full opacity-50' src={ user.logo != null - ? API_URL + user.logo + ? user.logo : '/images/data/user-default.png' } alt='Profil Picture' @@ -264,7 +303,7 @@ export const UserSettings: React.FC = () => { />
-
+
@@ -283,18 +322,46 @@ export const UserSettings: React.FC = () => {
- - - + {!hasAllProviders ? ( +
+

+ {t('application:signin-with-an-account')} +

+ {providers.map((provider, index) => { + if (!user.strategies.includes(provider)) { + return ( + + ) + } + return null + })} +
+ ) : null} + {user.strategies.length !== 1 && ( +
+

+ {t('application:signout-with-an-account')} +

+ {providers.map((provider, index) => { + if (user.strategies.includes(provider)) { + return ( + + ) + } + return null + })} +
+ )}
diff --git a/components/Authentication/AuthenticationSocialMedia.tsx b/components/Authentication/AuthenticationSocialMedia.tsx index 70fd785..5c39e26 100644 --- a/components/Authentication/AuthenticationSocialMedia.tsx +++ b/components/Authentication/AuthenticationSocialMedia.tsx @@ -4,6 +4,7 @@ import { useRouter } from 'next/router' import { api } from '../../tools/api' import { Authentication, isTokens } from '../../tools/authentication' import { SocialMediaButton, SocialMedia } from '../design/SocialMediaButton' +import { providers } from '../../models/OAuth' export const AuthenticationSocialMedia: React.FC = () => { const router = useRouter() @@ -12,7 +13,7 @@ export const AuthenticationSocialMedia: React.FC = () => { socialMedia: SocialMedia ): (() => Promise) => { return async () => { - const redirect = window.location.href + const redirect = window.location.href.replace(location.search, '') const { data: url } = await api.get( `/users/oauth2/${socialMedia.toLowerCase()}/signin?redirectURI=${redirect}` ) @@ -32,18 +33,15 @@ export const AuthenticationSocialMedia: React.FC = () => { return (
- - - + {providers.map((provider, index) => { + return ( + + ) + })}
) diff --git a/locales/en/application.json b/locales/en/application.json index 8286017..49380a1 100644 --- a/locales/en/application.json +++ b/locales/en/application.json @@ -19,6 +19,9 @@ "success-email-changed": "Please check your emails to confirm your new email address. You are now signed out.", "delete": "Delete", "signout": "Sign out", + "signin-with-an-account": "Sign in with an account", + "signout-with-an-account": "Sign out with an account", + "success-deleted-provider": "The provider has been deleted.", "leave": "Leave", "start-chatting-kill-ghost": "Start chatting to kill this ghost!" } diff --git a/locales/fr/application.json b/locales/fr/application.json index 46d80e4..11e1832 100644 --- a/locales/fr/application.json +++ b/locales/fr/application.json @@ -19,6 +19,9 @@ "success-email-changed": "Veuillez vérifier vos emails pour confirmer votre nouvelle adresse email. Vous êtes maintenant déconnecté.", "delete": "Supprimer", "signout": "Se déconnecter", + "signin-with-an-account": "Se connecter avec un compte", + "signout-with-an-account": "Se déconnecter avec un compte", + "success-deleted-provider": "Le moyen de connexion a été supprimé.", "leave": "Quitter", "start-chatting-kill-ghost": "Commencez à discuter pour tuer ce fantôme !" } diff --git a/next.config.js b/next.config.js index 7f82ba5..cfb2f19 100644 --- a/next.config.js +++ b/next.config.js @@ -9,6 +9,7 @@ module.exports = nextTranslate( domains: [ 'api.thream.divlo.fr', 'thream-api.herokuapp.com', + 'file-uploads-api.thream.divlo.fr', ...(process.env.NODE_ENV !== 'production' ? ['localhost'] : []) ] }, diff --git a/package-lock.json b/package-lock.json index abca7d3..b6d5850 100644 --- a/package-lock.json +++ b/package-lock.json @@ -91,8 +91,8 @@ "vercel": "24.0.1" }, "engines": { - "node": ">=16.0.0", - "npm": ">=8.0.0" + "node": ">=14.0.0", + "npm": ">=7.0.0" } }, "node_modules/@ampproject/remapping": { diff --git a/package.json b/package.json index de53e25..84f1795 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,8 @@ "url": "https://github.com/Thream/website" }, "engines": { - "node": ">=16.0.0", - "npm": ">=8.0.0" + "node": ">=14.0.0", + "npm": ">=7.0.0" }, "scripts": { "dev": "next dev",