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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  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
  74. initialRouteName={'services'}
  75. headerMode={'screen'}
  76. >
  77. <ServicesStack.Screen
  78. name={'services'}
  79. component={WebsitesHomeScreen}
  80. options={{ title: i18n.t('screens.services.title') }}
  81. />
  82. <ServicesStack.Screen
  83. name={'services-section'}
  84. component={ServicesSectionScreen}
  85. options={{ title: 'SECTION' }}
  86. />
  87. <ServicesStack.Screen
  88. name={'amicale-contact'}
  89. component={AmicaleContactScreen}
  90. options={{ title: i18n.t('screens.amicaleAbout.title') }}
  91. />
  92. </ServicesStack.Navigator>
  93. );
  94. }
  95. const ProxiwashStack = createStackNavigator();
  96. function ProxiwashStackComponent() {
  97. return (
  98. <ProxiwashStack.Navigator
  99. initialRouteName={'proxiwash'}
  100. headerMode={'screen'}
  101. >
  102. <ProxiwashStack.Screen
  103. name={'proxiwash'}
  104. component={ProxiwashScreen}
  105. options={{ title: i18n.t('screens.proxiwash.title') }}
  106. />
  107. <ProxiwashStack.Screen
  108. name={'proxiwash-about'}
  109. component={ProxiwashAboutScreen}
  110. options={{ title: i18n.t('screens.proxiwash.title') }}
  111. />
  112. </ProxiwashStack.Navigator>
  113. );
  114. }
  115. const PlanningStack = createStackNavigator();
  116. function PlanningStackComponent() {
  117. return (
  118. <PlanningStack.Navigator initialRouteName={'events'} headerMode={'screen'}>
  119. <PlanningStack.Screen
  120. name={'events'}
  121. component={PlanningScreen}
  122. options={{ title: i18n.t('screens.planning.title') }}
  123. />
  124. <PlanningStack.Screen
  125. name={'planning-information'}
  126. component={PlanningDisplayScreen}
  127. options={{ title: i18n.t('screens.planning.eventDetails') }}
  128. />
  129. </PlanningStack.Navigator>
  130. );
  131. }
  132. const HomeStack = createStackNavigator();
  133. function HomeStackComponent(
  134. initialRoute?: string,
  135. defaultData?: { [key: string]: string }
  136. ) {
  137. let params;
  138. if (initialRoute) {
  139. params = { data: defaultData, nextScreen: initialRoute, shouldOpen: true };
  140. }
  141. const { colors } = useTheme();
  142. return (
  143. <HomeStack.Navigator initialRouteName={'home'} headerMode={'screen'}>
  144. <HomeStack.Screen
  145. name={'home'}
  146. component={HomeScreen}
  147. options={{
  148. title: i18n.t('screens.home.title'),
  149. headerStyle: {
  150. backgroundColor: colors.surface,
  151. },
  152. headerTitle: (headerProps) => (
  153. <View style={styles.header}>
  154. <Mascot
  155. style={styles.mascot}
  156. emotion={MASCOT_STYLE.RANDOM}
  157. animated
  158. entryAnimation={{
  159. animation: 'bounceIn',
  160. duration: 1000,
  161. }}
  162. loopAnimation={{
  163. animation: 'pulse',
  164. duration: 2000,
  165. iterationCount: 'infinite',
  166. }}
  167. />
  168. <Title style={styles.title}>{headerProps.children}</Title>
  169. </View>
  170. ),
  171. }}
  172. initialParams={params}
  173. />
  174. <HomeStack.Screen
  175. name={'scanner'}
  176. component={ScannerScreen}
  177. options={{ title: i18n.t('screens.scanner.title') }}
  178. />
  179. <HomeStack.Screen
  180. name={'club-information'}
  181. component={ClubDisplayScreen}
  182. options={{
  183. title: i18n.t('screens.clubs.details'),
  184. }}
  185. />
  186. <HomeStack.Screen
  187. name={'feed-information'}
  188. component={FeedItemScreen}
  189. options={{
  190. title: i18n.t('screens.home.feed'),
  191. }}
  192. />
  193. <HomeStack.Screen
  194. name={'planning-information'}
  195. component={PlanningDisplayScreen}
  196. options={{
  197. title: i18n.t('screens.planning.eventDetails'),
  198. }}
  199. />
  200. </HomeStack.Navigator>
  201. );
  202. }
  203. const PlanexStack = createStackNavigator();
  204. function PlanexStackComponent() {
  205. return (
  206. <PlanexStack.Navigator initialRouteName={'planex'} headerMode={'screen'}>
  207. <PlanexStack.Screen
  208. name={'planex'}
  209. component={PlanexScreen}
  210. options={{
  211. title: i18n.t('screens.planex.title'),
  212. }}
  213. />
  214. <PlanexStack.Screen
  215. name={'group-select'}
  216. component={GroupSelectionScreen}
  217. options={{
  218. title: '',
  219. }}
  220. />
  221. </PlanexStack.Navigator>
  222. );
  223. }
  224. const Tab = createBottomTabNavigator<TabStackParamsList>();
  225. type PropsType = {
  226. defaultHomeRoute?: string;
  227. defaultHomeData?: { [key: string]: string };
  228. };
  229. const ICONS: {
  230. [key: string]: {
  231. normal: string;
  232. focused: string;
  233. };
  234. } = {
  235. services: {
  236. normal: 'account-circle-outline',
  237. focused: 'account-circle',
  238. },
  239. proxiwash: {
  240. normal: 'tshirt-crew-outline',
  241. focused: 'tshirt-crew',
  242. },
  243. home: {
  244. normal: '',
  245. focused: '',
  246. },
  247. events: {
  248. normal: 'calendar-range-outline',
  249. focused: 'calendar-range',
  250. },
  251. planex: {
  252. normal: 'clock-outline',
  253. focused: 'clock',
  254. },
  255. };
  256. function TabNavigator(props: PropsType) {
  257. const { preferences } = usePreferences();
  258. let defaultRoute = getPreferenceString(
  259. GeneralPreferenceKeys.defaultStartScreen,
  260. preferences
  261. );
  262. if (!defaultRoute) {
  263. defaultRoute = 'home';
  264. } else {
  265. defaultRoute = defaultRoute.toLowerCase();
  266. }
  267. const createHomeStackComponent = () =>
  268. HomeStackComponent(props.defaultHomeRoute, props.defaultHomeData);
  269. const LABELS: {
  270. [key: string]: string;
  271. } = {
  272. services: i18n.t('screens.services.title'),
  273. proxiwash: i18n.t('screens.proxiwash.title'),
  274. home: i18n.t('screens.home.title'),
  275. events: i18n.t('screens.planning.title'),
  276. planex: i18n.t('screens.planex.title'),
  277. };
  278. return (
  279. <Tab.Navigator
  280. initialRouteName={defaultRoute}
  281. tabBar={(tabProps) => (
  282. <CustomTabBar {...tabProps} labels={LABELS} icons={ICONS} />
  283. )}
  284. >
  285. <Tab.Screen
  286. name={'services'}
  287. component={ServicesStackComponent}
  288. options={{ title: i18n.t('screens.services.title') }}
  289. />
  290. <Tab.Screen
  291. name={'proxiwash'}
  292. component={ProxiwashStackComponent}
  293. options={{ title: i18n.t('screens.proxiwash.title') }}
  294. />
  295. <Tab.Screen
  296. name={'home'}
  297. component={createHomeStackComponent}
  298. options={{ title: i18n.t('screens.home.title') }}
  299. />
  300. <Tab.Screen
  301. name={'events'}
  302. component={PlanningStackComponent}
  303. options={{ title: i18n.t('screens.planning.title') }}
  304. />
  305. <Tab.Screen
  306. name={'planex'}
  307. component={PlanexStackComponent}
  308. options={{ title: i18n.t('screens.planex.title') }}
  309. />
  310. </Tab.Navigator>
  311. );
  312. }
  313. export default React.memo(
  314. TabNavigator,
  315. (pp: PropsType, np: PropsType) =>
  316. pp.defaultHomeRoute === np.defaultHomeRoute &&
  317. pp.defaultHomeData === np.defaultHomeData
  318. );
  319. export enum TabRoutes {
  320. Services = 'services',
  321. Proxiwash = 'proxiwash',
  322. Home = 'home',
  323. Planning = 'events',
  324. Planex = 'planex',
  325. }