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.

GroupSelectionScreen.tsx 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  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 React, { useCallback, useLayoutEffect, useState } from 'react';
  20. import { Platform } from 'react-native';
  21. import i18n from 'i18n-js';
  22. import { Searchbar } from 'react-native-paper';
  23. import { stringMatchQuery } from '../../utils/Search';
  24. import WebSectionList from '../../components/Screens/WebSectionList';
  25. import GroupListAccordion from '../../components/Lists/PlanexGroups/GroupListAccordion';
  26. import Urls from '../../constants/Urls';
  27. import { readData } from '../../utils/WebData';
  28. import { useNavigation } from '@react-navigation/core';
  29. import { useCachedPlanexGroups } from '../../context/cacheContext';
  30. import { usePlanexPreferences } from '../../context/preferencesContext';
  31. import {
  32. getPreferenceObject,
  33. PlanexPreferenceKeys,
  34. } from '../../utils/asyncStorage';
  35. export type PlanexGroupType = {
  36. name: string;
  37. id: number;
  38. };
  39. export type PlanexGroupCategoryType = {
  40. name: string;
  41. id: number;
  42. content: Array<PlanexGroupType>;
  43. };
  44. export type PlanexGroupsType = { [key: string]: PlanexGroupCategoryType };
  45. function sortName(
  46. a: PlanexGroupType | PlanexGroupCategoryType,
  47. b: PlanexGroupType | PlanexGroupCategoryType
  48. ): number {
  49. if (a.name.toLowerCase() < b.name.toLowerCase()) {
  50. return -1;
  51. }
  52. if (a.name.toLowerCase() > b.name.toLowerCase()) {
  53. return 1;
  54. }
  55. return 0;
  56. }
  57. function GroupSelectionScreen() {
  58. const navigation = useNavigation();
  59. const { preferences, updatePreferences } = usePlanexPreferences();
  60. const { groups, setGroups } = useCachedPlanexGroups();
  61. const [currentSearchString, setCurrentSearchString] = useState('');
  62. const getFavoriteGroups = (): Array<PlanexGroupType> => {
  63. const data = getPreferenceObject(
  64. PlanexPreferenceKeys.planexFavoriteGroups,
  65. preferences
  66. );
  67. if (data) {
  68. return data as Array<PlanexGroupType>;
  69. } else {
  70. return [];
  71. }
  72. };
  73. const favoriteGroups = getFavoriteGroups();
  74. useLayoutEffect(() => {
  75. navigation.setOptions({
  76. headerTitle: getSearchBar,
  77. headerBackTitleVisible: false,
  78. headerTitleContainerStyle:
  79. Platform.OS === 'ios'
  80. ? { marginHorizontal: 0, width: '70%' }
  81. : { marginHorizontal: 0, right: 50, left: 50 },
  82. });
  83. }, [navigation]);
  84. const getSearchBar = () => {
  85. return (
  86. // @ts-ignore
  87. <Searchbar
  88. placeholder={i18n.t('screens.proximo.search')}
  89. onChangeText={setCurrentSearchString}
  90. />
  91. );
  92. };
  93. /**
  94. * Gets a render item for the given article
  95. *
  96. * @param item The article to render
  97. * @return {*}
  98. */
  99. const getRenderItem = ({ item }: { item: PlanexGroupCategoryType }) => (
  100. <GroupListAccordion
  101. item={item}
  102. favorites={[...favoriteGroups]}
  103. onGroupPress={onListItemPress}
  104. onFavoritePress={onListFavoritePress}
  105. currentSearchString={currentSearchString}
  106. />
  107. );
  108. /**
  109. * Creates the dataset to be used in the FlatList
  110. *
  111. * @param fetchedData
  112. * @return {*}
  113. * */
  114. const createDataset = (
  115. fetchedData:
  116. | {
  117. [key: string]: PlanexGroupCategoryType;
  118. }
  119. | undefined
  120. ): Array<{ title: string; data: Array<PlanexGroupCategoryType> }> => {
  121. if (fetchedData) {
  122. return [
  123. {
  124. title: '',
  125. data: generateData(fetchedData),
  126. },
  127. ];
  128. } else {
  129. return [];
  130. }
  131. };
  132. /**
  133. * Callback used when clicking an article in the list.
  134. * It opens the modal to show detailed information about the article
  135. *
  136. * @param item The article pressed
  137. */
  138. const onListItemPress = (item: PlanexGroupType) => {
  139. updatePreferences(PlanexPreferenceKeys.planexCurrentGroup, item);
  140. navigation.goBack();
  141. };
  142. /**
  143. * Callback used when the user clicks on the favorite button
  144. *
  145. * @param item The item to add/remove from favorites
  146. */
  147. const onListFavoritePress = useCallback(
  148. (group: PlanexGroupType) => {
  149. const updateFavorites = (newValue: Array<PlanexGroupType>) => {
  150. updatePreferences(PlanexPreferenceKeys.planexFavoriteGroups, newValue);
  151. };
  152. const removeGroupFromFavorites = (g: PlanexGroupType) => {
  153. updateFavorites(favoriteGroups.filter((f) => f.id !== g.id));
  154. };
  155. const addGroupToFavorites = (g: PlanexGroupType) => {
  156. updateFavorites([...favoriteGroups, g].sort(sortName));
  157. };
  158. if (favoriteGroups.some((f) => f.id === group.id)) {
  159. removeGroupFromFavorites(group);
  160. } else {
  161. addGroupToFavorites(group);
  162. }
  163. },
  164. [favoriteGroups, updatePreferences]
  165. );
  166. /**
  167. * Generates the dataset to be used in the FlatList.
  168. * This improves formatting of group names, sorts alphabetically the categories, and adds favorites at the top.
  169. *
  170. * @param fetchedData The raw data fetched from the server
  171. * @returns {[]}
  172. */
  173. const generateData = (
  174. fetchedData: PlanexGroupsType
  175. ): Array<PlanexGroupCategoryType> => {
  176. const data: Array<PlanexGroupCategoryType> = [];
  177. // Convert the object into an array
  178. Object.values(fetchedData).forEach((category: PlanexGroupCategoryType) => {
  179. const content: Array<PlanexGroupType> = [];
  180. // Filter groups matching the search query
  181. category.content.forEach((g: PlanexGroupType) => {
  182. if (stringMatchQuery(g.name, currentSearchString)) {
  183. content.push(g);
  184. }
  185. });
  186. // Only add categories with groups matching the query
  187. if (content.length > 0) {
  188. data.push({
  189. id: category.id,
  190. name: category.name,
  191. content: content,
  192. });
  193. }
  194. });
  195. data.sort(sortName);
  196. // Add the favorites at the top
  197. data.unshift({
  198. name: i18n.t('screens.planex.favorites.title'),
  199. id: 0,
  200. content: favoriteGroups,
  201. });
  202. return data;
  203. };
  204. return (
  205. <WebSectionList
  206. request={() => readData<PlanexGroupsType>(Urls.planex.groups)}
  207. createDataset={createDataset}
  208. refreshOnFocus={true}
  209. renderItem={getRenderItem}
  210. extraData={currentSearchString + favoriteGroups.length}
  211. cache={groups}
  212. onCacheUpdate={setGroups}
  213. />
  214. );
  215. }
  216. export default GroupSelectionScreen;