Skip to content

Commit

Permalink
Override firebase authentication service for local environment (#14)
Browse files Browse the repository at this point in the history
* Override firebase authentication service for local environment

* Override firebase storage service for local environment
  • Loading branch information
abarghoud authored Nov 12, 2024
1 parent a3aab10 commit a8d2f96
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 40 deletions.
3 changes: 3 additions & 0 deletions apps/client/src/app/api/contact-form-api.requirements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface IContactFormApi {
saveContact(email: string, name: string, message: string): Promise<void>;
}
20 changes: 20 additions & 0 deletions apps/client/src/app/api/firebase-contact-form-api-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { IContactFormApi } from './contact-form-api.requirements';
import { addDoc, collection } from 'firebase/firestore';
import { initDb } from '../config/firebase';

export class FirebaseContactFormApiService implements IContactFormApi {
private readonly db;

public constructor() {
this.db = initDb();
}


public async saveContact(email: string, name: string, message: string): Promise<void> {
await addDoc(collection(this.db, 'messages'), {
email,
name,
message,
});
}
}
7 changes: 7 additions & 0 deletions apps/client/src/app/api/local-contact-form-api-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { IContactFormApi } from './contact-form-api.requirements';

export class LocalContactFormApiService implements IContactFormApi {
public async saveContact(email: string, name: string, message: string): Promise<void> {
console.log(email, name, message);
}
}
47 changes: 29 additions & 18 deletions apps/client/src/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import { PPSCertificateApiContext } from './contexts/pps-certificate-api-context
import { PPSCertificateApiService } from './api/pps-certificate-api-service';
import { IRecaptchaChecker, IRecaptchaGenerator } from '@pps-easy/recaptcha/contracts';
import { LocalRecaptchaGenerator } from './service/local-recaptcha-generator';
import { LocalAuthenticationService } from './service/local-authentication-service';
import { ContactFormApiServiceContext } from './contexts/contact-form-api-context';
import { LocalContactFormApiService } from './api/local-contact-form-api-service';
import { FirebaseContactFormApiService } from './api/firebase-contact-form-api-service';

export function App() {
const { theme } = useTheme();
Expand All @@ -31,31 +35,38 @@ export function App() {
const clientRecaptchaChecker: IRecaptchaChecker = isLocalEnvironment ?
new LocalRecaptchaChecker() :
new ClientRecaptchaChecker(axiosInstance);
const firebaseAuthenticationService = new FirebaseAuthenticationService(clientRecaptchaChecker, googleRecaptchaGenerator);
const authenticationService = isLocalEnvironment ?
new LocalAuthenticationService() :
new FirebaseAuthenticationService(clientRecaptchaChecker, googleRecaptchaGenerator);
const contactFormApiService = isLocalEnvironment ?
new LocalContactFormApiService() :
new FirebaseContactFormApiService();
const ppsCertificateApiService = new PPSCertificateApiService(axiosInstance, googleRecaptchaGenerator)

return (
<AuthenticationContext.Provider value={firebaseAuthenticationService}>
<AuthenticationContext.Provider value={authenticationService}>
<PPSCertificateApiContext.Provider value={ppsCertificateApiService}>
<div className={`
min-h-screen flex flex-col justify-center ${theme === "dark" ? "bg-background dark:bg-background" : "bg-white"} ${loading ? "items-center" : ""}
`}>
<Routes>
<Route path="/login" element={<AuthLayout />}>
<Route path="" element={<LoginFormPage />} />
</Route>
<ContactFormApiServiceContext.Provider value={contactFormApiService}>
<div className={`
min-h-screen flex flex-col justify-center ${theme === "dark" ? "bg-background dark:bg-background" : "bg-white"} ${loading ? "items-center" : ""}
`}>
<Routes>
<Route path="/login" element={<AuthLayout />}>
<Route path="" element={<LoginFormPage />} />
</Route>

<Route path="/generate-certificate" element={<GuestPage />} />
<Route path="/generate-certificate" element={<GuestPage />} />

<Route element={<PrivateRoute />}>
<Route element={<MainLayout />}>
{routesConfig.map((route) => (
<Route key={route.path} path={route.path} element={route.element} />
))}
<Route element={<PrivateRoute />}>
<Route element={<MainLayout />}>
{routesConfig.map((route) => (
<Route key={route.path} path={route.path} element={route.element} />
))}
</Route>
</Route>
</Route>
</Routes>
</div>
</Routes>
</div>
</ContactFormApiServiceContext.Provider>
</PPSCertificateApiContext.Provider>
</AuthenticationContext.Provider>
);
Expand Down
2 changes: 0 additions & 2 deletions apps/client/src/app/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import React, { FC, useCallback, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { FirebaseAuthenticationService } from '../service/firebase-authentication-service';
import { auth } from "../config/firebase";
import { LogOut, FileUser, Menu } from "lucide-react";
import { Button } from "@pps-easy/ui/button";
import { Sheet, SheetContent, SheetTrigger } from "@pps-easy/ui/sheet";
Expand Down
33 changes: 20 additions & 13 deletions apps/client/src/app/components/form/ContactForm.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { FC, useCallback } from 'react';
import { FC, useCallback, useContext } from 'react';
import { useForm, Controller } from 'react-hook-form';

import { Button } from '@pps-easy/ui/button';
import { db } from '../../config/firebase';
import { collection, addDoc } from 'firebase/firestore';

import { IContactFormApi } from '../../api/contact-form-api.requirements';
import { ContactFormApiServiceContext } from '../../contexts/contact-form-api-context';

interface ContactFormValues {
name: string;
Expand All @@ -12,17 +14,22 @@ interface ContactFormValues {

export const ContactForm: FC = () => {
const { control, handleSubmit, formState: { isSubmitting }, reset } = useForm<ContactFormValues>();
const contactFormApiService = useContext<IContactFormApi>(ContactFormApiServiceContext);

const onSubmit = useCallback(async (data: ContactFormValues) => {
console.log('Form Data:', data);
try {
await addDoc(collection(db, 'messages'), data);
alert('Votre message a été envoyé avec succès ! Nous vous répondrons dans les plus brefs délais.');
reset();
} catch (error) {
console.error('Error sending message:', error);
}
}, [reset]);
const onSubmit = useCallback(
async ({ email, name, message }: ContactFormValues) => {
try {
await contactFormApiService.saveContact(email, name, message);
alert(
'Votre message a été envoyé avec succès ! Nous vous répondrons dans les plus brefs délais.'
);
reset();
} catch (error) {
console.error('Error sending message:', error);
}
},
[contactFormApiService, reset]
);

return (
<div className="w-full max-w-2xl mx-auto min-h-[595px] flex flex-col gap-2 p-6 bg-card rounded-lg shadow-md border border-border overflow-auto">
Expand Down
16 changes: 12 additions & 4 deletions apps/client/src/app/config/firebase.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { initializeApp } from "firebase/app";
import { FirebaseApp, initializeApp } from 'firebase/app';
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";

Expand All @@ -11,7 +11,15 @@ const firebaseConfig = {
appId: process.env.REACT_APP_FIREBASE_APP_ID,
};

const app = initializeApp(firebaseConfig);
let app: FirebaseApp;

export const auth = getAuth(app);
export const db = getFirestore(app);
const getApp = () => {
if (!app) {
app = initializeApp(firebaseConfig);
}

return app;
}

export const initAuth = () => getAuth(getApp());
export const initDb = () => getFirestore(getApp());
1 change: 0 additions & 1 deletion apps/client/src/app/contexts/authentication-context.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';

import { IAuthenticationService } from '../service/authentication.interface';
import { FirebaseAuthenticationService } from '../service/firebase-authentication-service';
import { IUser } from '../interfaces/user.interface';

class NullAuthenticationService implements IAuthenticationService {
Expand Down
6 changes: 6 additions & 0 deletions apps/client/src/app/contexts/contact-form-api-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from 'react';

import { IContactFormApi } from '../api/contact-form-api.requirements';
import { LocalContactFormApiService } from '../api/local-contact-form-api-service';

export const ContactFormApiServiceContext = React.createContext<IContactFormApi>(new LocalContactFormApiService());
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { IRecaptchaChecker, IRecaptchaGenerator } from '@pps-easy/recaptcha/cont
import { ChallengeResult } from '@pps-easy/recaptcha/domain';

import { IAuthenticationService } from './authentication.interface';
import { auth } from '../config/firebase';
import { initAuth } from '../config/firebase';
import { IUser } from '../interfaces/user.interface';

export class FirebaseAuthenticationService implements IAuthenticationService {
Expand All @@ -25,7 +25,7 @@ export class FirebaseAuthenticationService implements IAuthenticationService {
private readonly recaptchaChecker: IRecaptchaChecker,
private readonly recaptchaGenerator: IRecaptchaGenerator
) {
this.auth = auth;
this.auth = initAuth();
}

public onAuthStateChanged(callback: (user: User | null) => void): () => void {
Expand Down
60 changes: 60 additions & 0 deletions apps/client/src/app/service/local-authentication-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { IAuthenticationService } from './authentication.interface';
import { IUser } from '../interfaces/user.interface';

export class LocalAuthenticationService implements IAuthenticationService {
private authenticatedUser: IUser | null = null;
private authStateChangedCallback: ((user: IUser | null) => void) | undefined;

public onAuthStateChanged(callback: (user: IUser | null) => void): () => void {
this.authStateChangedCallback = callback;

callback(this.authenticatedUser);

return () => {
this.authStateChangedCallback = undefined;
}
}

public async login(email: string, password: string): Promise<IUser> {
this.authenticatedUser = {
email,
displayName: null,
}

this.authStateChangedCallback?.call(this, this.authenticatedUser);

return this.authenticatedUser;
}

public async register(email: string, password: string): Promise<IUser> {
this.authenticatedUser = {
email,
displayName: null,
}

this.authStateChangedCallback?.call(this, this.authenticatedUser);

return this.authenticatedUser;
}

public async loginWithGoogle(): Promise<IUser> {
this.authenticatedUser = {
email: '[email protected]',
displayName: null,
}

this.authStateChangedCallback?.call(this, this.authenticatedUser);

return this.authenticatedUser;
}

public async logout(): Promise<void> {
this.authenticatedUser = null;

this.authStateChangedCallback?.call(this, this.authenticatedUser);
}

// eslint-disable-next-line @typescript-eslint/no-empty-function
public async resetPassword(email: string): Promise<void> {
}
}

0 comments on commit a8d2f96

Please sign in to comment.