refactor: project structure
@ -15,10 +15,9 @@ Un tracker d'habitudes pour performer au boulot et dans la vie de tous les jours
|
|||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
- [Sujet du projet](./docs/SUJET.md)
|
- [Sujet](./docs/SUJET.md) + [Cahier des charges](./docs/CAHIER-DES-CHARGES.md)
|
||||||
- [Cahier des charges](./docs/CAHIER-DES-CHARGES.md)
|
|
||||||
- [Modèle Logique des Données (MLD)](./docs/MLD.md)
|
- [Modèle Logique des Données (MLD)](./docs/MLD.md)
|
||||||
- [Clean Architecture](./docs/ARCHITECTURE.md)
|
- [Architecture](./docs/ARCHITECTURE.md)
|
||||||
- [Conventions développement informatique](./docs/CONVENTIONS.md)
|
- [Conventions développement informatique](./docs/CONVENTIONS.md)
|
||||||
|
|
||||||
#### Principaux Outils Informatiques Utilisés
|
#### Principaux Outils Informatiques Utilisés
|
||||||
@ -26,8 +25,10 @@ Un tracker d'habitudes pour performer au boulot et dans la vie de tous les jours
|
|||||||
- [React Native](https://reactnative.dev/) + [Expo](https://expo.io/): Framework pour le développement d'applications mobiles.
|
- [React Native](https://reactnative.dev/) + [Expo](https://expo.io/): Framework pour le développement d'applications mobiles.
|
||||||
- [TypeScript](https://www.typescriptlang.org/): Langage de programmation.
|
- [TypeScript](https://www.typescriptlang.org/): Langage de programmation.
|
||||||
- [React Native Paper](https://callstack.github.io/react-native-paper/): Bibliothèque de composants pour React Native.
|
- [React Native Paper](https://callstack.github.io/react-native-paper/): Bibliothèque de composants pour React Native.
|
||||||
<!-- - [WatermelonDB](https://nozbe.github.io/WatermelonDB/): Base de données locale, pour permettre une utilisation hors-ligne de l'application. -->
|
|
||||||
- [Supabase](https://supabase.io/): Backend, serveur d'API pour le stockage des données.
|
- [Supabase](https://supabase.io/): Backend, serveur d'API pour le stockage des données.
|
||||||
|
<!--
|
||||||
|
- [WatermelonDB](https://nozbe.github.io/WatermelonDB/): Base de données locale, pour permettre une utilisation hors-ligne de l'application.
|
||||||
|
-->
|
||||||
|
|
||||||
## Développement du projet en local
|
## Développement du projet en local
|
||||||
|
|
||||||
|
8
app.json
@ -4,11 +4,11 @@
|
|||||||
"slug": "p61-project",
|
"slug": "p61-project",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"orientation": "portrait",
|
"orientation": "portrait",
|
||||||
"icon": "./assets/images/icon.png",
|
"icon": "./presentation/assets/images/icon.png",
|
||||||
"scheme": "p61-project",
|
"scheme": "p61-project",
|
||||||
"userInterfaceStyle": "automatic",
|
"userInterfaceStyle": "automatic",
|
||||||
"splash": {
|
"splash": {
|
||||||
"image": "./assets/images/splashscreen.jpg",
|
"image": "./presentation/assets/images/splashscreen.jpg",
|
||||||
"resizeMode": "cover",
|
"resizeMode": "cover",
|
||||||
"backgroundColor": "#74b6cb"
|
"backgroundColor": "#74b6cb"
|
||||||
},
|
},
|
||||||
@ -18,14 +18,14 @@
|
|||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"adaptiveIcon": {
|
"adaptiveIcon": {
|
||||||
"foregroundImage": "./assets/images/adaptive-icon.png",
|
"foregroundImage": "./presentation/assets/images/adaptive-icon.png",
|
||||||
"backgroundColor": "#ffffff"
|
"backgroundColor": "#ffffff"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"web": {
|
"web": {
|
||||||
"bundler": "metro",
|
"bundler": "metro",
|
||||||
"output": "static",
|
"output": "static",
|
||||||
"favicon": "./assets/images/favicon.png"
|
"favicon": "./presentation/assets/images/favicon.png"
|
||||||
},
|
},
|
||||||
"plugins": ["expo-router"],
|
"plugins": ["expo-router"],
|
||||||
"experiments": {
|
"experiments": {
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
import { StyleSheet, Text, View } from "react-native"
|
import { StyleSheet, Text, View } from "react-native"
|
||||||
import { SafeAreaView } from "react-native-safe-area-context"
|
import { SafeAreaView } from "react-native-safe-area-context"
|
||||||
|
|
||||||
import { useHabitsTracker } from "@/contexts/HabitsTracker"
|
import { useHabitsTracker } from "@/presentation/react/contexts/HabitsTracker"
|
||||||
|
|
||||||
const HabitsPage: React.FC = () => {
|
const HabitsPage: React.FC = () => {
|
||||||
const { habitsTrackerPresenterState } = useHabitsTracker()
|
const { habitsTracker } = useHabitsTracker()
|
||||||
const { habitsTracker } = habitsTrackerPresenterState
|
|
||||||
const { habitProgressHistories } = habitsTracker
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={styles.container}>
|
||||||
{habitProgressHistories.map((progressHistory) => {
|
{habitsTracker.habitsHistory.map((progressHistory) => {
|
||||||
const { habit } = progressHistory
|
const { habit } = progressHistory
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -8,10 +8,10 @@ import {
|
|||||||
} from "react-native-paper"
|
} from "react-native-paper"
|
||||||
import { StatusBar } from "expo-status-bar"
|
import { StatusBar } from "expo-status-bar"
|
||||||
|
|
||||||
import CanterburyFont from "../assets/fonts/Canterbury.ttf"
|
import CanterburyFont from "../presentation/assets/fonts/Canterbury.ttf"
|
||||||
import GeoramFont from "../assets/fonts/Georama-Black.ttf"
|
import GeoramFont from "../presentation/assets/fonts/Georama-Black.ttf"
|
||||||
import SpaceMonoFont from "../assets/fonts/SpaceMono-Regular.ttf"
|
import SpaceMonoFont from "../presentation/assets/fonts/SpaceMono-Regular.ttf"
|
||||||
import { HabitsTrackerProvider } from "@/contexts/HabitsTracker"
|
import { HabitsTrackerProvider } from "@/presentation/react/contexts/HabitsTracker"
|
||||||
|
|
||||||
export { ErrorBoundary } from "expo-router"
|
export { ErrorBoundary } from "expo-router"
|
||||||
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
import type { Habit } from "./Habit"
|
|
||||||
import type { HabitProgress } from "./HabitProgress"
|
|
||||||
|
|
||||||
export interface HabitProgressHistoryOptions {
|
|
||||||
habit: Habit
|
|
||||||
habitProgresses: HabitProgress[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export class HabitProgressHistory implements HabitProgressHistoryOptions {
|
|
||||||
public habit: Habit
|
|
||||||
public habitProgresses: HabitProgress[]
|
|
||||||
|
|
||||||
public constructor(options: HabitProgressHistoryOptions) {
|
|
||||||
const { habit, habitProgresses } = options
|
|
||||||
this.habit = habit
|
|
||||||
this.habitProgresses = habitProgresses
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
import type { HabitProgressHistory } from "./HabitProgressHistory"
|
|
||||||
|
|
||||||
export interface HabitsTrackerOptions {
|
|
||||||
habitProgressHistories: HabitProgressHistory[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export class HabitsTracker implements HabitsTrackerOptions {
|
|
||||||
public habitProgressHistories: HabitProgressHistory[]
|
|
||||||
|
|
||||||
public constructor(options: HabitsTrackerOptions) {
|
|
||||||
const { habitProgressHistories } = options
|
|
||||||
this.habitProgressHistories = habitProgressHistories
|
|
||||||
}
|
|
||||||
|
|
||||||
public static default(): HabitsTracker {
|
|
||||||
return new HabitsTracker({ habitProgressHistories: [] })
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
import type { Habit } from "../entities/Habit"
|
|
||||||
import type { HabitProgress } from "../entities/HabitProgress"
|
|
||||||
|
|
||||||
export interface GetHabitProgressesOptions {
|
|
||||||
habit: Habit
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetHabitProgressesRepository {
|
|
||||||
execute: (options: GetHabitProgressesOptions) => Promise<HabitProgress[]>
|
|
||||||
}
|
|
@ -51,4 +51,4 @@ Vous serez évalués sur les aspects suivants :
|
|||||||
|
|
||||||
## Exemple d’interfaces
|
## Exemple d’interfaces
|
||||||
|
|
||||||
![UI Example](../assets/images/ui-example.png)
|
![UI Example](../presentation/assets/images/ui-example.png)
|
||||||
|
@ -22,15 +22,17 @@ Une pipeline CI ([`.gitlab-ci.yml`](.gitlab-ci.yml)) est en place pour vérifier
|
|||||||
|
|
||||||
Le projet suit la convention [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) reposant sur 2 branches principales:
|
Le projet suit la convention [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) reposant sur 2 branches principales:
|
||||||
|
|
||||||
- `main` (ou `master`): Contient le code de la dernière version stable et déployé en production.
|
- `main`: Contient le code de la dernière version stable et déployé en production.
|
||||||
- `develop`: Contient le code en cours de développement. Les nouvelles fonctionnalités et les correctifs de bugs sont fusionnés ici.
|
- `develop`: Contient le code en cours de développement. Les nouvelles fonctionnalités et les correctifs de bugs sont fusionnés ici.
|
||||||
|
|
||||||
|
Chaque nouvelle fonctionnalité ou correctif de bug est développé dans une branche dédiée à partir de `develop`, nommée `feat/<nom-de-la-fonctionnalité>` ou `fix/<nom-du-bug>`. Une fois le développement terminé, une merge request est créée pour demander une revue de code, et une fois validée, la branche est fusionnée dans `develop`, puis supprimée.
|
||||||
|
|
||||||
## Convention des commits
|
## Convention des commits
|
||||||
|
|
||||||
Les commits respectent la convention [Conventional Commits](https://www.conventionalcommits.org/) et [Semantic Versioning](https://semver.org/) pour la gestion des versions et des releases en fonction des commits.
|
Les commits respectent la convention [Conventional Commits](https://www.conventionalcommits.org/) et [Semantic Versioning](https://semver.org/) pour la gestion des versions et des releases en fonction des commits.
|
||||||
|
|
||||||
Les commits doivent être **atomiques** c'est à dire qu'il respecte 3 règles:
|
Les commits doivent être **atomiques** c'est à dire qu'il respecte 3 règles:
|
||||||
|
|
||||||
- Ne concerne qu'un seul sujet (une fonctionnalité, une correction de bug, etc.)
|
- Ne concerne qu'un seul sujet (une fonctionnalité, une correction de bug, etc.).
|
||||||
- Doit avoir un message clair et concis
|
- Doit avoir un message clair et concis.
|
||||||
- Ne doit pas rendre de dépôt "incohérent" (bloque la CI du projet)
|
- Ne doit pas rendre de dépôt "incohérent" (bloque la CI du projet).
|
||||||
|
18
domain/entities/HabitHistory.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import type { Habit } from "./Habit"
|
||||||
|
import type { HabitProgress } from "./HabitProgress"
|
||||||
|
|
||||||
|
export interface HabitHistoryOptions {
|
||||||
|
habit: Habit
|
||||||
|
progressHistory: HabitProgress[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HabitHistory implements HabitHistoryOptions {
|
||||||
|
public habit: Habit
|
||||||
|
public progressHistory: HabitProgress[]
|
||||||
|
|
||||||
|
public constructor(options: HabitHistoryOptions) {
|
||||||
|
const { habit, progressHistory } = options
|
||||||
|
this.habit = habit
|
||||||
|
this.progressHistory = progressHistory
|
||||||
|
}
|
||||||
|
}
|
18
domain/entities/HabitsTracker.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import type { HabitHistory } from "./HabitHistory"
|
||||||
|
|
||||||
|
export interface HabitsTrackerOptions {
|
||||||
|
habitsHistory: HabitHistory[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HabitsTracker implements HabitsTrackerOptions {
|
||||||
|
public habitsHistory: HabitHistory[]
|
||||||
|
|
||||||
|
public constructor(options: HabitsTrackerOptions) {
|
||||||
|
const { habitsHistory } = options
|
||||||
|
this.habitsHistory = habitsHistory
|
||||||
|
}
|
||||||
|
|
||||||
|
public static default(): HabitsTracker {
|
||||||
|
return new HabitsTracker({ habitsHistory: [] })
|
||||||
|
}
|
||||||
|
}
|
10
domain/repositories/GetHabitProgressHistory.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import type { Habit } from "../entities/Habit"
|
||||||
|
import type { HabitProgress } from "../entities/HabitProgress"
|
||||||
|
|
||||||
|
export interface GetHabitProgressHistoryOptions {
|
||||||
|
habit: Habit
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetHabitProgressHistoryRepository {
|
||||||
|
execute: (options: GetHabitProgressHistoryOptions) => Promise<HabitProgress[]>
|
||||||
|
}
|
@ -1,12 +1,12 @@
|
|||||||
import { HabitProgressHistory } from "../entities/HabitProgressHistory"
|
import { HabitHistory } from "../entities/HabitHistory"
|
||||||
import { HabitsTracker } from "../entities/HabitsTracker"
|
import { HabitsTracker } from "../entities/HabitsTracker"
|
||||||
import type { User } from "../entities/User"
|
import type { User } from "../entities/User"
|
||||||
import type { GetHabitProgressesRepository } from "../repositories/GetHabitProgresses"
|
import type { GetHabitProgressHistoryRepository } from "../repositories/GetHabitProgressHistory"
|
||||||
import type { GetHabitsByUserIdRepository } from "../repositories/GetHabitsByUserId"
|
import type { GetHabitsByUserIdRepository } from "../repositories/GetHabitsByUserId"
|
||||||
|
|
||||||
export interface RetrieveHabitsTrackerUseCaseDependencyOptions {
|
export interface RetrieveHabitsTrackerUseCaseDependencyOptions {
|
||||||
getHabitsByUserIdRepository: GetHabitsByUserIdRepository
|
getHabitsByUserIdRepository: GetHabitsByUserIdRepository
|
||||||
getHabitProgressesRepository: GetHabitProgressesRepository
|
getHabitProgressHistoryRepository: GetHabitProgressHistoryRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RetrieveHabitsTrackerUseCaseOptions {
|
export interface RetrieveHabitsTrackerUseCaseOptions {
|
||||||
@ -17,11 +17,12 @@ export class RetrieveHabitsTrackerUseCase
|
|||||||
implements RetrieveHabitsTrackerUseCaseDependencyOptions
|
implements RetrieveHabitsTrackerUseCaseDependencyOptions
|
||||||
{
|
{
|
||||||
public getHabitsByUserIdRepository: GetHabitsByUserIdRepository
|
public getHabitsByUserIdRepository: GetHabitsByUserIdRepository
|
||||||
public getHabitProgressesRepository: GetHabitProgressesRepository
|
public getHabitProgressHistoryRepository: GetHabitProgressHistoryRepository
|
||||||
|
|
||||||
public constructor(options: RetrieveHabitsTrackerUseCaseDependencyOptions) {
|
public constructor(options: RetrieveHabitsTrackerUseCaseDependencyOptions) {
|
||||||
this.getHabitsByUserIdRepository = options.getHabitsByUserIdRepository
|
this.getHabitsByUserIdRepository = options.getHabitsByUserIdRepository
|
||||||
this.getHabitProgressesRepository = options.getHabitProgressesRepository
|
this.getHabitProgressHistoryRepository =
|
||||||
|
options.getHabitProgressHistoryRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
public async execute(
|
public async execute(
|
||||||
@ -31,15 +32,16 @@ export class RetrieveHabitsTrackerUseCase
|
|||||||
const habits = await this.getHabitsByUserIdRepository.execute({ userId })
|
const habits = await this.getHabitsByUserIdRepository.execute({ userId })
|
||||||
const habitProgressHistories = await Promise.all(
|
const habitProgressHistories = await Promise.all(
|
||||||
habits.map(async (habit) => {
|
habits.map(async (habit) => {
|
||||||
const habitProgresses = await this.getHabitProgressesRepository.execute(
|
const progressHistory =
|
||||||
{
|
await this.getHabitProgressHistoryRepository.execute({
|
||||||
habit,
|
habit,
|
||||||
},
|
})
|
||||||
)
|
return new HabitHistory({ habit, progressHistory })
|
||||||
return new HabitProgressHistory({ habit, habitProgresses })
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
const habitsTracker = new HabitsTracker({ habitProgressHistories })
|
const habitsTracker = new HabitsTracker({
|
||||||
|
habitsHistory: habitProgressHistories,
|
||||||
|
})
|
||||||
return habitsTracker
|
return habitsTracker
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,93 +0,0 @@
|
|||||||
import { act, renderHook, waitFor } from "@testing-library/react-native"
|
|
||||||
import AsyncStorage from "@react-native-async-storage/async-storage"
|
|
||||||
|
|
||||||
import { useLocalStorage } from "@/hooks/useLocalStorage"
|
|
||||||
|
|
||||||
describe("hooks/useLocalStorage", () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
await AsyncStorage.clear()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should get the default value", () => {
|
|
||||||
const key = "key"
|
|
||||||
const givenDefaultValue = { key: "value" }
|
|
||||||
const { result } = renderHook(() => {
|
|
||||||
return useLocalStorage(key, givenDefaultValue)
|
|
||||||
})
|
|
||||||
const [actualValue] = result.current
|
|
||||||
expect(actualValue).toEqual(givenDefaultValue)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should get the value from the storage", async () => {
|
|
||||||
const key = "key"
|
|
||||||
const givenDefaultValue = { someValue: "value" }
|
|
||||||
const storedValue = { someValue: "value" }
|
|
||||||
const { result } = renderHook(() => {
|
|
||||||
return useLocalStorage(key, givenDefaultValue)
|
|
||||||
})
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(AsyncStorage.getItem).toHaveBeenCalledWith(key)
|
|
||||||
})
|
|
||||||
const [actualValue] = result.current
|
|
||||||
expect(actualValue).toEqual(storedValue)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should set the value to the storage", async () => {
|
|
||||||
const key = "key"
|
|
||||||
const givenDefaultValue = { someValue: "value" }
|
|
||||||
const storedValue = { someValue: "value" }
|
|
||||||
const { result } = renderHook(() => {
|
|
||||||
return useLocalStorage(key, givenDefaultValue)
|
|
||||||
})
|
|
||||||
const [, setValue] = result.current
|
|
||||||
await act(() => {
|
|
||||||
setValue(storedValue)
|
|
||||||
})
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(AsyncStorage.setItem).toHaveBeenCalledWith(
|
|
||||||
key,
|
|
||||||
JSON.stringify(storedValue),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should get default value if storage value is not valid JSON", async () => {
|
|
||||||
console.error = jest.fn()
|
|
||||||
const key = "key"
|
|
||||||
const givenDefaultValue = { someValue: "value" }
|
|
||||||
const storedValue = "{not valid JSON"
|
|
||||||
await AsyncStorage.setItem(key, storedValue)
|
|
||||||
const { result } = renderHook(() => {
|
|
||||||
return useLocalStorage(key, givenDefaultValue)
|
|
||||||
})
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(AsyncStorage.getItem).toHaveBeenCalledWith(key)
|
|
||||||
})
|
|
||||||
const [actualValue] = result.current
|
|
||||||
expect(actualValue).toEqual(givenDefaultValue)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should catch the error when setting the value to the storage", async () => {
|
|
||||||
console.error = jest.fn()
|
|
||||||
const key = "key"
|
|
||||||
const givenDefaultValue = { someValue: "value" }
|
|
||||||
const storedValue = { someValue: "value" }
|
|
||||||
const error = new Error("error")
|
|
||||||
;(AsyncStorage.setItem as jest.Mock).mockRejectedValue(error)
|
|
||||||
const { result } = renderHook(() => {
|
|
||||||
return useLocalStorage(key, givenDefaultValue)
|
|
||||||
})
|
|
||||||
const [, setValue] = result.current
|
|
||||||
await act(() => {
|
|
||||||
setValue(storedValue)
|
|
||||||
})
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(AsyncStorage.setItem).toHaveBeenCalledWith(
|
|
||||||
key,
|
|
||||||
JSON.stringify(storedValue),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
expect(console.error).toHaveBeenCalledWith(error)
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,43 +0,0 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from "react"
|
|
||||||
import AsyncStorage from "@react-native-async-storage/async-storage"
|
|
||||||
|
|
||||||
type UseLocalStorageResult<T> = [T, React.Dispatch<React.SetStateAction<T>>]
|
|
||||||
|
|
||||||
export const useLocalStorage = <T extends unknown>(
|
|
||||||
key: string,
|
|
||||||
defaultValue: T,
|
|
||||||
): UseLocalStorageResult<T> => {
|
|
||||||
const hasSaved = useRef(false)
|
|
||||||
const [value, setValue] = useState<T>(defaultValue)
|
|
||||||
|
|
||||||
const getFromLocalStorage = useCallback(async (): Promise<T> => {
|
|
||||||
const value = await AsyncStorage.getItem(key)
|
|
||||||
hasSaved.current = true
|
|
||||||
if (value == null) {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
return JSON.parse(value) as T
|
|
||||||
}, [key, defaultValue])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!hasSaved.current) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
AsyncStorage.setItem(key, JSON.stringify(value)).catch((error) => {
|
|
||||||
console.error(error)
|
|
||||||
})
|
|
||||||
}, [key, value])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getFromLocalStorage()
|
|
||||||
.then((value) => {
|
|
||||||
setValue(value)
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
setValue(defaultValue)
|
|
||||||
console.error(error)
|
|
||||||
})
|
|
||||||
}, [defaultValue, getFromLocalStorage])
|
|
||||||
|
|
||||||
return [value, setValue]
|
|
||||||
}
|
|
@ -1,17 +1,14 @@
|
|||||||
// export const taskRepository = new TaskLocalStorageRepository()
|
|
||||||
// export const taskService = new TaskService(taskRepository)
|
|
||||||
// export const taskPresenter = new TaskPresenter(taskService)
|
|
||||||
|
|
||||||
import { RetrieveHabitsTrackerUseCase } from "../domain/use-cases/RetrieveHabitsTracker"
|
import { RetrieveHabitsTrackerUseCase } from "../domain/use-cases/RetrieveHabitsTracker"
|
||||||
import { HabitsTrackerPresenter } from "./presenters/HabitsTrackerPresenter"
|
import { HabitsTrackerPresenter } from "../presentation/presenters/HabitsTracker"
|
||||||
import { GetHabitProgressesSupabaseRepository } from "./repositories/supabase/lib/GetHabitProgresses"
|
import { GetHabitProgressHistorySupabaseRepository } from "./repositories/supabase/lib/GetHabitProgressHistory"
|
||||||
import { GetHabitsByUserIdSupabaseRepository } from "./repositories/supabase/lib/GetHabitsByUserId"
|
import { GetHabitsByUserIdSupabaseRepository } from "./repositories/supabase/lib/GetHabitsByUserId"
|
||||||
import { supabaseClient } from "./repositories/supabase/supabase"
|
import { supabaseClient } from "./repositories/supabase/supabase"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repositories
|
* Repositories
|
||||||
*/
|
*/
|
||||||
const getHabitProgressesRepository = new GetHabitProgressesSupabaseRepository({
|
const getHabitProgressesRepository =
|
||||||
|
new GetHabitProgressHistorySupabaseRepository({
|
||||||
supabaseClient,
|
supabaseClient,
|
||||||
})
|
})
|
||||||
const getHabitsByUserIdRepository = new GetHabitsByUserIdSupabaseRepository({
|
const getHabitsByUserIdRepository = new GetHabitsByUserIdSupabaseRepository({
|
||||||
@ -22,7 +19,7 @@ const getHabitsByUserIdRepository = new GetHabitsByUserIdSupabaseRepository({
|
|||||||
* Use Cases
|
* Use Cases
|
||||||
*/
|
*/
|
||||||
const retrieveHabitsTrackerUseCase = new RetrieveHabitsTrackerUseCase({
|
const retrieveHabitsTrackerUseCase = new RetrieveHabitsTrackerUseCase({
|
||||||
getHabitProgressesRepository,
|
getHabitProgressHistoryRepository: getHabitProgressesRepository,
|
||||||
getHabitsByUserIdRepository,
|
getHabitsByUserIdRepository,
|
||||||
})
|
})
|
||||||
|
|
@ -1,17 +1,17 @@
|
|||||||
import type { GetHabitProgressesRepository } from "@/data/domain/repositories/GetHabitProgresses"
|
import type { GetHabitProgressHistoryRepository } from "@/domain/repositories/GetHabitProgressHistory"
|
||||||
import { SupabaseRepository } from "./_SupabaseRepository"
|
import { SupabaseRepository } from "./_SupabaseRepository"
|
||||||
import { HabitProgress } from "@/data/domain/entities/HabitProgress"
|
import { HabitProgress } from "@/domain/entities/HabitProgress"
|
||||||
import type { GoalProgress } from "@/data/domain/entities/Goal"
|
import type { GoalProgress } from "@/domain/entities/Goal"
|
||||||
import {
|
import {
|
||||||
GoalBooleanProgress,
|
GoalBooleanProgress,
|
||||||
GoalNumericProgress,
|
GoalNumericProgress,
|
||||||
} from "@/data/domain/entities/Goal"
|
} from "@/domain/entities/Goal"
|
||||||
|
|
||||||
export class GetHabitProgressesSupabaseRepository
|
export class GetHabitProgressHistorySupabaseRepository
|
||||||
extends SupabaseRepository
|
extends SupabaseRepository
|
||||||
implements GetHabitProgressesRepository
|
implements GetHabitProgressHistoryRepository
|
||||||
{
|
{
|
||||||
execute: GetHabitProgressesRepository["execute"] = async (options) => {
|
execute: GetHabitProgressHistoryRepository["execute"] = async (options) => {
|
||||||
const { habit } = options
|
const { habit } = options
|
||||||
const { data, error } = await this.supabaseClient
|
const { data, error } = await this.supabaseClient
|
||||||
.from("habits_progresses")
|
.from("habits_progresses")
|
||||||
@ -20,7 +20,7 @@ export class GetHabitProgressesSupabaseRepository
|
|||||||
if (error != null) {
|
if (error != null) {
|
||||||
throw new Error(error.message)
|
throw new Error(error.message)
|
||||||
}
|
}
|
||||||
const habitProgresses = data.map((item) => {
|
const habitProgressHistory = data.map((item) => {
|
||||||
let goalProgress: GoalProgress | null = null
|
let goalProgress: GoalProgress | null = null
|
||||||
if (habit.goal.isNumeric()) {
|
if (habit.goal.isNumeric()) {
|
||||||
goalProgress = new GoalNumericProgress({
|
goalProgress = new GoalNumericProgress({
|
||||||
@ -44,6 +44,6 @@ export class GetHabitProgressesSupabaseRepository
|
|||||||
})
|
})
|
||||||
return habitProgress
|
return habitProgress
|
||||||
})
|
})
|
||||||
return habitProgresses
|
return habitProgressHistory
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import type { GetHabitsByUserIdRepository } from "@/data/domain/repositories/GetHabitsByUserId"
|
import type { GetHabitsByUserIdRepository } from "@/domain/repositories/GetHabitsByUserId"
|
||||||
import { SupabaseRepository } from "./_SupabaseRepository"
|
import { SupabaseRepository } from "./_SupabaseRepository"
|
||||||
import { Habit } from "@/data/domain/entities/Habit"
|
import { Habit } from "@/domain/entities/Habit"
|
||||||
import type { Goal } from "@/data/domain/entities/Goal"
|
import type { Goal } from "@/domain/entities/Goal"
|
||||||
import { GoalBoolean, GoalNumeric } from "@/data/domain/entities/Goal"
|
import { GoalBoolean, GoalNumeric } from "@/domain/entities/Goal"
|
||||||
|
|
||||||
export class GetHabitsByUserIdSupabaseRepository
|
export class GetHabitsByUserIdSupabaseRepository
|
||||||
extends SupabaseRepository
|
extends SupabaseRepository
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"preset": "jest-expo",
|
"preset": "jest-expo",
|
||||||
"roots": ["./"],
|
"roots": ["./"],
|
||||||
"setupFilesAfterEnv": ["<rootDir>/tests/jest.setup.ts"],
|
"setupFilesAfterEnv": ["@testing-library/react-native/extend-expect"],
|
||||||
"fakeTimers": {
|
"fakeTimers": {
|
||||||
"enableGlobally": true
|
"enableGlobally": true
|
||||||
},
|
},
|
||||||
@ -10,7 +10,7 @@
|
|||||||
"coverageReporters": ["text", "text-summary", "cobertura"],
|
"coverageReporters": ["text", "text-summary", "cobertura"],
|
||||||
"collectCoverageFrom": [
|
"collectCoverageFrom": [
|
||||||
"<rootDir>/**/*.{ts,tsx}",
|
"<rootDir>/**/*.{ts,tsx}",
|
||||||
"!<rootDir>/components/ExternalLink.tsx",
|
"!<rootDir>/presentation/react/components/ExternalLink.tsx",
|
||||||
"!<rootDir>/.expo",
|
"!<rootDir>/.expo",
|
||||||
"!<rootDir>/app/+html.tsx",
|
"!<rootDir>/app/+html.tsx",
|
||||||
"!<rootDir>/app/**/_layout.tsx",
|
"!<rootDir>/app/**/_layout.tsx",
|
||||||
|
59
package-lock.json
generated
@ -10,7 +10,6 @@
|
|||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/vector-icons": "14.0.0",
|
"@expo/vector-icons": "14.0.0",
|
||||||
"@react-native-async-storage/async-storage": "1.21.0",
|
|
||||||
"@react-navigation/native": "6.1.16",
|
"@react-navigation/native": "6.1.16",
|
||||||
"@supabase/supabase-js": "2.39.8",
|
"@supabase/supabase-js": "2.39.8",
|
||||||
"expo": "50.0.13",
|
"expo": "50.0.13",
|
||||||
@ -34,7 +33,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.24.0",
|
"@babel/core": "7.24.0",
|
||||||
"@commitlint/cli": "19.1.0",
|
"@commitlint/cli": "19.2.0",
|
||||||
"@commitlint/config-conventional": "19.1.0",
|
"@commitlint/config-conventional": "19.1.0",
|
||||||
"@testing-library/react-native": "12.4.3",
|
"@testing-library/react-native": "12.4.3",
|
||||||
"@total-typescript/ts-reset": "0.5.1",
|
"@total-typescript/ts-reset": "0.5.1",
|
||||||
@ -51,7 +50,7 @@
|
|||||||
"eslint-plugin-import": "2.29.1",
|
"eslint-plugin-import": "2.29.1",
|
||||||
"eslint-plugin-prettier": "5.1.3",
|
"eslint-plugin-prettier": "5.1.3",
|
||||||
"eslint-plugin-promise": "6.1.1",
|
"eslint-plugin-promise": "6.1.1",
|
||||||
"eslint-plugin-react": "7.34.0",
|
"eslint-plugin-react": "7.34.1",
|
||||||
"eslint-plugin-react-hooks": "4.6.0",
|
"eslint-plugin-react-hooks": "4.6.0",
|
||||||
"eslint-plugin-react-native": "4.1.0",
|
"eslint-plugin-react-native": "4.1.0",
|
||||||
"eslint-plugin-unicorn": "51.0.1",
|
"eslint-plugin-unicorn": "51.0.1",
|
||||||
@ -2122,15 +2121,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/cli": {
|
"node_modules/@commitlint/cli": {
|
||||||
"version": "19.1.0",
|
"version": "19.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.2.0.tgz",
|
||||||
"integrity": "sha512-SYGm8HGbVzrlSYeB6oo6pG1Ec6bOMJcDsXgNGa4vgZQsPj6nJkcbTWlIRmtmIk0tHi0d5sCljGuQ+g/0NCPv7w==",
|
"integrity": "sha512-8XnQDMyQR+1/ldbmIyhonvnDS2enEw48Wompo/967fsEvy9Vj5/JbDutzmSBKxANWDVeEbR9QQm0yHpw6ArrFw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@commitlint/format": "^19.0.3",
|
"@commitlint/format": "^19.0.3",
|
||||||
"@commitlint/lint": "^19.1.0",
|
"@commitlint/lint": "^19.1.0",
|
||||||
"@commitlint/load": "^19.1.0",
|
"@commitlint/load": "^19.2.0",
|
||||||
"@commitlint/read": "^19.0.3",
|
"@commitlint/read": "^19.2.0",
|
||||||
"@commitlint/types": "^19.0.3",
|
"@commitlint/types": "^19.0.3",
|
||||||
"execa": "^8.0.1",
|
"execa": "^8.0.1",
|
||||||
"yargs": "^17.0.0"
|
"yargs": "^17.0.0"
|
||||||
@ -5292,17 +5291,6 @@
|
|||||||
"react": "^16.8 || ^17.0 || ^18.0"
|
"react": "^16.8 || ^17.0 || ^18.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@react-native-async-storage/async-storage": {
|
|
||||||
"version": "1.21.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.21.0.tgz",
|
|
||||||
"integrity": "sha512-JL0w36KuFHFCvnbOXRekqVAUplmOyT/OuCQkogo6X98MtpSaJOKEAeZnYO8JB0U/RIEixZaGI5px73YbRm/oag==",
|
|
||||||
"dependencies": {
|
|
||||||
"merge-options": "^3.0.4"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react-native": "^0.0.0-0 || >=0.60 <1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@react-native-community/cli": {
|
"node_modules/@react-native-community/cli": {
|
||||||
"version": "12.3.6",
|
"version": "12.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-12.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-12.3.6.tgz",
|
||||||
@ -11525,9 +11513,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-react": {
|
"node_modules/eslint-plugin-react": {
|
||||||
"version": "7.34.0",
|
"version": "7.34.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz",
|
||||||
"integrity": "sha512-MeVXdReleBTdkz/bvcQMSnCXGi+c9kvy51IpinjnJgutl3YTHWsDdke7Z1ufZpGfDG8xduBDKyjtB9JH1eBKIQ==",
|
"integrity": "sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"array-includes": "^3.1.7",
|
"array-includes": "^3.1.7",
|
||||||
@ -13997,11 +13985,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-plain-obj": {
|
"node_modules/is-plain-obj": {
|
||||||
"version": "2.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
|
||||||
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
|
"integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-plain-object": {
|
"node_modules/is-plain-object": {
|
||||||
@ -17761,17 +17749,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/merge-options": {
|
|
||||||
"version": "3.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz",
|
|
||||||
"integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"is-plain-obj": "^2.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/merge-stream": {
|
"node_modules/merge-stream": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||||
@ -21261,14 +21238,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sort-keys/node_modules/is-plain-obj": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/source-map": {
|
"node_modules/source-map": {
|
||||||
"version": "0.7.4",
|
"version": "0.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/vector-icons": "14.0.0",
|
"@expo/vector-icons": "14.0.0",
|
||||||
"@react-native-async-storage/async-storage": "1.21.0",
|
|
||||||
"@react-navigation/native": "6.1.16",
|
"@react-navigation/native": "6.1.16",
|
||||||
"@supabase/supabase-js": "2.39.8",
|
"@supabase/supabase-js": "2.39.8",
|
||||||
"expo": "50.0.13",
|
"expo": "50.0.13",
|
||||||
@ -43,7 +42,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.24.0",
|
"@babel/core": "7.24.0",
|
||||||
"@commitlint/cli": "19.1.0",
|
"@commitlint/cli": "19.2.0",
|
||||||
"@commitlint/config-conventional": "19.1.0",
|
"@commitlint/config-conventional": "19.1.0",
|
||||||
"@testing-library/react-native": "12.4.3",
|
"@testing-library/react-native": "12.4.3",
|
||||||
"@total-typescript/ts-reset": "0.5.1",
|
"@total-typescript/ts-reset": "0.5.1",
|
||||||
@ -60,7 +59,7 @@
|
|||||||
"eslint-plugin-import": "2.29.1",
|
"eslint-plugin-import": "2.29.1",
|
||||||
"eslint-plugin-prettier": "5.1.3",
|
"eslint-plugin-prettier": "5.1.3",
|
||||||
"eslint-plugin-promise": "6.1.1",
|
"eslint-plugin-promise": "6.1.1",
|
||||||
"eslint-plugin-react": "7.34.0",
|
"eslint-plugin-react": "7.34.1",
|
||||||
"eslint-plugin-react-hooks": "4.6.0",
|
"eslint-plugin-react-hooks": "4.6.0",
|
||||||
"eslint-plugin-react-native": "4.1.0",
|
"eslint-plugin-react-native": "4.1.0",
|
||||||
"eslint-plugin-unicorn": "51.0.1",
|
"eslint-plugin-unicorn": "51.0.1",
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
@ -1,9 +1,9 @@
|
|||||||
import { HabitsTracker } from "@/data/domain/entities/HabitsTracker"
|
import { HabitsTracker } from "@/domain/entities/HabitsTracker"
|
||||||
import { Presenter } from "./_Presenter"
|
import { Presenter } from "./_Presenter"
|
||||||
import type {
|
import type {
|
||||||
RetrieveHabitsTrackerUseCase,
|
RetrieveHabitsTrackerUseCase,
|
||||||
RetrieveHabitsTrackerUseCaseOptions,
|
RetrieveHabitsTrackerUseCaseOptions,
|
||||||
} from "@/data/domain/use-cases/RetrieveHabitsTracker"
|
} from "@/domain/use-cases/RetrieveHabitsTracker"
|
||||||
|
|
||||||
export interface HabitsTrackerPresenterState {
|
export interface HabitsTrackerPresenterState {
|
||||||
habitsTracker: HabitsTracker
|
habitsTracker: HabitsTracker
|
@ -1,6 +1,6 @@
|
|||||||
import renderer from "react-test-renderer"
|
import renderer from "react-test-renderer"
|
||||||
|
|
||||||
import { ButtonCustom } from "@/components/ButtonCustom"
|
import { ButtonCustom } from "@/presentation/react/components/ButtonCustom"
|
||||||
|
|
||||||
describe("<ButtonCustom />", () => {
|
describe("<ButtonCustom />", () => {
|
||||||
it("renders correctly", () => {
|
it("renders correctly", () => {
|
@ -1,6 +1,6 @@
|
|||||||
import renderer from "react-test-renderer"
|
import renderer from "react-test-renderer"
|
||||||
|
|
||||||
import { ExternalLink } from "@/components/ExternalLink"
|
import { ExternalLink } from "@/presentation/react/components/ExternalLink"
|
||||||
|
|
||||||
describe("<ExternalLink />", () => {
|
describe("<ExternalLink />", () => {
|
||||||
it("renders correctly", () => {
|
it("renders correctly", () => {
|
@ -1,6 +1,6 @@
|
|||||||
import renderer from "react-test-renderer"
|
import renderer from "react-test-renderer"
|
||||||
|
|
||||||
import { MonoText } from "@/components/MonoText"
|
import { MonoText } from "@/presentation/react/components/MonoText"
|
||||||
|
|
||||||
describe("<MonoText />", () => {
|
describe("<MonoText />", () => {
|
||||||
it("renders correctly", () => {
|
it("renders correctly", () => {
|
@ -3,12 +3,11 @@ import { createContext, useContext, useEffect } from "react"
|
|||||||
import type {
|
import type {
|
||||||
HabitsTrackerPresenter,
|
HabitsTrackerPresenter,
|
||||||
HabitsTrackerPresenterState,
|
HabitsTrackerPresenterState,
|
||||||
} from "@/data/infrastructure/presenters/HabitsTrackerPresenter"
|
} from "@/presentation/presenters/HabitsTracker"
|
||||||
import { usePresenterState } from "@/hooks/usePresenterState"
|
import { usePresenterState } from "@/presentation/react/hooks/usePresenterState"
|
||||||
import { habitsTrackerPresenter } from "@/data/infrastructure"
|
import { habitsTrackerPresenter } from "@/infrastructure"
|
||||||
|
|
||||||
export interface HabitsTrackerContextValue {
|
export interface HabitsTrackerContextValue extends HabitsTrackerPresenterState {
|
||||||
habitsTrackerPresenterState: HabitsTrackerPresenterState
|
|
||||||
habitsTrackerPresenter: HabitsTrackerPresenter
|
habitsTrackerPresenter: HabitsTrackerPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +35,10 @@ export const HabitsTrackerProvider: React.FC<HabitsTrackerProviderProps> = (
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<HabitsTrackerContext.Provider
|
<HabitsTrackerContext.Provider
|
||||||
value={{ habitsTrackerPresenterState, habitsTrackerPresenter }}
|
value={{
|
||||||
|
...habitsTrackerPresenterState,
|
||||||
|
habitsTrackerPresenter,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</HabitsTrackerContext.Provider>
|
</HabitsTrackerContext.Provider>
|
@ -1,6 +1,6 @@
|
|||||||
import { act, renderHook } from "@testing-library/react-native"
|
import { act, renderHook } from "@testing-library/react-native"
|
||||||
|
|
||||||
import { useBoolean } from "@/hooks/useBoolean"
|
import { useBoolean } from "@/presentation/react/hooks/useBoolean"
|
||||||
|
|
||||||
describe("hooks/useBoolean", () => {
|
describe("hooks/useBoolean", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
import type { Presenter } from "@/data/infrastructure/presenters/_Presenter"
|
import type { Presenter } from "@/presentation/presenters/_Presenter"
|
||||||
|
|
||||||
export const usePresenterState = <S>(presenter: Presenter<S>): S => {
|
export const usePresenterState = <S>(presenter: Presenter<S>): S => {
|
||||||
const [state, setState] = useState<S>(presenter.initialState)
|
const [state, setState] = useState<S>(presenter.initialState)
|
@ -1,5 +0,0 @@
|
|||||||
import "@testing-library/react-native/extend-expect"
|
|
||||||
|
|
||||||
jest.mock("@react-native-async-storage/async-storage", () => {
|
|
||||||
return require("@react-native-async-storage/async-storage/jest/async-storage-mock")
|
|
||||||
})
|
|