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.7KB

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