All files / src/screens/home useHomeScreenController.tsx

41.66% Statements 20/48
40% Branches 4/10
23.07% Functions 3/13
42.55% Lines 20/47

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202                                                                                                                          3x 3x 3x 3x 3x 3x 3x 3x 3x       3x         3x           3x 1x 1x     3x           3x 1x                                                                   1x 1x                                                                                                     3x                                      
/**
 * @fileoverview useHomeScreenController hook controls the HomeScreen component
 * @module useHomeScreenController Controller Hook
 * @description Controls the HomeScreen component by managing the states of the screen.
 * @requires react
 * @requires react-native
 * @requires @react-native-community/geolocation
 */
 
import { useContext, useEffect, useState } from 'react'
import Geolocation from '@react-native-community/geolocation'
import { Place } from '@global/types/Places'
import { AccountContext, defaultLocalisation } from '@global/contexts/AccountProvider'
import getCity from '@helpers/httpClient/localization'
import { Alert, Linking } from 'react-native'
import { GeolocationResponse } from '@global/types/Account'
import useNoloPlaces, { useSearchNoloPlaces } from '@helpers/httpClient/queries/places/useNoloPlaces'
 
/**
 * @interface HomeScreenController
 * @description HomeScreenController types
 * @property {string} city The search of the user
 * @property {'map' | 'carousel'} currentPage The current page of the screen
 * @property {boolean} displaySearchBar The display state of the search bar
 * @property {() => void} toggleSearchBar The function to toggle the search bar
 * @property {string} searchValue The value of the search bar
 * @property {(value: string) => void} setSearchValue The function to set the value of the search bar
 * @property {() => void} togglePage The function to toggle the current page of the screen
 * @property {Place[]} places The places of the user
 * @property {() => Place[]} getNearestPlaces The function to get the nearest places
 * @property {boolean} isLoading The loading state of the screen
 * @property {() => void} onRefresh The function to refresh the screen
 * @property {boolean} isRefreshing The refreshing state of the screen
 * @property {boolean} isSuccessful The success state of the screen
 */
interface HomeScreenController {
  city: string
  currentPage: 'map' | 'carousel'
  displaySearchBar: boolean
  toggleSearchBar: () => void
  searchValue: string
  setSearchValue: (value: string) => void
  togglePage: () => void
  places: Place[]
  latestPlaces: Place[]
  getNearestPlaces: () => Place[]
  getAllPlacesUsingSearch: () => void
  isLoading: boolean
  onRefresh: () => void
  isRefreshing: boolean
  isSuccessful: boolean
  goToFilterPage: () => void
}
 
/**
 * @function useHomeScreenController
 * @description HomeScreenController hook logic function
 * @returns {HomeScreenController} The controller of the HomeScreen component
 */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default function useHomeScreenController(navigation: any): HomeScreenController {
  const [userCity, setUserCity] = useState('')
  const [city, setCity] = useState('')
  const [currentPage, setCurrentPage] = useState<'map' | 'carousel'>('carousel')
  const [displaySearchBar, setDisplaySearchBar] = useState(false)
  const [searchValue, setSearchValue] = useState('')
  const { account, setAccount } = useContext(AccountContext)
  const [places, setPlaces] = useState<Place[]>([])
  const [latestPlaces, setLatestPlaces] = useState<Place[]>([])
  const noloPlacesMutation = useNoloPlaces({
    setPlaces,
    token: account.accessToken,
  })
  const noloPlacesMutationUsingSearch = useSearchNoloPlaces({
    setPlaces,
    q: searchValue,
    token: account.accessToken,
  })
  const noloLatestPlacesMutation = useNoloPlaces({
    setPlaces: setLatestPlaces,
    token: account.accessToken,
    getLatest: true,
  })
 
  const getAllPlaces = () => {
    noloPlacesMutation.mutate()
    noloLatestPlacesMutation.mutate()
  }
 
  const getAllPlacesUsingSearch = () => {
    noloPlacesMutationUsingSearch.mutate()
    toggleSearchBar()
    setCity(searchValue === '' ? userCity : searchValue)
  }
 
  useEffect(() => {
    Geolocation.getCurrentPosition(
      async (info: GeolocationResponse) => {
        setAccount({ ...account, localisation: info })
 
        const reversedCity = await getCity({ latitude: info.coords.latitude, longitude: info.coords.longitude })
 
        setUserCity(reversedCity)
        setCity(reversedCity)
      },
      async () => {
        setCity('Nantes')
        setAccount({
          ...account,
          localisation: defaultLocalisation,
        })
        const reversedCity = await getCity({
          latitude: defaultLocalisation.coords.latitude,
          longitude: defaultLocalisation.coords.longitude,
        })
 
        setUserCity(reversedCity)
        setCity(reversedCity)
 
        Alert.alert(
          'Localisation introuvable',
          "Vous avez désactivé la localisation, pour optimiser votre expérience, veuillez l'activer dans vos réglages",
          [
            { text: 'Activer', onPress: () => Linking.openSettings() },
            { text: 'Plus tard', style: 'cancel' },
          ]
        )
      },
      { enableHighAccuracy: true }
    )
    setCity(userCity)
    getAllPlaces()
    // Avoid infinite loop
  }, [])
 
  function onRefresh() {
    getAllPlaces()
  }
 
  function togglePage() {
    setCurrentPage(currentPage === 'map' ? 'carousel' : 'map')
  }
 
  function toggleSearchBar() {
    setDisplaySearchBar(!displaySearchBar)
  }
 
  function goToFilterPage() {
    navigation.navigate('FilterModal', {
      userCity,
      setCity,
      mutationToApply: noloPlacesMutation.mutate,
    })
    setDisplaySearchBar(false)
  }
 
  /**
   * @function getNearestPlaces
   * @description Get the nearest places from the user with a calculation of the distance
   * @returns {Place[]} The 5 nearest places
   */
  function getNearestPlaces() {
    let localisation = {
      latitude: 0,
      longitude: 0,
    }
    if (account.localisation) localisation = account.localisation.coords
    const placesBis = [...places]
 
    const placesOrderedByDistance = placesBis.sort((a, b) => {
      const distanceA = Math.sqrt(
        (a.address.latitude - localisation.latitude) ** 2 + (a.address.longitude - localisation.longitude) ** 2
      )
      const distanceB = Math.sqrt(
        (b.address.latitude - localisation.latitude) ** 2 + (b.address.longitude - localisation.longitude) ** 2
      )
 
      return distanceA - distanceB
    })
    return placesOrderedByDistance.slice(0, 10)
  }
 
  return {
    city,
    currentPage,
    displaySearchBar,
    toggleSearchBar,
    searchValue,
    setSearchValue,
    togglePage,
    places,
    latestPlaces,
    getNearestPlaces,
    getAllPlacesUsingSearch,
    isLoading: noloPlacesMutation.isPending || noloPlacesMutationUsingSearch.isPending,
    onRefresh,
    isRefreshing: noloPlacesMutation.isPending,
    isSuccessful: noloPlacesMutation.isSuccess || noloPlacesMutationUsingSearch.isSuccess,
    goToFilterPage,
  }
}