Application Android et IOS pour l'amicale des élèves https://play.google.com/store/apps/details?id=fr.amicaleinsat.application
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TabNavigator.tsx 9.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. /*
  2. * Copyright (c) 2019 - 2020 Arnaud Vergnet.
  3. *
  4. * This file is part of Campus INSAT.
  5. *
  6. * Campus INSAT is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Campus INSAT is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. import * as React from 'react';
  20. import { createStackNavigator } from '@react-navigation/stack';
  21. import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
  22. import { Title, useTheme } from 'react-native-paper';
  23. import { StyleSheet } from 'react-native';
  24. import i18n from 'i18n-js';
  25. import { View } from 'react-native-animatable';
  26. import HomeScreen from '../screens/Home/HomeScreen';
  27. import PlanningScreen from '../screens/Planning/PlanningScreen';
  28. import PlanningDisplayScreen from '../screens/Planning/PlanningDisplayScreen';
  29. import ProxiwashScreen from '../screens/Proxiwash/ProxiwashScreen';
  30. import ProxiwashAboutScreen from '../screens/Proxiwash/ProxiwashAboutScreen';
  31. import PlanexScreen from '../screens/Planex/PlanexScreen';
  32. import ClubDisplayScreen from '../screens/Amicale/Clubs/ClubDisplayScreen';
  33. import ScannerScreen from '../screens/Home/ScannerScreen';
  34. import FeedItemScreen from '../screens/Home/FeedItemScreen';
  35. import GroupSelectionScreen from '../screens/Planex/GroupSelectionScreen';
  36. import CustomTabBar from '../components/Tabbar/CustomTabBar';
  37. import WebsitesHomeScreen from '../screens/Services/ServicesScreen';
  38. import ServicesSectionScreen from '../screens/Services/ServicesSectionScreen';
  39. import AmicaleContactScreen from '../screens/Amicale/AmicaleContactScreen';
  40. import Mascot, { MASCOT_STYLE } from '../components/Mascot/Mascot';
  41. import { usePreferences } from '../context/preferencesContext';
  42. import {
  43. getPreferenceString,
  44. GeneralPreferenceKeys,
  45. } from '../utils/asyncStorage';
  46. const styles = StyleSheet.create({
  47. header: {
  48. flexDirection: 'row',
  49. },
  50. mascot: {
  51. width: 50,
  52. },
  53. title: {
  54. marginLeft: 10,
  55. marginTop: 'auto',
  56. marginBottom: 'auto',
  57. },
  58. });
  59. type DefaultParams = { [key in TabRoutes]: object | undefined };
  60. export type FullParamsList = DefaultParams & {
  61. [TabRoutes.Home]: {
  62. nextScreen: string;
  63. data: Record<string, object | undefined>;
  64. };
  65. };
  66. // Don't know why but TS is complaining without this
  67. // See: https://stackoverflow.com/questions/63652687/interface-does-not-satisfy-the-constraint-recordstring-object-undefined
  68. export type TabStackParamsList = FullParamsList &
  69. Record<string, object | undefined>;
  70. const ServicesStack = createStackNavigator();
  71. function ServicesStackComponent() {
  72. return (
  73. <ServicesStack.Navigator initialRouteName={'index'} headerMode={'screen'}>
  74. <ServicesStack.Screen
  75. name={'index'}
  76. component={WebsitesHomeScreen}
  77. options={{ title: i18n.t('screens.services.title') }}
  78. />
  79. <ServicesStack.Screen
  80. name={'services-section'}
  81. component={ServicesSectionScreen}
  82. options={{ title: 'SECTION' }}
  83. />
  84. <ServicesStack.Screen
  85. name={'amicale-contact'}
  86. component={AmicaleContactScreen}
  87. options={{ title: i18n.t('screens.amicaleAbout.title') }}
  88. />
  89. </ServicesStack.Navigator>
  90. );
  91. }
  92. const ProxiwashStack = createStackNavigator();
  93. function ProxiwashStackComponent() {
  94. return (
  95. <ProxiwashStack.Navigator initialRouteName={'index'} headerMode={'screen'}>
  96. <ProxiwashStack.Screen
  97. name={'index-contact'}
  98. component={ProxiwashScreen}
  99. options={{ title: i18n.t('screens.proxiwash.title') }}
  100. />
  101. <ProxiwashStack.Screen
  102. name={'proxiwash-about'}
  103. component={ProxiwashAboutScreen}
  104. options={{ title: i18n.t('screens.proxiwash.title') }}
  105. />
  106. </ProxiwashStack.Navigator>
  107. );
  108. }
  109. const PlanningStack = createStackNavigator();
  110. function PlanningStackComponent() {
  111. return (
  112. <PlanningStack.Navigator initialRouteName={'index'} headerMode={'screen'}>
  113. <PlanningStack.Screen
  114. name={'index'}
  115. component={PlanningScreen}
  116. options={{ title: i18n.t('screens.planning.title') }}
  117. />
  118. <PlanningStack.Screen
  119. name={'planning-information'}
  120. component={PlanningDisplayScreen}
  121. options={{ title: i18n.t('screens.planning.eventDetails') }}
  122. />
  123. </PlanningStack.Navigator>
  124. );
  125. }
  126. const HomeStack = createStackNavigator();
  127. function HomeStackComponent(
  128. initialRoute?: string,
  129. defaultData?: { [key: string]: string }
  130. ) {
  131. let params;
  132. if (initialRoute) {
  133. params = { data: defaultData, nextScreen: initialRoute, shouldOpen: true };
  134. }
  135. const { colors } = useTheme();
  136. return (
  137. <HomeStack.Navigator initialRouteName={'index'} headerMode={'screen'}>
  138. <HomeStack.Screen
  139. name={'index'}
  140. component={HomeScreen}
  141. options={{
  142. title: i18n.t('screens.home.title'),
  143. headerStyle: {
  144. backgroundColor: colors.surface,
  145. },
  146. headerTitle: (headerProps) => (
  147. <View style={styles.header}>
  148. <Mascot
  149. style={styles.mascot}
  150. emotion={MASCOT_STYLE.RANDOM}
  151. animated
  152. entryAnimation={{
  153. animation: 'bounceIn',
  154. duration: 1000,
  155. }}
  156. loopAnimation={{
  157. animation: 'pulse',
  158. duration: 2000,
  159. iterationCount: 'infinite',
  160. }}
  161. />
  162. <Title style={styles.title}>{headerProps.children}</Title>
  163. </View>
  164. ),
  165. }}
  166. initialParams={params}
  167. />
  168. <HomeStack.Screen
  169. name={'scanner'}
  170. component={ScannerScreen}
  171. options={{ title: i18n.t('screens.scanner.title') }}
  172. />
  173. <HomeStack.Screen
  174. name={'club-information'}
  175. component={ClubDisplayScreen}
  176. options={{
  177. title: i18n.t('screens.clubs.details'),
  178. }}
  179. />
  180. <HomeStack.Screen
  181. name={'feed-information'}
  182. component={FeedItemScreen}
  183. options={{
  184. title: i18n.t('screens.home.feed'),
  185. }}
  186. />
  187. <HomeStack.Screen
  188. name={'planning-information'}
  189. component={PlanningDisplayScreen}
  190. options={{
  191. title: i18n.t('screens.planning.eventDetails'),
  192. }}
  193. />
  194. </HomeStack.Navigator>
  195. );
  196. }
  197. const PlanexStack = createStackNavigator();
  198. function PlanexStackComponent() {
  199. return (
  200. <PlanexStack.Navigator initialRouteName={'index'} headerMode={'screen'}>
  201. <PlanexStack.Screen
  202. name={'index'}
  203. component={PlanexScreen}
  204. options={{
  205. title: i18n.t('screens.planex.title'),
  206. }}
  207. />
  208. <PlanexStack.Screen
  209. name={'group-select'}
  210. component={GroupSelectionScreen}
  211. options={{
  212. title: '',
  213. }}
  214. />
  215. </PlanexStack.Navigator>
  216. );
  217. }
  218. const Tab = createBottomTabNavigator<TabStackParamsList>();
  219. type PropsType = {
  220. defaultHomeRoute?: string;
  221. defaultHomeData?: { [key: string]: string };
  222. };
  223. const ICONS: {
  224. [key: string]: {
  225. normal: string;
  226. focused: string;
  227. };
  228. } = {
  229. services: {
  230. normal: 'account-circle-outline',
  231. focused: 'account-circle',
  232. },
  233. proxiwash: {
  234. normal: 'tshirt-crew-outline',
  235. focused: 'tshirt-crew',
  236. },
  237. home: {
  238. normal: '',
  239. focused: '',
  240. },
  241. events: {
  242. normal: 'calendar-range-outline',
  243. focused: 'calendar-range',
  244. },
  245. planex: {
  246. normal: 'clock-outline',
  247. focused: 'clock',
  248. },
  249. };
  250. function TabNavigator(props: PropsType) {
  251. const { preferences } = usePreferences();
  252. let defaultRoute = getPreferenceString(
  253. GeneralPreferenceKeys.defaultStartScreen,
  254. preferences
  255. );
  256. if (!defaultRoute) {
  257. defaultRoute = 'home';
  258. } else {
  259. defaultRoute = defaultRoute.toLowerCase();
  260. }
  261. const createHomeStackComponent = () =>
  262. HomeStackComponent(props.defaultHomeRoute, props.defaultHomeData);
  263. const LABELS: {
  264. [key: string]: string;
  265. } = {
  266. services: i18n.t('screens.services.title'),
  267. proxiwash: i18n.t('screens.proxiwash.title'),
  268. home: i18n.t('screens.home.title'),
  269. events: i18n.t('screens.planning.title'),
  270. planex: i18n.t('screens.planex.title'),
  271. };
  272. return (
  273. <Tab.Navigator
  274. initialRouteName={defaultRoute}
  275. tabBar={(tabProps) => (
  276. <CustomTabBar {...tabProps} labels={LABELS} icons={ICONS} />
  277. )}
  278. >
  279. <Tab.Screen
  280. name={'services'}
  281. component={ServicesStackComponent}
  282. options={{ title: i18n.t('screens.services.title') }}
  283. />
  284. <Tab.Screen
  285. name={'proxiwash'}
  286. component={ProxiwashStackComponent}
  287. options={{ title: i18n.t('screens.proxiwash.title') }}
  288. />
  289. <Tab.Screen
  290. name={'home'}
  291. component={createHomeStackComponent}
  292. options={{ title: i18n.t('screens.home.title') }}
  293. />
  294. <Tab.Screen
  295. name={'events'}
  296. component={PlanningStackComponent}
  297. options={{ title: i18n.t('screens.planning.title') }}
  298. />
  299. <Tab.Screen
  300. name={'planex'}
  301. component={PlanexStackComponent}
  302. options={{ title: i18n.t('screens.planex.title') }}
  303. />
  304. </Tab.Navigator>
  305. );
  306. }
  307. export default React.memo(
  308. TabNavigator,
  309. (pp: PropsType, np: PropsType) =>
  310. pp.defaultHomeRoute === np.defaultHomeRoute &&
  311. pp.defaultHomeData === np.defaultHomeData
  312. );
  313. export enum TabRoutes {
  314. Services = 'services',
  315. Proxiwash = 'proxiwash',
  316. Home = 'home',
  317. Planning = 'events',
  318. Planex = 'planex',
  319. }