From 24e137f680dea2363a282443e43119b34c74a140 Mon Sep 17 00:00:00 2001
From: Diego Zafferani <43524162+DiegoZaff@users.noreply.github.com>
Date: Mon, 9 Jan 2023 02:40:16 +0100
Subject: [PATCH 01/17] Filters
---
...ngsScroll.tsx => ContentWrapperScroll.tsx} | 42 +++--
src/components/Groups/Filters.tsx | 176 ++++++++++++++++++
src/components/Groups/GroupsFiltered.tsx | 15 ++
src/components/Groups/OutlinedButton.tsx | 69 +++++++
src/components/Home/MainMenu.tsx | 4 +-
src/components/Home/PoliSearchBar.tsx | 52 +++---
src/components/Settings/index.ts | 2 +-
src/navigation/MainStackNavigator.tsx | 2 +
src/navigation/NavigationTypes.ts | 1 +
src/pages/Groups.tsx | 39 ++++
src/pages/settings/Help.tsx | 6 +-
src/pages/settings/Settings.tsx | 6 +-
12 files changed, 366 insertions(+), 48 deletions(-)
rename src/components/{Settings/SettingsScroll.tsx => ContentWrapperScroll.tsx} (68%)
create mode 100644 src/components/Groups/Filters.tsx
create mode 100644 src/components/Groups/GroupsFiltered.tsx
create mode 100644 src/components/Groups/OutlinedButton.tsx
create mode 100644 src/pages/Groups.tsx
diff --git a/src/components/Settings/SettingsScroll.tsx b/src/components/ContentWrapperScroll.tsx
similarity index 68%
rename from src/components/Settings/SettingsScroll.tsx
rename to src/components/ContentWrapperScroll.tsx
index c99a15b1..3d298634 100644
--- a/src/components/Settings/SettingsScroll.tsx
+++ b/src/components/ContentWrapperScroll.tsx
@@ -7,9 +7,10 @@ import { usePalette } from "utils/colors"
/**
* General component useful for pages with a scrollable content.
* It provides a navbar and a scrollview with margin and rounded corners.
+ * Default margin Top is 86 (proper margin for Settings Page)
*/
-export const SettingsScroll: FC<{
- title: string
+export const ContentWrapperScroll: FC<{
+ title?: string
/**
* Remove the navbar from the bottom of the page.
*/
@@ -18,6 +19,7 @@ export const SettingsScroll: FC<{
* Props for the navbar, see {@link NavBar}
*/
navbarOptions?: NavbarProps
+ marginTop?: number
children: React.ReactNode
}> = props => {
@@ -32,30 +34,33 @@ export const SettingsScroll: FC<{
backgroundColor: isLight ? primary : background,
}}
>
-
-
- {props.title}
-
-
+
+ {props.title}
+
+
+ )}
+
{props.children}
-
{navbar ? : null}
)
diff --git a/src/components/Groups/Filters.tsx b/src/components/Groups/Filters.tsx
new file mode 100644
index 00000000..6cb9d2fe
--- /dev/null
+++ b/src/components/Groups/Filters.tsx
@@ -0,0 +1,176 @@
+import React, { FC, useState } from "react"
+import { View } from "react-native"
+import { OutlinedButton } from "./OutlinedButton"
+import { StyleSheet } from "react-native"
+import { ModalCustomSettings, SelectTile } from "components/Settings"
+
+/* export interface FiltersProps {
+ prova?: string
+} */
+
+//This is a mess for now
+const yearsList = ["2021/2022", "2020/2021", "2019/2020", "2018/2019"]
+const coursesList = ["Triennale", "Magistrale", "Ciclo unico"]
+const typesList = ["A", "B", "C", "bho"]
+const platformsList = ["Telgram", "Watsapp", "Bho"]
+
+const getNameFromMode = (mode: string) => {
+ if (mode === "year") {
+ return "Anno"
+ } else if (mode === "course") {
+ return "Corso"
+ } else if (mode === "platform") {
+ return "Piattaforma"
+ } else {
+ return "Tipo"
+ }
+}
+export type ValidModalType = "year" | "course" | "type" | "platform"
+const all = "Tutti"
+export const Filters: FC = () => {
+ //show or hide modal
+ const [isModalShowing, setIsModalShowing] = useState(false)
+ //type of modal: year - type - course - platform
+ const [modalMode, setModalMode] = useState("year")
+ //items to show inside modal
+ const [modalItems, setModalItems] = useState(yearsList)
+ //currently selected item inside modal
+ const [selectedItem, setSelectedItem] = useState(all)
+
+ const [year, setYear] = useState(all)
+
+ const [course, setCourse] = useState(all)
+
+ const [type, setType] = useState(all)
+
+ const [platform, setPlatform] = useState(all)
+
+ //update state when user taps "OK" in modal
+ const updateSelectedFilter = () => {
+ if (modalMode === "year") {
+ setYear(selectedItem)
+ } else if (modalMode === "course") {
+ setCourse(selectedItem)
+ } else if (modalMode === "platform") {
+ setPlatform(selectedItem)
+ } else {
+ setType(selectedItem)
+ }
+ }
+ //reset state on "reset"
+ const reset = () => {
+ setYear(all)
+ setCourse(all)
+ setType(all)
+ setPlatform(all)
+ }
+ return (
+
+
+ {
+ setModalMode("year")
+ setModalItems(yearsList)
+ setSelectedItem(year)
+ setIsModalShowing(true)
+ }}
+ />
+ {
+ setModalMode("course")
+ setModalItems(coursesList)
+ setSelectedItem(course)
+ setIsModalShowing(true)
+ }}
+ />
+ {
+ setModalMode("type")
+ setModalItems(typesList)
+ setSelectedItem(type)
+ setIsModalShowing(true)
+ }}
+ />
+ {
+ setModalMode("platform")
+ setModalItems(platformsList)
+ setSelectedItem(platform)
+ setIsModalShowing(true)
+ }}
+ />
+
+
+ {
+ setIsModalShowing(false)
+ }}
+ selectedValue={selectedItem}
+ onOK={() => {
+ updateSelectedFilter()
+ setIsModalShowing(false)
+ }}
+ >
+ {
+ setSelectedItem(all)
+ }}
+ />
+ {modalItems?.map((itemName, index) => {
+ return (
+ {
+ setSelectedItem(modalItems[index])
+ }}
+ />
+ )
+ })}
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ buttonRightMargin: {
+ marginRight: 8,
+ },
+ buttonBottomMargin: {
+ marginBottom: 8,
+ },
+})
diff --git a/src/components/Groups/GroupsFiltered.tsx b/src/components/Groups/GroupsFiltered.tsx
new file mode 100644
index 00000000..af099f6c
--- /dev/null
+++ b/src/components/Groups/GroupsFiltered.tsx
@@ -0,0 +1,15 @@
+import { Text } from "components/Text"
+import React, { FC } from "react"
+import { View } from "react-native"
+
+export interface GroupsFilteredProps {
+ string?: string
+}
+
+export const GroupsFiltered: FC = props => {
+ return (
+
+ {props.string}
+
+ )
+}
diff --git a/src/components/Groups/OutlinedButton.tsx b/src/components/Groups/OutlinedButton.tsx
new file mode 100644
index 00000000..1d9243c5
--- /dev/null
+++ b/src/components/Groups/OutlinedButton.tsx
@@ -0,0 +1,69 @@
+import { Text } from "components/Text"
+import React, { FC } from "react"
+import { Pressable, StyleProp, ViewStyle } from "react-native"
+import { usePalette } from "utils/colors"
+import { StyleSheet } from "react-native"
+export interface OutlinedButtonProps {
+ text?: string
+ /**
+ * ResetButton
+ */
+ isSpecial?: boolean
+ isSelected?: boolean
+ onPress?: () => void
+ buttonStyle?: StyleProp
+}
+
+export const OutlinedButton: FC = props => {
+ const { isLight } = usePalette()
+ return (
+
+
+ {props.text}
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ button: {
+ borderWidth: 2,
+ height: 32,
+ minWidth: 100,
+ borderRadius: 60,
+ flexDirection: "row",
+ justifyContent: "center",
+ alignItems: "center",
+ },
+})
diff --git a/src/components/Home/MainMenu.tsx b/src/components/Home/MainMenu.tsx
index ebbd117d..26c10b70 100644
--- a/src/components/Home/MainMenu.tsx
+++ b/src/components/Home/MainMenu.tsx
@@ -162,7 +162,9 @@ export const MainMenu: FC<{ filter?: string }> = ({ filter }) => {
if (isDeleting) setIsDeleting(false)
if (buttonIcon.id === 9) setModalVisible(true)
// TODO: actual navigation
- if (!isDeleting && buttonIcon.id !== 9) {
+ if (buttonIcon.id === 5) {
+ navigate("Groups")
+ } else if (!isDeleting && buttonIcon.id !== 9) {
navigate("Error404")
}
}}
diff --git a/src/components/Home/PoliSearchBar.tsx b/src/components/Home/PoliSearchBar.tsx
index d5ec1624..8f60331f 100644
--- a/src/components/Home/PoliSearchBar.tsx
+++ b/src/components/Home/PoliSearchBar.tsx
@@ -1,5 +1,11 @@
import React, { FC, useEffect, useState, useRef } from "react"
-import { TextInput, Animated, Pressable } from "react-native"
+import {
+ TextInput,
+ Animated,
+ Pressable,
+ StyleProp,
+ ViewStyle,
+} from "react-native"
import { usePalette } from "utils/colors"
import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia"
import searchLight from "assets/menu/searchLight.svg"
@@ -10,7 +16,8 @@ import searchDark from "assets/menu/searchDark.svg"
*/
export const PoliSearchBar: FC<{
onChange: (searchKey: string) => void
-}> = ({ onChange }) => {
+ style?: StyleProp
+}> = ({ onChange, style }) => {
const { fieldBackground, fieldText, bodyText, isLight } = usePalette()
const svg = useSVG(isLight ? searchLight : searchDark)
@@ -36,26 +43,29 @@ export const PoliSearchBar: FC<{
return (
()
@@ -21,6 +22,7 @@ export const MainStack: FC = () => {
+
)
}
diff --git a/src/navigation/NavigationTypes.ts b/src/navigation/NavigationTypes.ts
index ade07f25..6db61600 100644
--- a/src/navigation/NavigationTypes.ts
+++ b/src/navigation/NavigationTypes.ts
@@ -44,6 +44,7 @@ export type MainStackNavigatorParams = {
Article: { article: Article }
NewsList: { categoryName: string }
Error404: undefined
+ Groups: undefined
}
export type SettingsStackNavigatorParams = {
diff --git a/src/pages/Groups.tsx b/src/pages/Groups.tsx
new file mode 100644
index 00000000..5c19a055
--- /dev/null
+++ b/src/pages/Groups.tsx
@@ -0,0 +1,39 @@
+import React, { useState } from "react"
+import { MainStackScreen } from "navigation/NavigationTypes"
+import { View } from "react-native"
+import { ContentWrapperScroll } from "components/ContentWrapperScroll"
+import { Title } from "components/Text"
+import { PoliSearchBar } from "components/Home"
+import { Filters } from "components/Groups/Filters"
+import { GroupsFiltered } from "components/Groups/GroupsFiltered"
+
+export const Groups: MainStackScreen<"Groups"> = () => {
+ const [search, setSearch] = useState("")
+
+ const [isSearching, setIsSearching] = useState(false)
+ return (
+
+
+
+ Gruppi Corsi
+ {
+ setSearch(value)
+ if (value !== "") {
+ setIsSearching(true)
+ } else if (isSearching === true) {
+ setIsSearching(false)
+ }
+ }}
+ style={{ marginTop: 46, marginBottom: 24 }}
+ />
+ {!isSearching ? (
+
+ ) : (
+
+ )}
+
+
+
+ )
+}
diff --git a/src/pages/settings/Help.tsx b/src/pages/settings/Help.tsx
index cb1c5d78..46df9bc9 100644
--- a/src/pages/settings/Help.tsx
+++ b/src/pages/settings/Help.tsx
@@ -1,7 +1,7 @@
import React from "react"
import { View } from "react-native"
import { SettingsStackScreen } from "navigation/NavigationTypes"
-import { SettingsScroll } from "components/Settings/SettingsScroll"
+import { ContentWrapperScroll } from "components/ContentWrapperScroll"
import { SettingTile } from "components/Settings/SettingTile"
import { SettingOptions } from "utils/settings"
@@ -28,12 +28,12 @@ export const settingsList: SettingOptions[] = [
*/
export const Help: SettingsStackScreen<"Help"> = () => {
return (
-
+
{settingsList.map((setting, index) => {
return
})}
-
+
)
}
diff --git a/src/pages/settings/Settings.tsx b/src/pages/settings/Settings.tsx
index 47953055..bd7a3c68 100644
--- a/src/pages/settings/Settings.tsx
+++ b/src/pages/settings/Settings.tsx
@@ -1,7 +1,7 @@
import React, { useContext, useState } from "react"
import { View } from "react-native"
import { SettingsStackScreen, useNavigation } from "navigation/NavigationTypes"
-import { SettingsScroll } from "components/Settings"
+import { ContentWrapperScroll } from "components/Settings"
import { Divider } from "components/Divider"
import { SettingTile } from "components/Settings"
import { settingsIcons } from "assets/settings"
@@ -86,7 +86,7 @@ export const SettingsPage: SettingsStackScreen<"Settings"> = () => {
return (
-
+
{loggedIn ? (
) : (
@@ -108,7 +108,7 @@ export const SettingsPage: SettingsStackScreen<"Settings"> = () => {
{settingsList.map((setting, index) => {
return
})}
-
+
Date: Wed, 11 Jan 2023 23:14:20 +0100
Subject: [PATCH 02/17] groups api
---
src/api/groups.ts | 45 +++++++++++++++++++
src/api/index.ts | 2 +
src/components/Groups/Filters.tsx | 23 ++++++++--
.../{ModalSettings.tsx => ModalSelection.tsx} | 6 ++-
src/components/Settings/index.ts | 2 +-
src/pages/settings/Settings.tsx | 10 ++---
6 files changed, 76 insertions(+), 12 deletions(-)
create mode 100644 src/api/groups.ts
rename src/components/Settings/{ModalSettings.tsx => ModalSelection.tsx} (96%)
diff --git a/src/api/groups.ts b/src/api/groups.ts
new file mode 100644
index 00000000..14bd21f0
--- /dev/null
+++ b/src/api/groups.ts
@@ -0,0 +1,45 @@
+import { getIsoStringFromDaysPassed } from "utils/functions"
+import { HttpClient, RequestOptions } from "./HttpClient"
+
+/* eslint-disable @typescript-eslint/naming-convention */
+
+export interface GroupOptions {
+ name?: string
+ year?: string
+ degree?: string
+ type?: string
+ platform?: string
+ language?: string
+ office?: string
+}
+
+const client = HttpClient.getInstance()
+
+/**
+ * Collection of endpoints related to Groups.
+ */
+export const groups = {
+ /**
+ * Retrieves groups from PoliNetwork server.
+ * Check {@link GroupOptions} for additional parameters.
+ */
+ // ! temporary
+ async get(groupsOptions?: GroupOptions, options?: RequestOptions) {
+ const response = await client.poliNetworkInstance.get(
+ "/v1/groups/search",
+ {
+ ...options,
+ params: {
+ name: groupsOptions?.name,
+ year: groupsOptions?.year,
+ degree: groupsOptions?.degree,
+ type: groupsOptions?.type,
+ platform: groupsOptions?.platform,
+ language: groupsOptions?.language,
+ office: groupsOptions?.office,
+ },
+ }
+ )
+ return response.data.results
+ },
+}
diff --git a/src/api/index.ts b/src/api/index.ts
index 833178dc..3cef1fe4 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -1,5 +1,6 @@
import { articles } from "./articles"
import { auth } from "./auth"
+import { groups } from "./groups"
import { tags } from "./tags"
import { timetable } from "./timetable"
import { user } from "./user"
@@ -27,4 +28,5 @@ export const api = {
tags,
timetable,
user,
+ groups,
}
diff --git a/src/components/Groups/Filters.tsx b/src/components/Groups/Filters.tsx
index 6cb9d2fe..13a774dc 100644
--- a/src/components/Groups/Filters.tsx
+++ b/src/components/Groups/Filters.tsx
@@ -1,8 +1,9 @@
-import React, { FC, useState } from "react"
+import React, { FC, useEffect, useState } from "react"
import { View } from "react-native"
import { OutlinedButton } from "./OutlinedButton"
import { StyleSheet } from "react-native"
-import { ModalCustomSettings, SelectTile } from "components/Settings"
+import { ModalSelection, SelectTile } from "components/Settings"
+import { api } from "api"
/* export interface FiltersProps {
prova?: string
@@ -64,6 +65,20 @@ export const Filters: FC = () => {
setType(all)
setPlatform(all)
}
+
+ const searchGroups = async () => {
+ try {
+ //broken
+ const response = await api.groups.get({ name: "Informatica" })
+ console.log(response)
+ } catch (error) {
+ console.log(error)
+ }
+ }
+
+ useEffect(() => {
+ void searchGroups()
+ }, [])
return (
{
onPress={reset}
/>
- {
@@ -161,7 +176,7 @@ export const Filters: FC = () => {
/>
)
})}
-
+
)
}
diff --git a/src/components/Settings/ModalSettings.tsx b/src/components/Settings/ModalSelection.tsx
similarity index 96%
rename from src/components/Settings/ModalSettings.tsx
rename to src/components/Settings/ModalSelection.tsx
index d7d52e4a..9f2ff791 100644
--- a/src/components/Settings/ModalSettings.tsx
+++ b/src/components/Settings/ModalSelection.tsx
@@ -4,7 +4,7 @@ import { Text } from "components/Text"
import { usePalette } from "utils/colors"
import { ButtonCustom } from "./ButtonCustom"
-export interface ModalCustomSettingsProps {
+export interface ModalSelectionProps {
/**
* content of the modal
*/
@@ -38,11 +38,13 @@ export interface ModalCustomSettingsProps {
height?: number
}
+// ? maybe should move this out of Settings folder ?
+
/**
* Custom Modal Component with two buttons at the bottom.
*
*/
-export const ModalCustomSettings: FC = props => {
+export const ModalSelection: FC = props => {
const { backgroundSecondary, homeBackground, modalBarrier, isLight } =
usePalette()
diff --git a/src/components/Settings/index.ts b/src/components/Settings/index.ts
index b4798f7b..b1393746 100644
--- a/src/components/Settings/index.ts
+++ b/src/components/Settings/index.ts
@@ -5,6 +5,6 @@ export * from "./SettingTile"
export * from "./SelectTile"
export * from "../ContentWrapperScroll"
export * from "./UserDetailsTile"
-export * from "./ModalSettings"
+export * from "./ModalSelection"
export * from "./UserAnonymousTile"
export * from "./CareerColumn"
diff --git a/src/pages/settings/Settings.tsx b/src/pages/settings/Settings.tsx
index bd7a3c68..f23a2033 100644
--- a/src/pages/settings/Settings.tsx
+++ b/src/pages/settings/Settings.tsx
@@ -6,7 +6,7 @@ import { Divider } from "components/Divider"
import { SettingTile } from "components/Settings"
import { settingsIcons } from "assets/settings"
import { UserDetailsTile } from "components/Settings"
-import { ModalCustomSettings } from "components/Settings"
+import { ModalSelection } from "components/Settings"
import { CareerTile } from "components/Settings"
import { SelectTile } from "components/Settings"
import { UserAnonymousTile } from "components/Settings"
@@ -110,7 +110,7 @@ export const SettingsPage: SettingsStackScreen<"Settings"> = () => {
})}
- = () => {
/>
)
})}
-
-
+ = () => {
)
})}
-
+
)
}
From fc9adb3dbaabe898e2918cf9dba54231029e2d47 Mon Sep 17 00:00:00 2001
From: DiegoZaff
Date: Fri, 13 Jan 2023 23:35:59 +0100
Subject: [PATCH 03/17] Group searching logic with mocked data and language
filter
---
src/api/groups.ts | 97 +++++++++++++-
src/components/Divider.tsx | 18 ++-
src/components/Groups/AnimatedLine.tsx | 51 ++++++++
.../Groups/ExpandablePoliSearchBar.tsx | 119 ++++++++++++++++++
src/components/Groups/Filters.tsx | 71 +++++------
src/components/Groups/GroupTile.tsx | 37 ++++++
src/components/Groups/GroupsFiltered.tsx | 15 ---
src/components/Groups/OutlinedButton.tsx | 6 +-
src/components/Text/BodyText.tsx | 4 +-
src/pages/Groups.tsx | 108 +++++++++++++---
src/utils/groups.ts | 11 ++
src/utils/useMounted.ts | 43 +++++++
12 files changed, 493 insertions(+), 87 deletions(-)
create mode 100644 src/components/Groups/AnimatedLine.tsx
create mode 100644 src/components/Groups/ExpandablePoliSearchBar.tsx
create mode 100644 src/components/Groups/GroupTile.tsx
delete mode 100644 src/components/Groups/GroupsFiltered.tsx
create mode 100644 src/utils/groups.ts
create mode 100644 src/utils/useMounted.ts
diff --git a/src/api/groups.ts b/src/api/groups.ts
index 14bd21f0..62d126d3 100644
--- a/src/api/groups.ts
+++ b/src/api/groups.ts
@@ -1,4 +1,3 @@
-import { getIsoStringFromDaysPassed } from "utils/functions"
import { HttpClient, RequestOptions } from "./HttpClient"
/* eslint-disable @typescript-eslint/naming-convention */
@@ -13,6 +12,19 @@ export interface GroupOptions {
office?: string
}
+export interface MockedGroup {
+ name?: string
+ year?: string
+ id?: string
+ degree?: string
+ type?: string
+ platform?: string
+ language?: string
+ office?: string
+ school?: string
+ idLink?: string
+}
+
const client = HttpClient.getInstance()
/**
@@ -23,7 +35,7 @@ export const groups = {
* Retrieves groups from PoliNetwork server.
* Check {@link GroupOptions} for additional parameters.
*/
- // ! temporary
+ // ! temporarily broken
async get(groupsOptions?: GroupOptions, options?: RequestOptions) {
const response = await client.poliNetworkInstance.get(
"/v1/groups/search",
@@ -42,4 +54,85 @@ export const groups = {
)
return response.data.results
},
+ getMocked() {
+ return mockedGroups.groups
+ },
+}
+
+//random information
+const mockedGroups = {
+ groups: [
+ {
+ name: "GRUPPO 1 ITA",
+ year: "2022",
+ id: "1",
+ degree: "LT",
+ type: "S",
+ platform: "WA",
+ language: "ITA",
+ office: "Leonardo",
+ school: "idk",
+ idLink: "https://t.me/joinchat/9RcVXahIKchlMGZk",
+ },
+ {
+ name: "GRUPPO 2 ITA",
+ year: "2021",
+ id: "1",
+ degree: "LT",
+ type: "S",
+ platform: "TG",
+ language: "ITA",
+ office: "Leonardo",
+ school: "idk",
+ idLink: "https://t.me/joinchat/lcaKVtappk83NzU0",
+ },
+ {
+ name: "GRUPPO 3 ENG",
+ year: "2020",
+ id: "1",
+ degree: "LT",
+ type: "C",
+ platform: "TG",
+ language: "ENG",
+ office: "Leonardo",
+ school: "idk",
+ idLink: "https://chat.whatsapp.com/HDZd7mCzDg80dS4fCcSszy",
+ },
+ {
+ name: "GRUPPO 4 ENG",
+ year: "2020",
+ id: "1",
+ degree: "LT",
+ type: "C",
+ platform: "TG",
+ language: "ENG",
+ office: "Leonardo",
+ school: "idk",
+ idLink: "https://t.me/joinchat/YEBlpQ_fzoZmYzI0",
+ },
+ {
+ name: "GRUPPO 5 ITA",
+ year: "2020",
+ id: "1",
+ degree: "LT",
+ type: "C",
+ platform: "TG",
+ language: "ITA",
+ office: "Leonardo",
+ school: "idk",
+ idLink: "https://www.facebook.com/groups/170744940120942",
+ },
+ {
+ name: "GRUPPO 6 ENG",
+ year: "2020",
+ id: "1",
+ degree: "LT",
+ type: "C",
+ platform: "TG",
+ language: "ENG",
+ office: "Leonardo",
+ school: "idk",
+ idLink: "https://t.me/joinchat/_9vETcjqnX5iNzNk",
+ },
+ ],
}
diff --git a/src/components/Divider.tsx b/src/components/Divider.tsx
index e2512791..3e7c2454 100644
--- a/src/components/Divider.tsx
+++ b/src/components/Divider.tsx
@@ -1,19 +1,25 @@
import React, { FC } from "react"
-import { View } from "react-native"
+import { View, ViewStyle } from "react-native"
export interface DividerProps {
color?: string
height?: number
+ width?: number
+ style?: ViewStyle
}
export const Divider: FC = props => {
return (
)
}
diff --git a/src/components/Groups/AnimatedLine.tsx b/src/components/Groups/AnimatedLine.tsx
new file mode 100644
index 00000000..a39973b8
--- /dev/null
+++ b/src/components/Groups/AnimatedLine.tsx
@@ -0,0 +1,51 @@
+import React, { FC } from "react"
+import { Animated, Easing, ViewStyle } from "react-native"
+import { usePalette } from "utils/colors"
+
+export interface AnimatedLineProps {
+ mounted: boolean
+ color?: string
+ height?: number
+ width?: number
+ style?: ViewStyle
+}
+
+export const AnimatedLine: FC = props => {
+ const { isLight } = usePalette()
+
+ const { current: widthAnim } = React.useRef(
+ new Animated.Value(1)
+ )
+
+ React.useEffect(() => {
+ if (props.mounted) {
+ Animated.timing(widthAnim, {
+ toValue: 250,
+ duration: 300,
+ easing: Easing.ease,
+ useNativeDriver: false,
+ }).start()
+ } else {
+ Animated.timing(widthAnim, {
+ toValue: 0,
+ duration: 300,
+ easing: Easing.ease,
+ useNativeDriver: false,
+ }).start()
+ }
+ }, [props.mounted])
+
+ return (
+
+ )
+}
diff --git a/src/components/Groups/ExpandablePoliSearchBar.tsx b/src/components/Groups/ExpandablePoliSearchBar.tsx
new file mode 100644
index 00000000..85b5a6b2
--- /dev/null
+++ b/src/components/Groups/ExpandablePoliSearchBar.tsx
@@ -0,0 +1,119 @@
+import { MockedGroup } from "api/groups"
+import { PoliSearchBar } from "components/Home"
+import React, { FC, useState } from "react"
+import { ScrollView, View } from "react-native"
+import { usePalette } from "utils/colors"
+import { AnimatedLine } from "./AnimatedLine"
+import { GroupTile } from "./GroupTile"
+import { OutlinedButton } from "./OutlinedButton"
+
+export interface ExpandablePoliSearchBarProps {
+ setSearch: (val: string) => void
+ isSearching: boolean
+ setIsSearching: (val: boolean) => void
+ groups?: MockedGroup[]
+ language: ValidLanguageType
+ setLanguage: (val: ValidLanguageType) => void
+}
+
+export type ValidLanguageType = "ITA" | "ENG" | undefined
+
+export const ExpandablePoliSearchBar: FC<
+ ExpandablePoliSearchBarProps
+> = props => {
+ const { isLight } = usePalette()
+
+ return (
+
+
+ {
+ props.setSearch(val)
+ if (val !== "") {
+ props.setIsSearching(true)
+ } else if (props.isSearching === true) {
+ props.setIsSearching(false)
+ props.setLanguage(undefined)
+ } else {
+ props.setLanguage(undefined)
+ }
+ }}
+ style={{ marginTop: 0, marginBottom: 0 }}
+ />
+
+
+ {props.groups && props.isSearching && (
+
+
+ {
+ if (props.language === "ITA") {
+ props.setLanguage(undefined)
+ } else {
+ props.setLanguage("ITA")
+ }
+ }}
+ />
+ {
+ if (props.language === "ENG") {
+ props.setLanguage(undefined)
+ } else {
+ props.setLanguage("ENG")
+ }
+ }}
+ />
+
+
+ {props.groups.map((group, idx) => {
+ return (
+
+ )
+ })}
+
+
+ )}
+
+
+ )
+}
diff --git a/src/components/Groups/Filters.tsx b/src/components/Groups/Filters.tsx
index 13a774dc..7c13a32d 100644
--- a/src/components/Groups/Filters.tsx
+++ b/src/components/Groups/Filters.tsx
@@ -1,13 +1,19 @@
-import React, { FC, useEffect, useState } from "react"
+import React, { FC, useState } from "react"
import { View } from "react-native"
import { OutlinedButton } from "./OutlinedButton"
import { StyleSheet } from "react-native"
import { ModalSelection, SelectTile } from "components/Settings"
-import { api } from "api"
-/* export interface FiltersProps {
- prova?: string
-} */
+export interface FiltersProps {
+ year: string
+ setYear: (value: string) => void
+ course: string
+ setCourse: (value: string) => void
+ type: string
+ setType: (value: string) => void
+ platform: string
+ setPlatform: (value: string) => void
+}
//This is a mess for now
const yearsList = ["2021/2022", "2020/2021", "2019/2020", "2018/2019"]
@@ -28,7 +34,7 @@ const getNameFromMode = (mode: string) => {
}
export type ValidModalType = "year" | "course" | "type" | "platform"
const all = "Tutti"
-export const Filters: FC = () => {
+export const Filters: FC = props => {
//show or hide modal
const [isModalShowing, setIsModalShowing] = useState(false)
//type of modal: year - type - course - platform
@@ -38,47 +44,26 @@ export const Filters: FC = () => {
//currently selected item inside modal
const [selectedItem, setSelectedItem] = useState(all)
- const [year, setYear] = useState(all)
-
- const [course, setCourse] = useState(all)
-
- const [type, setType] = useState(all)
-
- const [platform, setPlatform] = useState(all)
-
//update state when user taps "OK" in modal
const updateSelectedFilter = () => {
if (modalMode === "year") {
- setYear(selectedItem)
+ props.setYear(selectedItem)
} else if (modalMode === "course") {
- setCourse(selectedItem)
+ props.setCourse(selectedItem)
} else if (modalMode === "platform") {
- setPlatform(selectedItem)
+ props.setPlatform(selectedItem)
} else {
- setType(selectedItem)
+ props.setType(selectedItem)
}
}
//reset state on "reset"
const reset = () => {
- setYear(all)
- setCourse(all)
- setType(all)
- setPlatform(all)
- }
-
- const searchGroups = async () => {
- try {
- //broken
- const response = await api.groups.get({ name: "Informatica" })
- console.log(response)
- } catch (error) {
- console.log(error)
- }
+ props.setYear(all)
+ props.setCourse(all)
+ props.setType(all)
+ props.setPlatform(all)
}
- useEffect(() => {
- void searchGroups()
- }, [])
return (
{
styles.buttonRightMargin,
styles.buttonBottomMargin,
]}
- isSelected={year !== all ? true : false}
+ isSelected={props.year !== all ? true : false}
onPress={() => {
setModalMode("year")
setModalItems(yearsList)
- setSelectedItem(year)
+ setSelectedItem(props.year)
setIsModalShowing(true)
}}
/>
@@ -108,33 +93,33 @@ export const Filters: FC = () => {
styles.buttonRightMargin,
styles.buttonBottomMargin,
]}
- isSelected={course !== all ? true : false}
+ isSelected={props.course !== all ? true : false}
onPress={() => {
setModalMode("course")
setModalItems(coursesList)
- setSelectedItem(course)
+ setSelectedItem(props.course)
setIsModalShowing(true)
}}
/>
{
setModalMode("type")
setModalItems(typesList)
- setSelectedItem(type)
+ setSelectedItem(props.type)
setIsModalShowing(true)
}}
/>
{
setModalMode("platform")
setModalItems(platformsList)
- setSelectedItem(platform)
+ setSelectedItem(props.platform)
setIsModalShowing(true)
}}
/>
diff --git a/src/components/Groups/GroupTile.tsx b/src/components/Groups/GroupTile.tsx
new file mode 100644
index 00000000..82b37ae2
--- /dev/null
+++ b/src/components/Groups/GroupTile.tsx
@@ -0,0 +1,37 @@
+import { BodyText } from "components/Text"
+import React, { FC } from "react"
+import { Linking, Pressable } from "react-native"
+import { usePalette } from "utils/colors"
+
+export interface GroupTileProps {
+ name?: string
+ link?: string
+}
+
+export const GroupTile: FC = props => {
+ const { isLight } = usePalette()
+
+ const handlePress = async () => {
+ if (!props.link) {
+ return
+ }
+ // Checking if the link is supported for links with custom URL scheme.
+ const supported = await Linking.canOpenURL(props.link)
+
+ if (supported) {
+ // Opening the link with some app, if the URL scheme is "http" the web link should be opened
+ // by some browser in the mobile
+ await Linking.openURL(props.link)
+ }
+ }
+
+ return (
+
+
+ {props.name}
+
+
+ )
+}
diff --git a/src/components/Groups/GroupsFiltered.tsx b/src/components/Groups/GroupsFiltered.tsx
deleted file mode 100644
index af099f6c..00000000
--- a/src/components/Groups/GroupsFiltered.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Text } from "components/Text"
-import React, { FC } from "react"
-import { View } from "react-native"
-
-export interface GroupsFilteredProps {
- string?: string
-}
-
-export const GroupsFiltered: FC = props => {
- return (
-
- {props.string}
-
- )
-}
diff --git a/src/components/Groups/OutlinedButton.tsx b/src/components/Groups/OutlinedButton.tsx
index 1d9243c5..df89c456 100644
--- a/src/components/Groups/OutlinedButton.tsx
+++ b/src/components/Groups/OutlinedButton.tsx
@@ -1,4 +1,4 @@
-import { Text } from "components/Text"
+import { BodyText } from "components/Text"
import React, { FC } from "react"
import { Pressable, StyleProp, ViewStyle } from "react-native"
import { usePalette } from "utils/colors"
@@ -38,7 +38,7 @@ export const OutlinedButton: FC = props => {
]}
onPress={props.onPress}
>
- = props => {
}}
>
{props.text}
-
+
)
}
diff --git a/src/components/Text/BodyText.tsx b/src/components/Text/BodyText.tsx
index b9790831..402248c6 100644
--- a/src/components/Text/BodyText.tsx
+++ b/src/components/Text/BodyText.tsx
@@ -22,7 +22,9 @@ export const BodyText: FC = props => {
fontFamily:
fontWeight === "900"
? "Roboto_900Black"
- : fontWeight === "bold" || fontWeight === "700"
+ : fontWeight === "bold" ||
+ fontWeight === "700" ||
+ fontWeight === "600"
? "Roboto_700Bold"
: fontWeight === "300"
? "Roboto_300Light"
diff --git a/src/pages/Groups.tsx b/src/pages/Groups.tsx
index 5c19a055..8f9c73e3 100644
--- a/src/pages/Groups.tsx
+++ b/src/pages/Groups.tsx
@@ -1,36 +1,110 @@
-import React, { useState } from "react"
+import React, { useEffect, useState } from "react"
import { MainStackScreen } from "navigation/NavigationTypes"
import { View } from "react-native"
import { ContentWrapperScroll } from "components/ContentWrapperScroll"
import { Title } from "components/Text"
-import { PoliSearchBar } from "components/Home"
import { Filters } from "components/Groups/Filters"
-import { GroupsFiltered } from "components/Groups/GroupsFiltered"
+import {
+ ExpandablePoliSearchBar,
+ ValidLanguageType,
+} from "components/Groups/ExpandablePoliSearchBar"
+import { api } from "api"
+import { MockedGroup } from "api/groups"
+import { useMounted } from "utils/useMounted"
+import { filterByLanguage } from "utils/groups"
+
+const all = "Tutti"
export const Groups: MainStackScreen<"Groups"> = () => {
const [search, setSearch] = useState("")
const [isSearching, setIsSearching] = useState(false)
+
+ const [year, setYear] = useState(all)
+
+ const [course, setCourse] = useState(all)
+
+ const [type, setType] = useState(all)
+
+ const [platform, setPlatform] = useState(all)
+
+ const [groups, setGroups] = useState(undefined)
+
+ const [language, setLanguage] = useState()
+
+ //when user selects "ITA" or "ENG"
+ const [filteredGroups, setFilteredGroups] = useState<
+ MockedGroup[] | undefined
+ >(undefined)
+
+ //tracking first render
+ const isMounted = useMounted()
+
+ //api call every time user enter a new character
+ const searchGroups = async () => {
+ if (isMounted) {
+ try {
+ //mocked
+ const response = await api.groups.getMocked()
+ console.log(response)
+ setGroups(response)
+ } catch (error) {
+ console.log(error)
+ }
+ }
+ }
+ useEffect(() => {
+ void searchGroups()
+ }, [search])
+
+ //filter items every time selected language changes
+ useEffect(() => {
+ if (isMounted && groups) {
+ if (language) {
+ const newFilteredGroups = filterByLanguage(groups, language)
+ setFilteredGroups(newFilteredGroups)
+ } else {
+ resetFilterLanguage()
+ }
+ }
+ }, [language])
+
+ //load groups to filtered groups every time a new response from api arrives. (language filters ignored)
+ useEffect(() => {
+ if (isMounted) {
+ setFilteredGroups(groups)
+ }
+ }, [groups])
+
+ //helper function to reset language filters
+ const resetFilterLanguage = () => {
+ setFilteredGroups(groups)
+ }
return (
Gruppi Corsi
- {
- setSearch(value)
- if (value !== "") {
- setIsSearching(true)
- } else if (isSearching === true) {
- setIsSearching(false)
- }
- }}
- style={{ marginTop: 46, marginBottom: 24 }}
+ setIsSearching(val)}
+ setSearch={val => setSearch(val)}
+ groups={filteredGroups}
+ language={language}
+ setLanguage={val => setLanguage(val)}
/>
- {!isSearching ? (
-
- ) : (
-
+
+ {!isSearching && (
+ setYear(val)}
+ course={course}
+ setCourse={val => setCourse(val)}
+ type={type}
+ setType={val => setType(val)}
+ platform={platform}
+ setPlatform={val => setPlatform(val)}
+ />
)}
diff --git a/src/utils/groups.ts b/src/utils/groups.ts
new file mode 100644
index 00000000..fc501320
--- /dev/null
+++ b/src/utils/groups.ts
@@ -0,0 +1,11 @@
+import { MockedGroup } from "api/groups"
+
+/**
+ * return groups filtered by language
+ * see {@link Groups} Page
+ */
+export function filterByLanguage(groups: MockedGroup[], language: string) {
+ return groups.filter(group => {
+ return group.language === language
+ })
+}
diff --git a/src/utils/useMounted.ts b/src/utils/useMounted.ts
new file mode 100644
index 00000000..3c36dc4b
--- /dev/null
+++ b/src/utils/useMounted.ts
@@ -0,0 +1,43 @@
+import { useEffect, useState } from "react"
+
+/**
+ * useful hook to keep track of first render in multiple useEffects
+ *
+ * from https://stackoverflow.com/questions/57240169/skip-first-useeffect-when-there-are-multiple-useeffects
+ *
+ * @example
+ * ```ts
+ * const [valueFirst, setValueFirst] = useState(0)
+ * const [valueSecond, setValueSecond] = useState(0)
+ *
+ * const isMounted = useMounted()
+ *
+ * //1st effect which should run whenever valueFirst change except
+ * //first time
+ * React.useEffect(() => {
+ * if (isMounted) {
+ * console.log("valueFirst ran")
+ * }
+ *
+ * }, [valueFirst])
+ *
+ *
+ * //2nd effect which should run whenever valueFirst change except
+ * //first time
+ * React.useEffect(() => {
+ * if (isMounted) {
+ * console.log("valueSecond ran")
+ * }
+ *
+ * }, [valueSecond])
+ *
+ * ```
+ */
+export function useMounted() {
+ const [isMounted, setIsMounted] = useState(false)
+
+ useEffect(() => {
+ setIsMounted(true)
+ }, [])
+ return isMounted
+}
From d95e989f40d72722fa9d79728bf7bb291c1a621d Mon Sep 17 00:00:00 2001
From: DiegoZaff
Date: Sun, 15 Jan 2023 01:09:04 +0100
Subject: [PATCH 04/17] Adding proper filter values reordering code
---
src/api/groups.ts | 7 +--
src/components/Groups/AnimatedLine.tsx | 3 ++
.../Groups/ExpandablePoliSearchBar.tsx | 2 +-
src/components/Groups/Filters.tsx | 53 +++++++++++--------
src/components/Groups/GroupTile.tsx | 3 +-
src/pages/Groups.tsx | 6 ++-
src/utils/groups.ts | 14 +++++
7 files changed, 59 insertions(+), 29 deletions(-)
diff --git a/src/api/groups.ts b/src/api/groups.ts
index 62d126d3..67d3c75d 100644
--- a/src/api/groups.ts
+++ b/src/api/groups.ts
@@ -1,4 +1,4 @@
-import { HttpClient, RequestOptions } from "./HttpClient"
+/* import { HttpClient, RequestOptions } from "./HttpClient" */
/* eslint-disable @typescript-eslint/naming-convention */
@@ -25,7 +25,7 @@ export interface MockedGroup {
idLink?: string
}
-const client = HttpClient.getInstance()
+/* const client = HttpClient.getInstance() */
/**
* Collection of endpoints related to Groups.
@@ -35,6 +35,7 @@ export const groups = {
* Retrieves groups from PoliNetwork server.
* Check {@link GroupOptions} for additional parameters.
*/
+ /*
// ! temporarily broken
async get(groupsOptions?: GroupOptions, options?: RequestOptions) {
const response = await client.poliNetworkInstance.get(
@@ -53,7 +54,7 @@ export const groups = {
}
)
return response.data.results
- },
+ }, */
getMocked() {
return mockedGroups.groups
},
diff --git a/src/components/Groups/AnimatedLine.tsx b/src/components/Groups/AnimatedLine.tsx
index a39973b8..98045b11 100644
--- a/src/components/Groups/AnimatedLine.tsx
+++ b/src/components/Groups/AnimatedLine.tsx
@@ -3,6 +3,9 @@ import { Animated, Easing, ViewStyle } from "react-native"
import { usePalette } from "utils/colors"
export interface AnimatedLineProps {
+ /**
+ * animate when this value changes
+ */
mounted: boolean
color?: string
height?: number
diff --git a/src/components/Groups/ExpandablePoliSearchBar.tsx b/src/components/Groups/ExpandablePoliSearchBar.tsx
index 85b5a6b2..095181c1 100644
--- a/src/components/Groups/ExpandablePoliSearchBar.tsx
+++ b/src/components/Groups/ExpandablePoliSearchBar.tsx
@@ -1,6 +1,6 @@
import { MockedGroup } from "api/groups"
import { PoliSearchBar } from "components/Home"
-import React, { FC, useState } from "react"
+import React, { FC } from "react"
import { ScrollView, View } from "react-native"
import { usePalette } from "utils/colors"
import { AnimatedLine } from "./AnimatedLine"
diff --git a/src/components/Groups/Filters.tsx b/src/components/Groups/Filters.tsx
index 7c13a32d..cfd3c682 100644
--- a/src/components/Groups/Filters.tsx
+++ b/src/components/Groups/Filters.tsx
@@ -3,6 +3,7 @@ import { View } from "react-native"
import { OutlinedButton } from "./OutlinedButton"
import { StyleSheet } from "react-native"
import { ModalSelection, SelectTile } from "components/Settings"
+import { getNameFromMode, ValidModalType } from "utils/groups"
export interface FiltersProps {
year: string
@@ -15,34 +16,42 @@ export interface FiltersProps {
setPlatform: (value: string) => void
}
-//This is a mess for now
-const yearsList = ["2021/2022", "2020/2021", "2019/2020", "2018/2019"]
-const coursesList = ["Triennale", "Magistrale", "Ciclo unico"]
-const typesList = ["A", "B", "C", "bho"]
-const platformsList = ["Telgram", "Watsapp", "Bho"]
+interface ModalItemList {
+ itemsToShow: string[]
+ itemsToSave: string[]
+}
-const getNameFromMode = (mode: string) => {
- if (mode === "year") {
- return "Anno"
- } else if (mode === "course") {
- return "Corso"
- } else if (mode === "platform") {
- return "Piattaforma"
- } else {
- return "Tipo"
- }
+const yearsList: ModalItemList = {
+ itemsToShow: ["2021/2022", "2020/2021", "2019/2020", "2018/2019"],
+ itemsToSave: ["2021", "2020", "2019", "2018"],
+}
+const coursesList: ModalItemList = {
+ itemsToShow: ["Triennale", "Magistrale", "Ciclo unico"],
+ itemsToSave: ["LT", "LM", "LU"],
+}
+
+const typesList: ModalItemList = {
+ itemsToShow: ["Scuola", "Corso", "Extra"],
+ itemsToSave: ["S", "C", "E"],
}
-export type ValidModalType = "year" | "course" | "type" | "platform"
+
+const platformsList: ModalItemList = {
+ itemsToShow: ["Whatsapp", "Facebook", "Telegram"],
+ itemsToSave: ["WA", "FB", "TG"],
+}
+
+//to avoid writing mistakes
const all = "Tutti"
+
export const Filters: FC = props => {
//show or hide modal
const [isModalShowing, setIsModalShowing] = useState(false)
//type of modal: year - type - course - platform
const [modalMode, setModalMode] = useState("year")
//items to show inside modal
- const [modalItems, setModalItems] = useState(yearsList)
+ const [modalItems, setModalItems] = useState(yearsList)
//currently selected item inside modal
- const [selectedItem, setSelectedItem] = useState(all)
+ const [selectedItem, setSelectedItem] = useState(all)
//update state when user taps "OK" in modal
const updateSelectedFilter = () => {
@@ -149,14 +158,16 @@ export const Filters: FC = props => {
setSelectedItem(all)
}}
/>
- {modalItems?.map((itemName, index) => {
+ {modalItems?.itemsToShow.map((itemName, index) => {
return (
{
- setSelectedItem(modalItems[index])
+ setSelectedItem(modalItems.itemsToSave[index])
}}
/>
)
diff --git a/src/components/Groups/GroupTile.tsx b/src/components/Groups/GroupTile.tsx
index 82b37ae2..3b2aef7f 100644
--- a/src/components/Groups/GroupTile.tsx
+++ b/src/components/Groups/GroupTile.tsx
@@ -19,8 +19,7 @@ export const GroupTile: FC = props => {
const supported = await Linking.canOpenURL(props.link)
if (supported) {
- // Opening the link with some app, if the URL scheme is "http" the web link should be opened
- // by some browser in the mobile
+ // Opening the link with some app
await Linking.openURL(props.link)
}
}
diff --git a/src/pages/Groups.tsx b/src/pages/Groups.tsx
index 8f9c73e3..6f770d83 100644
--- a/src/pages/Groups.tsx
+++ b/src/pages/Groups.tsx
@@ -20,6 +20,8 @@ export const Groups: MainStackScreen<"Groups"> = () => {
const [isSearching, setIsSearching] = useState(false)
+ // ? I'd like to typecheck year, course, etc... but I need dynamic type checking
+ // ? and it's very ugly, and need to use type guards or some library, so maybe it is not worth it?
const [year, setYear] = useState(all)
const [course, setCourse] = useState(all)
@@ -41,11 +43,11 @@ export const Groups: MainStackScreen<"Groups"> = () => {
const isMounted = useMounted()
//api call every time user enter a new character
- const searchGroups = async () => {
+ const searchGroups = () => {
if (isMounted) {
try {
//mocked
- const response = await api.groups.getMocked()
+ const response = api.groups.getMocked()
console.log(response)
setGroups(response)
} catch (error) {
diff --git a/src/utils/groups.ts b/src/utils/groups.ts
index fc501320..9d86cacf 100644
--- a/src/utils/groups.ts
+++ b/src/utils/groups.ts
@@ -9,3 +9,17 @@ export function filterByLanguage(groups: MockedGroup[], language: string) {
return group.language === language
})
}
+
+export type ValidModalType = "year" | "course" | "type" | "platform"
+
+export const getNameFromMode = (mode: ValidModalType) => {
+ if (mode === "year") {
+ return "Anno"
+ } else if (mode === "course") {
+ return "Corso"
+ } else if (mode === "platform") {
+ return "Piattaforma"
+ } else {
+ return "Tipo"
+ }
+}
From 3080d85af4721526541686cf59936b28e071fcc6 Mon Sep 17 00:00:00 2001
From: DiegoZaff
Date: Fri, 20 Jan 2023 02:04:17 +0100
Subject: [PATCH 05/17] groups api draft
---
src/api/groups.ts | 58 ++++++++-------
.../Groups/ExpandablePoliSearchBar.tsx | 14 +++-
src/components/Groups/Filters.tsx | 2 +-
src/pages/Groups.tsx | 74 +++++++++++++++----
src/utils/groups.ts | 21 +++++-
5 files changed, 120 insertions(+), 49 deletions(-)
diff --git a/src/api/groups.ts b/src/api/groups.ts
index 67d3c75d..c8cd49a2 100644
--- a/src/api/groups.ts
+++ b/src/api/groups.ts
@@ -1,4 +1,4 @@
-/* import { HttpClient, RequestOptions } from "./HttpClient" */
+import { HttpClient, RequestOptions } from "./HttpClient"
/* eslint-disable @typescript-eslint/naming-convention */
@@ -12,20 +12,23 @@ export interface GroupOptions {
office?: string
}
-export interface MockedGroup {
- name?: string
- year?: string
+export interface Group {
+ class: string
+ office: string
id?: string
degree?: string
- type?: string
- platform?: string
- language?: string
- office?: string
school?: string
- idLink?: string
+ id_link: string
+ language: string
+ type_?: string
+ year?: string
+ platform: string
+ permanent_id?: number
+ last_updated?: string
+ link_is_working?: string
}
-/* const client = HttpClient.getInstance() */
+const client = HttpClient.getInstance()
/**
* Collection of endpoints related to Groups.
@@ -35,26 +38,25 @@ export const groups = {
* Retrieves groups from PoliNetwork server.
* Check {@link GroupOptions} for additional parameters.
*/
- /*
+
// ! temporarily broken
async get(groupsOptions?: GroupOptions, options?: RequestOptions) {
- const response = await client.poliNetworkInstance.get(
- "/v1/groups/search",
- {
- ...options,
- params: {
- name: groupsOptions?.name,
- year: groupsOptions?.year,
- degree: groupsOptions?.degree,
- type: groupsOptions?.type,
- platform: groupsOptions?.platform,
- language: groupsOptions?.language,
- office: groupsOptions?.office,
- },
- }
- )
- return response.data.results
- }, */
+ const response = await client.poliNetworkInstance.get<{
+ groups: Group[]
+ }>("/v1/groups", {
+ ...options,
+ params: {
+ name: groupsOptions?.name,
+ year: groupsOptions?.year,
+ degree: groupsOptions?.degree,
+ type: groupsOptions?.type,
+ platform: groupsOptions?.platform,
+ language: groupsOptions?.language,
+ office: groupsOptions?.office,
+ },
+ })
+ return response.data.groups
+ },
getMocked() {
return mockedGroups.groups
},
diff --git a/src/components/Groups/ExpandablePoliSearchBar.tsx b/src/components/Groups/ExpandablePoliSearchBar.tsx
index 095181c1..de813da7 100644
--- a/src/components/Groups/ExpandablePoliSearchBar.tsx
+++ b/src/components/Groups/ExpandablePoliSearchBar.tsx
@@ -1,8 +1,9 @@
-import { MockedGroup } from "api/groups"
+import { Group } from "api/groups"
import { PoliSearchBar } from "components/Home"
import React, { FC } from "react"
import { ScrollView, View } from "react-native"
import { usePalette } from "utils/colors"
+import { createGroupLink } from "utils/groups"
import { AnimatedLine } from "./AnimatedLine"
import { GroupTile } from "./GroupTile"
import { OutlinedButton } from "./OutlinedButton"
@@ -11,7 +12,7 @@ export interface ExpandablePoliSearchBarProps {
setSearch: (val: string) => void
isSearching: boolean
setIsSearching: (val: boolean) => void
- groups?: MockedGroup[]
+ groups?: Group[]
language: ValidLanguageType
setLanguage: (val: ValidLanguageType) => void
}
@@ -40,6 +41,7 @@ export const ExpandablePoliSearchBar: FC<
marginTop: 46,
marginBottom: 24,
height: props.isSearching ? 268 : undefined,
+ width: 285,
borderBottomRightRadius: 8,
borderBottomLeftRadius: 8,
borderTopRightRadius: 28,
@@ -104,8 +106,12 @@ export const ExpandablePoliSearchBar: FC<
{props.groups.map((group, idx) => {
return (
)
diff --git a/src/components/Groups/Filters.tsx b/src/components/Groups/Filters.tsx
index cfd3c682..e48f46cd 100644
--- a/src/components/Groups/Filters.tsx
+++ b/src/components/Groups/Filters.tsx
@@ -23,7 +23,7 @@ interface ModalItemList {
const yearsList: ModalItemList = {
itemsToShow: ["2021/2022", "2020/2021", "2019/2020", "2018/2019"],
- itemsToSave: ["2021", "2020", "2019", "2018"],
+ itemsToSave: ["2021/2022", "2020/2021", "2019/2020", "2018/2019"],
}
const coursesList: ModalItemList = {
itemsToShow: ["Triennale", "Magistrale", "Ciclo unico"],
diff --git a/src/pages/Groups.tsx b/src/pages/Groups.tsx
index 6f770d83..57594d1f 100644
--- a/src/pages/Groups.tsx
+++ b/src/pages/Groups.tsx
@@ -8,14 +8,21 @@ import {
ExpandablePoliSearchBar,
ValidLanguageType,
} from "components/Groups/ExpandablePoliSearchBar"
-import { api } from "api"
-import { MockedGroup } from "api/groups"
+import { api, RetryType } from "api"
+import { Group } from "api/groups"
import { useMounted } from "utils/useMounted"
-import { filterByLanguage } from "utils/groups"
+import { filterByLanguage, msPassedBetween } from "utils/groups"
+import { wait } from "utils/functions"
const all = "Tutti"
export const Groups: MainStackScreen<"Groups"> = () => {
+ const [lastSearchTime, setLastSearchTime] = useState(
+ undefined
+ )
+
+ const [needSearching, setNeedSearching] = useState(0)
+
const [search, setSearch] = useState("")
const [isSearching, setIsSearching] = useState(false)
@@ -30,26 +37,50 @@ export const Groups: MainStackScreen<"Groups"> = () => {
const [platform, setPlatform] = useState(all)
- const [groups, setGroups] = useState(undefined)
+ const [groups, setGroups] = useState(undefined)
const [language, setLanguage] = useState()
//when user selects "ITA" or "ENG"
- const [filteredGroups, setFilteredGroups] = useState<
- MockedGroup[] | undefined
- >(undefined)
+ const [filteredGroups, setFilteredGroups] = useState(
+ undefined
+ )
//tracking first render
const isMounted = useMounted()
- //api call every time user enter a new character
- const searchGroups = () => {
+ //serch if len > 5 char and last search was more than 3000ms ago, if not search in (3000ms - time)ms
+ //if the user doesnt search for something else
+ const searchGroups = async () => {
if (isMounted) {
+ if (search.length < 5) {
+ if (groups !== undefined) {
+ setGroups(undefined)
+ }
+ return
+ }
try {
- //mocked
- const response = api.groups.getMocked()
- console.log(response)
- setGroups(response)
+ const now = new Date()
+ const msPassed = msPassedBetween(lastSearchTime, now)
+ if (msPassed === undefined || msPassed >= 3000) {
+ console.log("searching")
+ setLastSearchTime(now)
+ const response = await api.groups.get(
+ {
+ name: search,
+ year: year === all ? undefined : year,
+ degree: course === all ? undefined : course,
+ type: type === all ? undefined : type,
+ platform: platform === all ? undefined : platform,
+ },
+ { maxRetries: 1, retryType: RetryType.RETRY_N_TIMES }
+ )
+ console.log("response arrived")
+ setGroups(response)
+ } else {
+ console.log(msPassed + " passsed, reschedule research")
+ await researchIn(3000 - msPassed)
+ }
} catch (error) {
console.log(error)
}
@@ -57,7 +88,22 @@ export const Groups: MainStackScreen<"Groups"> = () => {
}
useEffect(() => {
void searchGroups()
- }, [search])
+ }, [search, needSearching])
+
+ /**
+ * reserach in (3000 - time) ms if search string doesnt change in this span of time.
+ * Fired every time a search is canceled because it didnt pass enough time from last search.
+ **/
+ const researchIn = async (time: number) => {
+ const prevSearch = search
+ const prevNeedSearching = needSearching
+ await wait(time)
+ // ! not working properly
+ if (prevSearch === search && prevNeedSearching === needSearching) {
+ setNeedSearching(needSearching + 1)
+ console.log("research fired!!")
+ }
+ }
//filter items every time selected language changes
useEffect(() => {
diff --git a/src/utils/groups.ts b/src/utils/groups.ts
index 9d86cacf..b1803021 100644
--- a/src/utils/groups.ts
+++ b/src/utils/groups.ts
@@ -1,10 +1,10 @@
-import { MockedGroup } from "api/groups"
+import { Group } from "api/groups"
/**
* return groups filtered by language
* see {@link Groups} Page
*/
-export function filterByLanguage(groups: MockedGroup[], language: string) {
+export function filterByLanguage(groups: Group[], language: string) {
return groups.filter(group => {
return group.language === language
})
@@ -23,3 +23,20 @@ export const getNameFromMode = (mode: ValidModalType) => {
return "Tipo"
}
}
+
+export function createGroupLink(idLink: string, platform: string) {
+ if (platform === "TG") {
+ return `https://t.me/joinchat/${idLink}`
+ } else if (platform === "WA") {
+ return `https://chat.whatsapp.com/${idLink}`
+ } else {
+ return `https://www.facebook.com/groups/${idLink}`
+ }
+}
+
+export function msPassedBetween(start: Date | undefined, end: Date) {
+ if (start === undefined) {
+ return undefined
+ }
+ return end.getTime() - start.getTime()
+}
From 837500b2835d3057a497fda37dcda2d0874af0f6 Mon Sep 17 00:00:00 2001
From: DiegoZaff
Date: Sat, 21 Jan 2023 02:38:56 +0100
Subject: [PATCH 06/17] fixed search logic
---
src/pages/Groups.tsx | 83 ++++++++++++++++++++++++++++++++------------
1 file changed, 60 insertions(+), 23 deletions(-)
diff --git a/src/pages/Groups.tsx b/src/pages/Groups.tsx
index 57594d1f..3f380228 100644
--- a/src/pages/Groups.tsx
+++ b/src/pages/Groups.tsx
@@ -16,12 +16,23 @@ import { wait } from "utils/functions"
const all = "Tutti"
+const sleepTime = 500 //ms
+
export const Groups: MainStackScreen<"Groups"> = () => {
+ //keep track of latest search request time (successful or not)
const [lastSearchTime, setLastSearchTime] = useState(
undefined
)
+ //for forcing api search request if needeed
+ const [needSearching, setNeedSearching] = useState(false)
+
+ //for triggering api search request side effect
+ //reason : I want to trigger it only if I set needSearching to True, not to False
+ const [rescheduleSearch, setRescheduleSearch] = useState(0)
- const [needSearching, setNeedSearching] = useState(0)
+ //for triggering api request when user doesnt write in a time span of `sleepTime`
+ //reason: I don't want to send api request on every character change.
+ const [prevSearch, setPrevSearch] = useState("")
const [search, setSearch] = useState("")
@@ -49,8 +60,17 @@ export const Groups: MainStackScreen<"Groups"> = () => {
//tracking first render
const isMounted = useMounted()
- //serch if len > 5 char and last search was more than 3000ms ago, if not search in (3000ms - time)ms
- //if the user doesnt search for something else
+ /**
+ * Api search request side effect.
+ *
+ * How this mess works (or should work, actually it seems like it works for now):
+ * 1) gets called every time search text changes or rescheduleSearch is incremented (for forcing search request)
+ * 2) if len(search) < 5 do nothing and delete store groups if any.
+ * 3) if no other searches were done before, send search request to api. Store time of search in `lastSearchTime`.
+ * 4) successive requests are allowed if: last search request time (successful or not) was more than `sleepTime` (500ms) ago
+ * 5) if a request is not allowed, store `lastTimeSearch` anyway, then check `sleepTime` (500ms) later if the user has kept writing.
+ * if he has, then do nothing because side effect will be called again, if he hasn't, then force search request with latest search value.
+ */
const searchGroups = async () => {
if (isMounted) {
if (search.length < 5) {
@@ -62,12 +82,16 @@ export const Groups: MainStackScreen<"Groups"> = () => {
try {
const now = new Date()
const msPassed = msPassedBetween(lastSearchTime, now)
- if (msPassed === undefined || msPassed >= 3000) {
- console.log("searching")
+ if (
+ msPassed === undefined ||
+ msPassed >= sleepTime ||
+ needSearching === true
+ ) {
+ //update last time search
setLastSearchTime(now)
const response = await api.groups.get(
{
- name: search,
+ name: search.trimEnd(),
year: year === all ? undefined : year,
degree: course === all ? undefined : course,
type: type === all ? undefined : type,
@@ -75,11 +99,17 @@ export const Groups: MainStackScreen<"Groups"> = () => {
},
{ maxRetries: 1, retryType: RetryType.RETRY_N_TIMES }
)
- console.log("response arrived")
setGroups(response)
+ //reset need searching for next render
+ setNeedSearching(false)
} else {
- console.log(msPassed + " passsed, reschedule research")
- await researchIn(3000 - msPassed)
+ // ? over optimization maybe: this prevents api request if user keeps writing fast.
+ // ? maybe I should request every 1 seconds even if user is still writing? debatable
+ setLastSearchTime(now)
+ await wait(sleepTime)
+ //in the next render you have the previous and current search text, and can compare changes
+ //this triggers Reschedule Search Side Effect
+ setPrevSearch(search)
}
} catch (error) {
console.log(error)
@@ -88,22 +118,25 @@ export const Groups: MainStackScreen<"Groups"> = () => {
}
useEffect(() => {
void searchGroups()
- }, [search, needSearching])
+ }, [search, rescheduleSearch])
/**
- * reserach in (3000 - time) ms if search string doesnt change in this span of time.
- * Fired every time a search is canceled because it didnt pass enough time from last search.
- **/
- const researchIn = async (time: number) => {
- const prevSearch = search
- const prevNeedSearching = needSearching
- await wait(time)
- // ! not working properly
- if (prevSearch === search && prevNeedSearching === needSearching) {
- setNeedSearching(needSearching + 1)
- console.log("research fired!!")
+ * Reschedule Search Side Effect
+ *
+ * evaluate if user stopped writing characters in a time span of `sleepTime` seconds
+ */
+ useEffect(() => {
+ if (isMounted) {
+ if (prevSearch !== search) {
+ //Do nothing
+ return
+ } else {
+ //force search in next frame
+ setRescheduleSearch(rescheduleSearch + 1)
+ setNeedSearching(true)
+ }
}
- }
+ }, [prevSearch])
//filter items every time selected language changes
useEffect(() => {
@@ -120,7 +153,11 @@ export const Groups: MainStackScreen<"Groups"> = () => {
//load groups to filtered groups every time a new response from api arrives. (language filters ignored)
useEffect(() => {
if (isMounted) {
- setFilteredGroups(groups)
+ let newGroups = groups
+ if (language !== undefined && newGroups) {
+ newGroups = filterByLanguage(newGroups, language)
+ }
+ setFilteredGroups(newGroups)
}
}, [groups])
From cff329620c323e164610cff2f1c45df24cd1485b Mon Sep 17 00:00:00 2001
From: DiegoZaff
Date: Sun, 22 Jan 2023 02:35:19 +0100
Subject: [PATCH 07/17] Reordering by Year
---
src/api/groups.ts | 2 +-
.../Groups/ExpandablePoliSearchBar.tsx | 4 +-
src/pages/Groups.tsx | 20 ++++++---
src/utils/groups.ts | 44 ++++++++++++++++++-
4 files changed, 61 insertions(+), 9 deletions(-)
diff --git a/src/api/groups.ts b/src/api/groups.ts
index c8cd49a2..f4c08fb4 100644
--- a/src/api/groups.ts
+++ b/src/api/groups.ts
@@ -21,7 +21,7 @@ export interface Group {
id_link: string
language: string
type_?: string
- year?: string
+ year: string | null
platform: string
permanent_id?: number
last_updated?: string
diff --git a/src/components/Groups/ExpandablePoliSearchBar.tsx b/src/components/Groups/ExpandablePoliSearchBar.tsx
index de813da7..f92018c5 100644
--- a/src/components/Groups/ExpandablePoliSearchBar.tsx
+++ b/src/components/Groups/ExpandablePoliSearchBar.tsx
@@ -106,7 +106,9 @@ export const ExpandablePoliSearchBar: FC<
{props.groups.map((group, idx) => {
return (
= () => {
//for forcing api search request if needeed
const [needSearching, setNeedSearching] = useState(false)
- //for triggering api search request side effect
+ //for triggering api search request side effect (used in conjunction with `needSearching`)
//reason : I want to trigger it only if I set needSearching to True, not to False
const [rescheduleSearch, setRescheduleSearch] = useState(0)
@@ -52,7 +56,7 @@ export const Groups: MainStackScreen<"Groups"> = () => {
const [language, setLanguage] = useState()
- //when user selects "ITA" or "ENG"
+ //actually displayed groups (filtered by language)
const [filteredGroups, setFilteredGroups] = useState(
undefined
)
@@ -63,11 +67,11 @@ export const Groups: MainStackScreen<"Groups"> = () => {
/**
* Api search request side effect.
*
- * How this mess works (or should work, actually it seems like it works for now):
+ * How this works (general ideas):
* 1) gets called every time search text changes or rescheduleSearch is incremented (for forcing search request)
- * 2) if len(search) < 5 do nothing and delete store groups if any.
+ * 2) if len(search) < 5 do nothing and delete stored groups if there are any.
* 3) if no other searches were done before, send search request to api. Store time of search in `lastSearchTime`.
- * 4) successive requests are allowed if: last search request time (successful or not) was more than `sleepTime` (500ms) ago
+ * 4) successive requests are allowed if last search request time (successful or not) was more than `sleepTime` (500ms) ago
* 5) if a request is not allowed, store `lastTimeSearch` anyway, then check `sleepTime` (500ms) later if the user has kept writing.
* if he has, then do nothing because side effect will be called again, if he hasn't, then force search request with latest search value.
*/
@@ -154,6 +158,10 @@ export const Groups: MainStackScreen<"Groups"> = () => {
useEffect(() => {
if (isMounted) {
let newGroups = groups
+
+ if (year === all && newGroups) {
+ newGroups = orderByMostRecentYear(newGroups)
+ }
if (language !== undefined && newGroups) {
newGroups = filterByLanguage(newGroups, language)
}
diff --git a/src/utils/groups.ts b/src/utils/groups.ts
index b1803021..c8f8b8e4 100644
--- a/src/utils/groups.ts
+++ b/src/utils/groups.ts
@@ -4,12 +4,54 @@ import { Group } from "api/groups"
* return groups filtered by language
* see {@link Groups} Page
*/
-export function filterByLanguage(groups: Group[], language: string) {
+export function filterByLanguage(groups: Group[], language?: string) {
return groups.filter(group => {
return group.language === language
})
}
+/**
+ * return groups ordered by most recent year using a bubble sort algorithm
+ * see {@link Groups} Page
+ */
+export function orderByMostRecentYear(groups: Group[]) {
+ let hasChanged: boolean
+ try {
+ do {
+ hasChanged = false
+ for (let n = 0; n < groups.length - 1; n++) {
+ if (
+ compareBiYear(groups[n].year, groups[n + 1].year) === true
+ ) {
+ //swap
+ let temp = groups[n]
+ groups[n] = groups[n + 1]
+ groups[n + 1] = temp
+ hasChanged = true
+ }
+ }
+ } while (hasChanged === true)
+ } catch (error) {
+ console.log(error)
+ }
+ return groups
+}
+
+function compareBiYear(first: string | null, second: string | null) {
+ if ((first === null || first === "?/?") && second !== null) {
+ return true
+ } else if (second === null || first === null) {
+ return false
+ }
+ const regex = /^\d{4}\/\d{4}$/
+ if (regex.test(first) && regex.test(second)) {
+ if (parseInt(first.substring(5)) < parseInt(second.substring(5))) {
+ return true
+ }
+ }
+ return false
+}
+
export type ValidModalType = "year" | "course" | "type" | "platform"
export const getNameFromMode = (mode: ValidModalType) => {
From c67eec30b5c5ce8362a228904ebd0f9c8605e2f0 Mon Sep 17 00:00:00 2001
From: DiegoZaff
Date: Sun, 22 Jan 2023 02:35:39 +0100
Subject: [PATCH 08/17] lint error
---
src/utils/groups.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/utils/groups.ts b/src/utils/groups.ts
index c8f8b8e4..98f9d6f6 100644
--- a/src/utils/groups.ts
+++ b/src/utils/groups.ts
@@ -24,7 +24,7 @@ export function orderByMostRecentYear(groups: Group[]) {
compareBiYear(groups[n].year, groups[n + 1].year) === true
) {
//swap
- let temp = groups[n]
+ const temp = groups[n]
groups[n] = groups[n + 1]
groups[n + 1] = temp
hasChanged = true
From dc7718d43af84c0f53460d5666c4617899be3877 Mon Sep 17 00:00:00 2001
From: DiegoZaff
Date: Mon, 23 Jan 2023 01:47:09 +0100
Subject: [PATCH 09/17] New Groups Page Design
---
src/api/groups.ts | 2 +-
src/components/ContentWrapperScroll.tsx | 4 +-
.../Groups/AnimatedPoliSearchBar.tsx | 40 ++++
.../Groups/ExpandablePoliSearchBar.tsx | 127 -----------
src/components/Groups/Filters.tsx | 2 +
src/components/Groups/GroupTile.tsx | 70 ++++--
src/components/Groups/ModalGroup.tsx | 151 +++++++++++++
src/components/Groups/PageWrapper.tsx | 56 +++++
src/components/Home/PoliSearchBar.tsx | 15 ++
src/components/Settings/ButtonCustom.tsx | 6 +-
src/pages/Groups.tsx | 210 +++++++++++++-----
11 files changed, 475 insertions(+), 208 deletions(-)
create mode 100644 src/components/Groups/AnimatedPoliSearchBar.tsx
delete mode 100644 src/components/Groups/ExpandablePoliSearchBar.tsx
create mode 100644 src/components/Groups/ModalGroup.tsx
create mode 100644 src/components/Groups/PageWrapper.tsx
diff --git a/src/api/groups.ts b/src/api/groups.ts
index f4c08fb4..1e1702e9 100644
--- a/src/api/groups.ts
+++ b/src/api/groups.ts
@@ -18,7 +18,7 @@ export interface Group {
id?: string
degree?: string
school?: string
- id_link: string
+ link_id: string
language: string
type_?: string
year: string | null
diff --git a/src/components/ContentWrapperScroll.tsx b/src/components/ContentWrapperScroll.tsx
index 3d298634..b238b91c 100644
--- a/src/components/ContentWrapperScroll.tsx
+++ b/src/components/ContentWrapperScroll.tsx
@@ -10,6 +10,8 @@ import { usePalette } from "utils/colors"
* Default margin Top is 86 (proper margin for Settings Page)
*/
export const ContentWrapperScroll: FC<{
+ children: React.ReactNode
+
title?: string
/**
* Remove the navbar from the bottom of the page.
@@ -20,8 +22,6 @@ export const ContentWrapperScroll: FC<{
*/
navbarOptions?: NavbarProps
marginTop?: number
-
- children: React.ReactNode
}> = props => {
const { background, isLight, primary } = usePalette()
diff --git a/src/components/Groups/AnimatedPoliSearchBar.tsx b/src/components/Groups/AnimatedPoliSearchBar.tsx
new file mode 100644
index 00000000..c3678fa3
--- /dev/null
+++ b/src/components/Groups/AnimatedPoliSearchBar.tsx
@@ -0,0 +1,40 @@
+import { Group } from "api/groups"
+import { PoliSearchBar } from "components/Home"
+import React, { FC } from "react"
+import { ScrollView, View, ViewStyle } from "react-native"
+import { usePalette } from "utils/colors"
+import { createGroupLink } from "utils/groups"
+import { AnimatedLine } from "./AnimatedLine"
+import { GroupTile } from "./GroupTile"
+import { OutlinedButton } from "./OutlinedButton"
+
+export interface AnimatedPoliSearchBarProps {
+ setSearch: (val: string) => void
+ isSearching: boolean
+ setIsSearching: (val: boolean) => void
+ groups?: Group[]
+ style?: ViewStyle
+}
+
+export const AnimatedPoliSearchBar: FC<
+ AnimatedPoliSearchBarProps
+> = props => {
+ const { isLight } = usePalette()
+
+ return (
+
+ {
+ props.setSearch(val)
+ if (val !== "") {
+ props.setIsSearching(true)
+ } else if (props.isSearching === true) {
+ props.setIsSearching(false)
+ }
+ }}
+ style={{ marginTop: 0, marginBottom: 0 }}
+ />
+
+
+ )
+}
diff --git a/src/components/Groups/ExpandablePoliSearchBar.tsx b/src/components/Groups/ExpandablePoliSearchBar.tsx
deleted file mode 100644
index f92018c5..00000000
--- a/src/components/Groups/ExpandablePoliSearchBar.tsx
+++ /dev/null
@@ -1,127 +0,0 @@
-import { Group } from "api/groups"
-import { PoliSearchBar } from "components/Home"
-import React, { FC } from "react"
-import { ScrollView, View } from "react-native"
-import { usePalette } from "utils/colors"
-import { createGroupLink } from "utils/groups"
-import { AnimatedLine } from "./AnimatedLine"
-import { GroupTile } from "./GroupTile"
-import { OutlinedButton } from "./OutlinedButton"
-
-export interface ExpandablePoliSearchBarProps {
- setSearch: (val: string) => void
- isSearching: boolean
- setIsSearching: (val: boolean) => void
- groups?: Group[]
- language: ValidLanguageType
- setLanguage: (val: ValidLanguageType) => void
-}
-
-export type ValidLanguageType = "ITA" | "ENG" | undefined
-
-export const ExpandablePoliSearchBar: FC<
- ExpandablePoliSearchBarProps
-> = props => {
- const { isLight } = usePalette()
-
- return (
-
-
- {
- props.setSearch(val)
- if (val !== "") {
- props.setIsSearching(true)
- } else if (props.isSearching === true) {
- props.setIsSearching(false)
- props.setLanguage(undefined)
- } else {
- props.setLanguage(undefined)
- }
- }}
- style={{ marginTop: 0, marginBottom: 0 }}
- />
-
-
- {props.groups && props.isSearching && (
-
-
- {
- if (props.language === "ITA") {
- props.setLanguage(undefined)
- } else {
- props.setLanguage("ITA")
- }
- }}
- />
- {
- if (props.language === "ENG") {
- props.setLanguage(undefined)
- } else {
- props.setLanguage("ENG")
- }
- }}
- />
-
-
- {props.groups.map((group, idx) => {
- return (
-
- )
- })}
-
-
- )}
-
-
- )
-}
diff --git a/src/components/Groups/Filters.tsx b/src/components/Groups/Filters.tsx
index e48f46cd..2e9305b9 100644
--- a/src/components/Groups/Filters.tsx
+++ b/src/components/Groups/Filters.tsx
@@ -14,6 +14,7 @@ export interface FiltersProps {
setType: (value: string) => void
platform: string
setPlatform: (value: string) => void
+ forceSearch: () => void
}
interface ModalItemList {
@@ -71,6 +72,7 @@ export const Filters: FC = props => {
props.setCourse(all)
props.setType(all)
props.setPlatform(all)
+ props.forceSearch()
}
return (
diff --git a/src/components/Groups/GroupTile.tsx b/src/components/Groups/GroupTile.tsx
index 3b2aef7f..b1898583 100644
--- a/src/components/Groups/GroupTile.tsx
+++ b/src/components/Groups/GroupTile.tsx
@@ -1,36 +1,66 @@
+import { Divider } from "components/Divider"
import { BodyText } from "components/Text"
import React, { FC } from "react"
-import { Linking, Pressable } from "react-native"
+import { Pressable, View } from "react-native"
import { usePalette } from "utils/colors"
export interface GroupTileProps {
name?: string
link?: string
+ onClick?: () => void
}
export const GroupTile: FC = props => {
const { isLight } = usePalette()
- const handlePress = async () => {
- if (!props.link) {
- return
- }
- // Checking if the link is supported for links with custom URL scheme.
- const supported = await Linking.canOpenURL(props.link)
-
- if (supported) {
- // Opening the link with some app
- await Linking.openURL(props.link)
- }
- }
-
return (
-
-
+
- {props.name}
-
-
+
+
+
+
+ {props.name}
+
+
+
+ -:- members
+
+
+
+
+
+
)
}
diff --git a/src/components/Groups/ModalGroup.tsx b/src/components/Groups/ModalGroup.tsx
new file mode 100644
index 00000000..5014bd3a
--- /dev/null
+++ b/src/components/Groups/ModalGroup.tsx
@@ -0,0 +1,151 @@
+import React, { FC } from "react"
+import { View, Modal, StyleSheet, Pressable } from "react-native"
+import { usePalette } from "utils/colors"
+import { ButtonCustom } from "components/Settings"
+import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia"
+import { deleteSvg as icon } from "assets/modal"
+import { Group } from "api/groups"
+
+export interface ModalGroupProps {
+ /**
+ * content of the modal
+ */
+ children: React.ReactNode
+
+ /**
+ * whether ot not to show the modal
+ */
+ isShowing: boolean
+ /**
+ * this function hides the modal by changing the state in the parent component
+ */
+ onClose: () => void
+
+ /**
+ * function called when button "OK" is pressed
+ */
+ onJoin: (group?: Group) => void
+
+ group?: Group
+ /**
+ * modal wrapper height, specify if height is fixed
+ */
+ height?: number
+}
+
+/**
+ * Custom Modal Component with two buttons at the bottom.
+ *
+ */
+export const ModalGroup: FC = props => {
+ const { backgroundSecondary, homeBackground, modalBarrier, isLight } =
+ usePalette()
+
+ const deleteSvg = useSVG(icon.svg)
+ return (
+ //TODO: animationType fade or slide?
+
+
+
+ props.onClose()}
+ >
+
+
+
+
+
+ {props.children}
+
+ props.onJoin(props.group)}
+ />
+
+
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ pageWrapper: {
+ flex: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ },
+ contentWrapper: {
+ flexDirection: "column",
+ justifyContent: "space-between",
+ width: 320,
+ borderRadius: 12,
+ marginHorizontal: 15,
+
+ shadowColor: "#000",
+ shadowOffset: {
+ width: 0,
+ height: 3,
+ },
+ shadowOpacity: 0.27,
+ shadowRadius: 4.65,
+ elevation: 6,
+ },
+ title: {
+ fontSize: 32,
+ fontWeight: "900",
+ },
+})
diff --git a/src/components/Groups/PageWrapper.tsx b/src/components/Groups/PageWrapper.tsx
new file mode 100644
index 00000000..8c46e51e
--- /dev/null
+++ b/src/components/Groups/PageWrapper.tsx
@@ -0,0 +1,56 @@
+import React, { FC } from "react"
+import { View, ViewStyle } from "react-native"
+import { NavBar, NavbarProps } from "components/NavBar"
+import { usePalette } from "utils/colors"
+
+/**
+ * Groups page Wrapper
+ */
+export const PageWrapper: FC<{
+ children: React.ReactNode
+
+ title?: string
+ /**
+ * Remove the navbar from the bottom of the page.
+ */
+ hideNavbar?: boolean
+ /**
+ * Props for the navbar, see {@link NavBar}
+ */
+ navbarOptions?: NavbarProps
+ marginTop?: number
+ style?: ViewStyle
+}> = props => {
+ const { background, isLight, primary } = usePalette()
+
+ const navbar = !props.hideNavbar
+
+ return (
+
+
+ {props.children}
+
+ {navbar ? : null}
+
+ )
+}
diff --git a/src/components/Home/PoliSearchBar.tsx b/src/components/Home/PoliSearchBar.tsx
index 8f60331f..d5237e85 100644
--- a/src/components/Home/PoliSearchBar.tsx
+++ b/src/components/Home/PoliSearchBar.tsx
@@ -5,6 +5,7 @@ import {
Pressable,
StyleProp,
ViewStyle,
+ Keyboard,
} from "react-native"
import { usePalette } from "utils/colors"
import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia"
@@ -25,6 +26,20 @@ export const PoliSearchBar: FC<{
const [isFocused, setIsFocused] = useState(false)
const shadowAnim = useRef(new Animated.Value(0)).current
const inputText = useRef(null)
+
+ useEffect(() => {
+ const keyboardDidHideListener = Keyboard.addListener(
+ "keyboardDidHide",
+ () => {
+ inputText.current?.blur()
+ }
+ )
+
+ return () => {
+ keyboardDidHideListener.remove()
+ }
+ }, [])
+
useEffect(() => {
const duration = 100
if (isFocused)
diff --git a/src/components/Settings/ButtonCustom.tsx b/src/components/Settings/ButtonCustom.tsx
index 0dce8349..958fc1f6 100644
--- a/src/components/Settings/ButtonCustom.tsx
+++ b/src/components/Settings/ButtonCustom.tsx
@@ -12,6 +12,8 @@ export interface ButtonCustomProps {
light?: boolean
onPress?: () => void
buttonStyle?: ViewStyle
+ overrideLight?: string
+ overrideDark?: string
}
/**
* Custom button component. Specify param `light` to select button type
@@ -26,8 +28,8 @@ export const ButtonCustom: FC = props => {
{
backgroundColor: props.light
? isLight
- ? palette.lighter
- : palette.darker
+ ? (props.overrideLight ?? palette.lighter)
+ : (props.overrideDark ?? palette.darker)
: isLight
? palette.darker
: palette.lighter,
diff --git a/src/pages/Groups.tsx b/src/pages/Groups.tsx
index 589ef804..b772329e 100644
--- a/src/pages/Groups.tsx
+++ b/src/pages/Groups.tsx
@@ -1,22 +1,22 @@
import React, { useEffect, useState } from "react"
import { MainStackScreen } from "navigation/NavigationTypes"
-import { View } from "react-native"
-import { ContentWrapperScroll } from "components/ContentWrapperScroll"
-import { Title } from "components/Text"
+import { Linking, ScrollView, View } from "react-native"
+import { BodyText, Title } from "components/Text"
import { Filters } from "components/Groups/Filters"
-import {
- ExpandablePoliSearchBar,
- ValidLanguageType,
-} from "components/Groups/ExpandablePoliSearchBar"
import { api, RetryType } from "api"
import { Group } from "api/groups"
import { useMounted } from "utils/useMounted"
import {
- filterByLanguage,
+ createGroupLink,
msPassedBetween,
orderByMostRecentYear,
} from "utils/groups"
import { wait } from "utils/functions"
+import { AnimatedPoliSearchBar } from "components/Groups/AnimatedPoliSearchBar"
+import { GroupTile } from "components/Groups/GroupTile"
+import { PageWrapper } from "components/Groups/PageWrapper"
+import { usePalette } from "utils/colors"
+import { ModalGroup } from "components/Groups/ModalGroup"
const all = "Tutti"
@@ -54,13 +54,17 @@ export const Groups: MainStackScreen<"Groups"> = () => {
const [groups, setGroups] = useState(undefined)
- const [language, setLanguage] = useState()
+ /* const [language, setLanguage] = useState() */
- //actually displayed groups (filtered by language)
- const [filteredGroups, setFilteredGroups] = useState(
+ //actually displayed groups (Ordered by language)
+ const [orderedGroups, setOrderedGroups] = useState(
undefined
)
+ const [isModalShowing, setIsModalShowing] = useState(false)
+
+ const [modalGroup, setModalGroup] = useState(undefined)
+
//tracking first render
const isMounted = useMounted()
@@ -136,25 +140,19 @@ export const Groups: MainStackScreen<"Groups"> = () => {
return
} else {
//force search in next frame
- setRescheduleSearch(rescheduleSearch + 1)
- setNeedSearching(true)
+ forceSearch()
}
}
}, [prevSearch])
- //filter items every time selected language changes
+ //if filters are applied after search, search again
useEffect(() => {
if (isMounted && groups) {
- if (language) {
- const newFilteredGroups = filterByLanguage(groups, language)
- setFilteredGroups(newFilteredGroups)
- } else {
- resetFilterLanguage()
- }
+ forceSearch()
}
- }, [language])
+ }, [course, year, type, platform])
- //load groups to filtered groups every time a new response from api arrives. (language filters ignored)
+ //load groups to Ordered groups every time a new response from api arrives.
useEffect(() => {
if (isMounted) {
let newGroups = groups
@@ -162,45 +160,145 @@ export const Groups: MainStackScreen<"Groups"> = () => {
if (year === all && newGroups) {
newGroups = orderByMostRecentYear(newGroups)
}
- if (language !== undefined && newGroups) {
- newGroups = filterByLanguage(newGroups, language)
- }
- setFilteredGroups(newGroups)
+ setOrderedGroups(newGroups)
}
}, [groups])
- //helper function to reset language filters
- const resetFilterLanguage = () => {
- setFilteredGroups(groups)
+ const forceSearch = () => {
+ setRescheduleSearch(rescheduleSearch + 1)
+ setNeedSearching(true)
}
+
+ const { isLight } = usePalette()
return (
-
-
-
- Gruppi Corsi
- setIsSearching(val)}
- setSearch={val => setSearch(val)}
- groups={filteredGroups}
- language={language}
- setLanguage={val => setLanguage(val)}
- />
-
- {!isSearching && (
- setYear(val)}
- course={course}
- setCourse={val => setCourse(val)}
- type={type}
- setType={val => setType(val)}
- platform={platform}
- setPlatform={val => setPlatform(val)}
- />
- )}
+
+
+ Gruppi Corsi
+ setIsSearching(val)}
+ setSearch={val => setSearch(val)}
+ groups={orderedGroups}
+ style={{ marginTop: 36, marginBottom: 22 }}
+ />
+ setYear(val)}
+ course={course}
+ setCourse={val => setCourse(val)}
+ type={type}
+ setType={val => setType(val)}
+ platform={platform}
+ setPlatform={val => setPlatform(val)}
+ forceSearch={forceSearch}
+ />
+
+
+
+ {orderedGroups?.map((group, idx) => {
+ return (
+ {
+ setModalGroup(group)
+ setIsModalShowing(true)
+ }}
+ />
+ )
+ })}
+
-
-
+ setIsModalShowing(false)}
+ onJoin={async (group?: Group) => {
+ if (!group?.link_id) {
+ return
+ }
+
+ const link = createGroupLink(
+ group.link_id,
+ group.platform
+ )
+ // Checking if the link is supported for links with custom URL scheme.
+ const supported = await Linking.canOpenURL(link)
+
+ if (supported) {
+ // Opening the link with some app
+ await Linking.openURL(link)
+ }
+ }}
+ >
+
+
+
+ {modalGroup?.class}
+
+
+
+ --:-- members, --:-- online
+
+
+ {modalGroup?.year} {modalGroup?.platform}
+
+
+
+
+
+
)
}
From 816e558989cd9dd2a8a98394cc7aba485a395bbb Mon Sep 17 00:00:00 2001
From: DiegoZaff
Date: Mon, 23 Jan 2023 11:31:22 +0100
Subject: [PATCH 10/17] General fixes
---
src/api/groups.ts | 85 +----------------
.../Groups/AnimatedPoliSearchBar.tsx | 8 +-
src/components/Groups/GroupTile.tsx | 5 +-
src/components/Groups/ModalGroup.tsx | 7 +-
src/components/Groups/ModalGroupItem.tsx | 68 ++++++++++++++
src/pages/Groups.tsx | 93 ++++---------------
src/utils/groups.ts | 31 ++++---
7 files changed, 114 insertions(+), 183 deletions(-)
create mode 100644 src/components/Groups/ModalGroupItem.tsx
diff --git a/src/api/groups.ts b/src/api/groups.ts
index 1e1702e9..29242ea4 100644
--- a/src/api/groups.ts
+++ b/src/api/groups.ts
@@ -21,7 +21,7 @@ export interface Group {
link_id: string
language: string
type_?: string
- year: string | null
+ year: string | null //probably I should use | null evreywhere?
platform: string
permanent_id?: number
last_updated?: string
@@ -38,8 +38,6 @@ export const groups = {
* Retrieves groups from PoliNetwork server.
* Check {@link GroupOptions} for additional parameters.
*/
-
- // ! temporarily broken
async get(groupsOptions?: GroupOptions, options?: RequestOptions) {
const response = await client.poliNetworkInstance.get<{
groups: Group[]
@@ -57,85 +55,4 @@ export const groups = {
})
return response.data.groups
},
- getMocked() {
- return mockedGroups.groups
- },
-}
-
-//random information
-const mockedGroups = {
- groups: [
- {
- name: "GRUPPO 1 ITA",
- year: "2022",
- id: "1",
- degree: "LT",
- type: "S",
- platform: "WA",
- language: "ITA",
- office: "Leonardo",
- school: "idk",
- idLink: "https://t.me/joinchat/9RcVXahIKchlMGZk",
- },
- {
- name: "GRUPPO 2 ITA",
- year: "2021",
- id: "1",
- degree: "LT",
- type: "S",
- platform: "TG",
- language: "ITA",
- office: "Leonardo",
- school: "idk",
- idLink: "https://t.me/joinchat/lcaKVtappk83NzU0",
- },
- {
- name: "GRUPPO 3 ENG",
- year: "2020",
- id: "1",
- degree: "LT",
- type: "C",
- platform: "TG",
- language: "ENG",
- office: "Leonardo",
- school: "idk",
- idLink: "https://chat.whatsapp.com/HDZd7mCzDg80dS4fCcSszy",
- },
- {
- name: "GRUPPO 4 ENG",
- year: "2020",
- id: "1",
- degree: "LT",
- type: "C",
- platform: "TG",
- language: "ENG",
- office: "Leonardo",
- school: "idk",
- idLink: "https://t.me/joinchat/YEBlpQ_fzoZmYzI0",
- },
- {
- name: "GRUPPO 5 ITA",
- year: "2020",
- id: "1",
- degree: "LT",
- type: "C",
- platform: "TG",
- language: "ITA",
- office: "Leonardo",
- school: "idk",
- idLink: "https://www.facebook.com/groups/170744940120942",
- },
- {
- name: "GRUPPO 6 ENG",
- year: "2020",
- id: "1",
- degree: "LT",
- type: "C",
- platform: "TG",
- language: "ENG",
- office: "Leonardo",
- school: "idk",
- idLink: "https://t.me/joinchat/_9vETcjqnX5iNzNk",
- },
- ],
}
diff --git a/src/components/Groups/AnimatedPoliSearchBar.tsx b/src/components/Groups/AnimatedPoliSearchBar.tsx
index c3678fa3..d1044cdd 100644
--- a/src/components/Groups/AnimatedPoliSearchBar.tsx
+++ b/src/components/Groups/AnimatedPoliSearchBar.tsx
@@ -1,12 +1,9 @@
import { Group } from "api/groups"
import { PoliSearchBar } from "components/Home"
import React, { FC } from "react"
-import { ScrollView, View, ViewStyle } from "react-native"
-import { usePalette } from "utils/colors"
-import { createGroupLink } from "utils/groups"
+import { View, ViewStyle } from "react-native"
import { AnimatedLine } from "./AnimatedLine"
-import { GroupTile } from "./GroupTile"
-import { OutlinedButton } from "./OutlinedButton"
+
export interface AnimatedPoliSearchBarProps {
setSearch: (val: string) => void
@@ -19,7 +16,6 @@ export interface AnimatedPoliSearchBarProps {
export const AnimatedPoliSearchBar: FC<
AnimatedPoliSearchBarProps
> = props => {
- const { isLight } = usePalette()
return (
diff --git a/src/components/Groups/GroupTile.tsx b/src/components/Groups/GroupTile.tsx
index b1898583..f84abe1f 100644
--- a/src/components/Groups/GroupTile.tsx
+++ b/src/components/Groups/GroupTile.tsx
@@ -5,8 +5,7 @@ import { Pressable, View } from "react-native"
import { usePalette } from "utils/colors"
export interface GroupTileProps {
- name?: string
- link?: string
+ text?: string
onClick?: () => void
}
@@ -45,7 +44,7 @@ export const GroupTile: FC = props => {
color: isLight ? "#454773" : "#fff",
}}
>
- {props.name}
+ {props.text}
void
/**
- * function called when button "OK" is pressed
+ * function called when button "JOIN GROUP" is pressed
*/
onJoin: (group?: Group) => void
@@ -34,12 +34,11 @@ export interface ModalGroupProps {
}
/**
- * Custom Modal Component with two buttons at the bottom.
+ * Custom Modal Component for Groups Page.
*
*/
export const ModalGroup: FC = props => {
- const { backgroundSecondary, homeBackground, modalBarrier, isLight } =
- usePalette()
+ const { backgroundSecondary, modalBarrier } = usePalette()
const deleteSvg = useSVG(icon.svg)
return (
diff --git a/src/components/Groups/ModalGroupItem.tsx b/src/components/Groups/ModalGroupItem.tsx
new file mode 100644
index 00000000..31fc1bb5
--- /dev/null
+++ b/src/components/Groups/ModalGroupItem.tsx
@@ -0,0 +1,68 @@
+import { BodyText } from "components/Text"
+import React, { FC } from "react"
+import { View } from "react-native"
+import { usePalette } from "utils/colors"
+import { Group } from "api/groups"
+
+export interface ModalGroupItemProps {
+ /**
+ * ResetButton
+ */
+ group?: Group
+}
+
+export const ModalGroupItem: FC = props => {
+ const { isLight } = usePalette()
+ return (
+
+
+
+ {props.group?.class}
+
+
+
+ --:-- members, --:-- online
+
+
+ {props.group?.year} {props.group?.platform}
+
+
+
+ )
+}
diff --git a/src/pages/Groups.tsx b/src/pages/Groups.tsx
index b772329e..ede8382d 100644
--- a/src/pages/Groups.tsx
+++ b/src/pages/Groups.tsx
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from "react"
import { MainStackScreen } from "navigation/NavigationTypes"
import { Linking, ScrollView, View } from "react-native"
-import { BodyText, Title } from "components/Text"
+import { Title } from "components/Text"
import { Filters } from "components/Groups/Filters"
import { api, RetryType } from "api"
import { Group } from "api/groups"
@@ -15,12 +15,12 @@ import { wait } from "utils/functions"
import { AnimatedPoliSearchBar } from "components/Groups/AnimatedPoliSearchBar"
import { GroupTile } from "components/Groups/GroupTile"
import { PageWrapper } from "components/Groups/PageWrapper"
-import { usePalette } from "utils/colors"
import { ModalGroup } from "components/Groups/ModalGroup"
+import { ModalGroupItem } from "components/Groups/ModalGroupItem"
const all = "Tutti"
-const sleepTime = 500 //ms
+const deltaTime = 500 //ms
export const Groups: MainStackScreen<"Groups"> = () => {
//keep track of latest search request time (successful or not)
@@ -34,7 +34,7 @@ export const Groups: MainStackScreen<"Groups"> = () => {
//reason : I want to trigger it only if I set needSearching to True, not to False
const [rescheduleSearch, setRescheduleSearch] = useState(0)
- //for triggering api request when user doesnt write in a time span of `sleepTime`
+ //for triggering api request when user doesnt write in a time span of `deltaTime`
//reason: I don't want to send api request on every character change.
const [prevSearch, setPrevSearch] = useState("")
@@ -54,9 +54,7 @@ export const Groups: MainStackScreen<"Groups"> = () => {
const [groups, setGroups] = useState(undefined)
- /* const [language, setLanguage] = useState() */
-
- //actually displayed groups (Ordered by language)
+ //actually displayed groups (Ordered by year)
const [orderedGroups, setOrderedGroups] = useState(
undefined
)
@@ -75,9 +73,10 @@ export const Groups: MainStackScreen<"Groups"> = () => {
* 1) gets called every time search text changes or rescheduleSearch is incremented (for forcing search request)
* 2) if len(search) < 5 do nothing and delete stored groups if there are any.
* 3) if no other searches were done before, send search request to api. Store time of search in `lastSearchTime`.
- * 4) successive requests are allowed if last search request time (successful or not) was more than `sleepTime` (500ms) ago
- * 5) if a request is not allowed, store `lastTimeSearch` anyway, then check `sleepTime` (500ms) later if the user has kept writing.
- * if he has, then do nothing because side effect will be called again, if he hasn't, then force search request with latest search value.
+ * 4) successive requests are allowed if last search request time (successful or not) was more than `deltaTime` (500ms) ago
+ * 5) if a request is not allowed, store `lastTimeSearch` anyway, then check `deltaTime` (500ms) later if the user has kept writing.
+ * if he has, then do nothing because side effect will be called again, if he hasn't,
+ * then force search request with latest search value.
*/
const searchGroups = async () => {
if (isMounted) {
@@ -92,7 +91,7 @@ export const Groups: MainStackScreen<"Groups"> = () => {
const msPassed = msPassedBetween(lastSearchTime, now)
if (
msPassed === undefined ||
- msPassed >= sleepTime ||
+ msPassed >= deltaTime ||
needSearching === true
) {
//update last time search
@@ -111,10 +110,8 @@ export const Groups: MainStackScreen<"Groups"> = () => {
//reset need searching for next render
setNeedSearching(false)
} else {
- // ? over optimization maybe: this prevents api request if user keeps writing fast.
- // ? maybe I should request every 1 seconds even if user is still writing? debatable
setLastSearchTime(now)
- await wait(sleepTime)
+ await wait(deltaTime)
//in the next render you have the previous and current search text, and can compare changes
//this triggers Reschedule Search Side Effect
setPrevSearch(search)
@@ -131,7 +128,7 @@ export const Groups: MainStackScreen<"Groups"> = () => {
/**
* Reschedule Search Side Effect
*
- * evaluate if user stopped writing characters in a time span of `sleepTime` seconds
+ * evaluate if user stopped writing characters in a time span of `deltaTime` seconds
*/
useEffect(() => {
if (isMounted) {
@@ -156,7 +153,10 @@ export const Groups: MainStackScreen<"Groups"> = () => {
useEffect(() => {
if (isMounted) {
let newGroups = groups
-
+ //if response from api arrives when we no longer expect it
+ if (newGroups && search.length < 5) {
+ newGroups = undefined
+ }
if (year === all && newGroups) {
newGroups = orderByMostRecentYear(newGroups)
}
@@ -164,12 +164,12 @@ export const Groups: MainStackScreen<"Groups"> = () => {
}
}, [groups])
+ //force search in next render
const forceSearch = () => {
setRescheduleSearch(rescheduleSearch + 1)
setNeedSearching(true)
}
- const { isLight } = usePalette()
return (
= () => {
marginBottom: 93,
}}
>
-
+
{orderedGroups?.map((group, idx) => {
return (
{
setModalGroup(group)
@@ -247,56 +243,7 @@ export const Groups: MainStackScreen<"Groups"> = () => {
}
}}
>
-
-
-
- {modalGroup?.class}
-
-
-
- --:-- members, --:-- online
-
-
- {modalGroup?.year} {modalGroup?.platform}
-
-
-
+
diff --git a/src/utils/groups.ts b/src/utils/groups.ts
index 98f9d6f6..5a5e32d8 100644
--- a/src/utils/groups.ts
+++ b/src/utils/groups.ts
@@ -1,15 +1,5 @@
import { Group } from "api/groups"
-/**
- * return groups filtered by language
- * see {@link Groups} Page
- */
-export function filterByLanguage(groups: Group[], language?: string) {
- return groups.filter(group => {
- return group.language === language
- })
-}
-
/**
* return groups ordered by most recent year using a bubble sort algorithm
* see {@link Groups} Page
@@ -38,16 +28,31 @@ export function orderByMostRecentYear(groups: Group[]) {
}
function compareBiYear(first: string | null, second: string | null) {
- if ((first === null || first === "?/?") && second !== null) {
+ //apparently null != undefined :( code breaks if I use undefined instead of null
+ if ((first === "?/?" || first === null) && second !== null) {
return true
} else if (second === null || first === null) {
return false
}
- const regex = /^\d{4}\/\d{4}$/
- if (regex.test(first) && regex.test(second)) {
+ //standard year format "2021/2022"
+ const regexStandard = /^\d{4}\/\d{4}$/
+ //for inconsistencies in the db ex "2021/22"
+ const regexNonStandard = /^\d{4}\/\d{2}$/
+ if (
+ (regexStandard.test(first) && regexStandard.test(second)) ||
+ (regexNonStandard.test(first) && regexNonStandard.test(second))
+ ) {
if (parseInt(first.substring(5)) < parseInt(second.substring(5))) {
return true
}
+ } else if (regexNonStandard.test(first) && regexStandard.test(second)) {
+ if (parseInt(first.substring(5)) < parseInt(second.substring(7))) {
+ return true
+ }
+ } else if (regexStandard.test(first) && regexNonStandard.test(second)) {
+ if (parseInt(first.substring(7)) < parseInt(second.substring(5))) {
+ return true
+ }
}
return false
}
From bff3567f92501c45f72018a436565286b737a343 Mon Sep 17 00:00:00 2001
From: DiegoZaff
Date: Sun, 29 Jan 2023 10:43:08 +0100
Subject: [PATCH 11/17] added svg platform icons
---
assets/groups/facebook.svg | 4 +++
assets/groups/index.ts | 32 ++++++++++++++++++
assets/groups/telegram.svg | 10 ++++++
assets/groups/whatsapp.svg | 21 ++++++++++++
src/components/Groups/GroupTile.tsx | 42 ++++++++++++++++++++++--
src/components/Groups/ModalGroupItem.tsx | 34 +++++++++++++++++--
src/pages/Groups.tsx | 2 ++
src/utils/groups.ts | 13 +++++++-
8 files changed, 152 insertions(+), 6 deletions(-)
create mode 100644 assets/groups/facebook.svg
create mode 100644 assets/groups/index.ts
create mode 100644 assets/groups/telegram.svg
create mode 100644 assets/groups/whatsapp.svg
diff --git a/assets/groups/facebook.svg b/assets/groups/facebook.svg
new file mode 100644
index 00000000..0ab81d19
--- /dev/null
+++ b/assets/groups/facebook.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/groups/index.ts b/assets/groups/index.ts
new file mode 100644
index 00000000..3cc1a4cf
--- /dev/null
+++ b/assets/groups/index.ts
@@ -0,0 +1,32 @@
+import { DataSourceParam } from "@shopify/react-native-skia"
+import whatsapp from "./whatsapp.svg"
+import facebook from "./facebook.svg"
+import telegram from "./telegram.svg"
+
+/**
+ * list of tray icons
+ */
+export const platformIconList = ["telegram", "whatsapp", "facebook"] as const
+
+export type PlatformIcon = typeof platformIconList[number]
+
+export const platformIcons: Record<
+ PlatformIcon,
+ { svg: DataSourceParam; width: number; heigth: number }
+> = {
+ whatsapp: {
+ svg: whatsapp,
+ width: 24,
+ heigth: 24,
+ },
+ telegram: {
+ svg: telegram,
+ width: 24,
+ heigth: 24,
+ },
+ facebook: {
+ svg: facebook,
+ width: 24,
+ heigth: 24,
+ },
+}
\ No newline at end of file
diff --git a/assets/groups/telegram.svg b/assets/groups/telegram.svg
new file mode 100644
index 00000000..bbc6a5e4
--- /dev/null
+++ b/assets/groups/telegram.svg
@@ -0,0 +1,10 @@
+
diff --git a/assets/groups/whatsapp.svg b/assets/groups/whatsapp.svg
new file mode 100644
index 00000000..5f7a5387
--- /dev/null
+++ b/assets/groups/whatsapp.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/components/Groups/GroupTile.tsx b/src/components/Groups/GroupTile.tsx
index f84abe1f..2d50c9ed 100644
--- a/src/components/Groups/GroupTile.tsx
+++ b/src/components/Groups/GroupTile.tsx
@@ -1,3 +1,9 @@
+import {
+ Canvas,
+ DataSourceParam,
+ ImageSVG,
+ useSVG,
+} from "@shopify/react-native-skia"
import { Divider } from "components/Divider"
import { BodyText } from "components/Text"
import React, { FC } from "react"
@@ -6,12 +12,16 @@ import { usePalette } from "utils/colors"
export interface GroupTileProps {
text?: string
+ icon?: { svg: DataSourceParam; width: number; heigth: number }
onClick?: () => void
}
export const GroupTile: FC = props => {
const { isLight } = usePalette()
+ const iconSvg = useSVG(props.icon?.svg)
+
+
return (
= props => {
+ >
+ {props.icon && iconSvg && (
+
+
+
+ )}
+
= props => {
const { isLight } = usePalette()
+ const icon = choosePlatformIcon(props.group?.platform)
+ const iconSvg = useSVG(icon?.svg)
+
+ const scaleFactor = 2.5
+
return (
= props => {
style={{
width: 88,
height: 88,
- backgroundColor: isLight ? "#454773" : "#fff",
borderRadius: 44,
marginTop: 16,
marginBottom: 8,
+ justifyContent: "center",
+ alignItems: "center",
}}
- />
+ >
+ {icon && iconSvg && (
+
+ )}
+
= props => {
marginTop: 16,
}}
>
- {props.group?.year} {props.group?.platform}
+ {props.group?.year}
diff --git a/src/pages/Groups.tsx b/src/pages/Groups.tsx
index ede8382d..af41eb13 100644
--- a/src/pages/Groups.tsx
+++ b/src/pages/Groups.tsx
@@ -7,6 +7,7 @@ import { api, RetryType } from "api"
import { Group } from "api/groups"
import { useMounted } from "utils/useMounted"
import {
+ choosePlatformIcon,
createGroupLink,
msPassedBetween,
orderByMostRecentYear,
@@ -216,6 +217,7 @@ export const Groups: MainStackScreen<"Groups"> = () => {
setModalGroup(group)
setIsModalShowing(true)
}}
+ icon={choosePlatformIcon(group.platform)}
/>
)
})}
diff --git a/src/utils/groups.ts b/src/utils/groups.ts
index 5a5e32d8..87281643 100644
--- a/src/utils/groups.ts
+++ b/src/utils/groups.ts
@@ -1,5 +1,5 @@
import { Group } from "api/groups"
-
+import { platformIcons } from "assets/groups"
/**
* return groups ordered by most recent year using a bubble sort algorithm
* see {@link Groups} Page
@@ -87,3 +87,14 @@ export function msPassedBetween(start: Date | undefined, end: Date) {
}
return end.getTime() - start.getTime()
}
+
+export function choosePlatformIcon(platform? : string){
+ if(platform === "TG"){
+ return platformIcons.telegram
+ }else if(platform ==="FB"){
+ return platformIcons.facebook
+ }else if(platform === "WA"){
+ return platformIcons.whatsapp
+ }
+ return undefined
+}
\ No newline at end of file
From fcf77568a63bb4a62fd1fe0b833dfa25e793a2c5 Mon Sep 17 00:00:00 2001
From: DiegoZaff
Date: Sun, 29 Jan 2023 13:33:33 +0100
Subject: [PATCH 12/17] added 2022/2023
---
src/components/Groups/Filters.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/Groups/Filters.tsx b/src/components/Groups/Filters.tsx
index 2e9305b9..e2ffcc29 100644
--- a/src/components/Groups/Filters.tsx
+++ b/src/components/Groups/Filters.tsx
@@ -23,8 +23,8 @@ interface ModalItemList {
}
const yearsList: ModalItemList = {
- itemsToShow: ["2021/2022", "2020/2021", "2019/2020", "2018/2019"],
- itemsToSave: ["2021/2022", "2020/2021", "2019/2020", "2018/2019"],
+ itemsToShow: [ "2022/2023","2021/2022", "2020/2021", "2019/2020", "2018/2019"],
+ itemsToSave: [ "2022/2023","2021/2022", "2020/2021", "2019/2020", "2018/2019"],
}
const coursesList: ModalItemList = {
itemsToShow: ["Triennale", "Magistrale", "Ciclo unico"],
From a9be3e255f4155fb714d4fae65d2962d3e5f28f2 Mon Sep 17 00:00:00 2001
From: DiegoZaff
Date: Tue, 31 Jan 2023 23:39:07 +0100
Subject: [PATCH 13/17] pr requested changes
---
assets/groups/index.ts | 2 +-
.../Groups/AnimatedPoliSearchBar.tsx | 25 +-
src/components/Groups/Filters.tsx | 127 +++++-----
src/components/Groups/GroupTile.tsx | 1 -
src/components/Groups/ModalGroup.tsx | 10 +-
src/components/Settings/ButtonCustom.tsx | 10 +-
src/components/Settings/CareerTile.tsx | 2 +-
src/components/Settings/ModalSelection.tsx | 6 -
src/pages/Groups.tsx | 220 +++++-------------
src/pages/settings/Settings.tsx | 2 -
src/utils/groups.ts | 11 +-
11 files changed, 150 insertions(+), 266 deletions(-)
diff --git a/assets/groups/index.ts b/assets/groups/index.ts
index 3cc1a4cf..fd402523 100644
--- a/assets/groups/index.ts
+++ b/assets/groups/index.ts
@@ -26,7 +26,7 @@ export const platformIcons: Record<
},
facebook: {
svg: facebook,
- width: 24,
+ width: 25,
heigth: 24,
},
}
\ No newline at end of file
diff --git a/src/components/Groups/AnimatedPoliSearchBar.tsx b/src/components/Groups/AnimatedPoliSearchBar.tsx
index d1044cdd..a990a25e 100644
--- a/src/components/Groups/AnimatedPoliSearchBar.tsx
+++ b/src/components/Groups/AnimatedPoliSearchBar.tsx
@@ -1,36 +1,29 @@
-import { Group } from "api/groups"
import { PoliSearchBar } from "components/Home"
-import React, { FC } from "react"
+import React, { FC, useState } from "react"
import { View, ViewStyle } from "react-native"
import { AnimatedLine } from "./AnimatedLine"
-
export interface AnimatedPoliSearchBarProps {
- setSearch: (val: string) => void
- isSearching: boolean
- setIsSearching: (val: boolean) => void
- groups?: Group[]
+ onSearch: (val: string) => void
style?: ViewStyle
}
-export const AnimatedPoliSearchBar: FC<
- AnimatedPoliSearchBarProps
-> = props => {
-
+export const AnimatedPoliSearchBar: FC = props => {
+ const [isSearching, setIsSearching] = useState(false)
return (
{
- props.setSearch(val)
+ props.onSearch(val)
if (val !== "") {
- props.setIsSearching(true)
- } else if (props.isSearching === true) {
- props.setIsSearching(false)
+ setIsSearching(true)
+ } else if (isSearching === true) {
+ setIsSearching(false)
}
}}
style={{ marginTop: 0, marginBottom: 0 }}
/>
-
+
)
}
diff --git a/src/components/Groups/Filters.tsx b/src/components/Groups/Filters.tsx
index e2ffcc29..a2fec793 100644
--- a/src/components/Groups/Filters.tsx
+++ b/src/components/Groups/Filters.tsx
@@ -6,15 +6,15 @@ import { ModalSelection, SelectTile } from "components/Settings"
import { getNameFromMode, ValidModalType } from "utils/groups"
export interface FiltersProps {
- year: string
- setYear: (value: string) => void
- course: string
- setCourse: (value: string) => void
- type: string
- setType: (value: string) => void
- platform: string
- setPlatform: (value: string) => void
- forceSearch: () => void
+ filters: Filters
+ onFilterChange: (filters: Filters) => void
+}
+
+export interface Filters {
+ year?: string
+ course?: string
+ platform?: string
+ type?: string
}
interface ModalItemList {
@@ -23,8 +23,20 @@ interface ModalItemList {
}
const yearsList: ModalItemList = {
- itemsToShow: [ "2022/2023","2021/2022", "2020/2021", "2019/2020", "2018/2019"],
- itemsToSave: [ "2022/2023","2021/2022", "2020/2021", "2019/2020", "2018/2019"],
+ itemsToShow: [
+ "2022/2023",
+ "2021/2022",
+ "2020/2021",
+ "2019/2020",
+ "2018/2019",
+ ],
+ itemsToSave: [
+ "2022/2023",
+ "2021/2022",
+ "2020/2021",
+ "2019/2020",
+ "2018/2019",
+ ],
}
const coursesList: ModalItemList = {
itemsToShow: ["Triennale", "Magistrale", "Ciclo unico"],
@@ -41,8 +53,6 @@ const platformsList: ModalItemList = {
itemsToSave: ["WA", "FB", "TG"],
}
-//to avoid writing mistakes
-const all = "Tutti"
export const Filters: FC = props => {
//show or hide modal
@@ -52,27 +62,13 @@ export const Filters: FC = props => {
//items to show inside modal
const [modalItems, setModalItems] = useState(yearsList)
//currently selected item inside modal
- const [selectedItem, setSelectedItem] = useState(all)
+ const [selectedItem, setSelectedItem] = useState(
+ undefined
+ )
- //update state when user taps "OK" in modal
- const updateSelectedFilter = () => {
- if (modalMode === "year") {
- props.setYear(selectedItem)
- } else if (modalMode === "course") {
- props.setCourse(selectedItem)
- } else if (modalMode === "platform") {
- props.setPlatform(selectedItem)
- } else {
- props.setType(selectedItem)
- }
- }
//reset state on "reset"
const reset = () => {
- props.setYear(all)
- props.setCourse(all)
- props.setType(all)
- props.setPlatform(all)
- props.forceSearch()
+ props.onFilterChange({})
}
return (
@@ -81,63 +77,56 @@ export const Filters: FC = props => {
style={{
flexDirection: "row",
flexWrap: "wrap",
- paddingHorizontal: 10,
}}
>
{
setModalMode("year")
setModalItems(yearsList)
- setSelectedItem(props.year)
+ setSelectedItem(props.filters.year)
setIsModalShowing(true)
}}
/>
{
setModalMode("course")
setModalItems(coursesList)
- setSelectedItem(props.course)
+ setSelectedItem(props.filters.course)
setIsModalShowing(true)
}}
/>
{
setModalMode("type")
setModalItems(typesList)
- setSelectedItem(props.type)
+ setSelectedItem(props.filters.type)
setIsModalShowing(true)
}}
/>
{
setModalMode("platform")
setModalItems(platformsList)
- setSelectedItem(props.platform)
+ setSelectedItem(props.filters.platform)
setIsModalShowing(true)
}}
/>
@@ -147,17 +136,36 @@ export const Filters: FC = props => {
onClose={() => {
setIsModalShowing(false)
}}
- selectedValue={selectedItem}
onOK={() => {
- updateSelectedFilter()
+ if (modalMode === "course") {
+ props.onFilterChange({
+ ...props.filters,
+ course: selectedItem,
+ })
+ } else if (modalMode === "platform") {
+ props.onFilterChange({
+ ...props.filters,
+ platform: selectedItem,
+ })
+ } else if (modalMode === "year") {
+ props.onFilterChange({
+ ...props.filters,
+ year: selectedItem,
+ })
+ } else if (modalMode === "type") {
+ props.onFilterChange({
+ ...props.filters,
+ type: selectedItem,
+ })
+ }
setIsModalShowing(false)
}}
>
{
- setSelectedItem(all)
+ setSelectedItem(undefined)
}}
/>
{modalItems?.itemsToShow.map((itemName, index) => {
@@ -180,10 +188,9 @@ export const Filters: FC = props => {
}
const styles = StyleSheet.create({
- buttonRightMargin: {
- marginRight: 8,
- },
- buttonBottomMargin: {
+ buttonCustomMargin: {
+ marginRight: 4,
+ marginLeft: 4,
marginBottom: 8,
},
})
diff --git a/src/components/Groups/GroupTile.tsx b/src/components/Groups/GroupTile.tsx
index 2d50c9ed..d9852971 100644
--- a/src/components/Groups/GroupTile.tsx
+++ b/src/components/Groups/GroupTile.tsx
@@ -21,7 +21,6 @@ export const GroupTile: FC = props => {
const iconSvg = useSVG(props.icon?.svg)
-
return (
= props => {
- const { backgroundSecondary, modalBarrier } = usePalette()
+ const { backgroundSecondary, modalBarrier, isLight } = usePalette()
const deleteSvg = useSVG(icon.svg)
return (
@@ -65,7 +65,6 @@ export const ModalGroup: FC = props => {
height: 30,
backgroundColor: "#ffffff",
borderRadius: 15,
- marginTop: 96,
marginBottom: 8,
justifyContent: "center",
alignItems: "center",
@@ -108,8 +107,11 @@ export const ModalGroup: FC = props => {
}}
>
props.onJoin(props.group)}
/>
diff --git a/src/components/Settings/ButtonCustom.tsx b/src/components/Settings/ButtonCustom.tsx
index 958fc1f6..51ff3606 100644
--- a/src/components/Settings/ButtonCustom.tsx
+++ b/src/components/Settings/ButtonCustom.tsx
@@ -11,9 +11,7 @@ export interface ButtonCustomProps {
*/
light?: boolean
onPress?: () => void
- buttonStyle?: ViewStyle
- overrideLight?: string
- overrideDark?: string
+ style?: ViewStyle
}
/**
* Custom button component. Specify param `light` to select button type
@@ -28,14 +26,14 @@ export const ButtonCustom: FC = props => {
{
backgroundColor: props.light
? isLight
- ? (props.overrideLight ?? palette.lighter)
- : (props.overrideDark ?? palette.darker)
+ ? (palette.lighter)
+ : ( palette.darker)
: isLight
? palette.darker
: palette.lighter,
minWidth: 130,
},
- props.buttonStyle,
+ props.style,
]}
onPress={props.onPress}
>
diff --git a/src/components/Settings/CareerTile.tsx b/src/components/Settings/CareerTile.tsx
index a3e2b1aa..161c9ee6 100644
--- a/src/components/Settings/CareerTile.tsx
+++ b/src/components/Settings/CareerTile.tsx
@@ -31,7 +31,7 @@ export const CareerTile: FC = props => {
diff --git a/src/components/Settings/ModalSelection.tsx b/src/components/Settings/ModalSelection.tsx
index 9f2ff791..e7b609cb 100644
--- a/src/components/Settings/ModalSelection.tsx
+++ b/src/components/Settings/ModalSelection.tsx
@@ -26,12 +26,6 @@ export interface ModalSelectionProps {
*/
onOK: () => void
- /**
- * input value of `onOk` function.
- * Usually the current state value of a {@link RadioButtonGroup}
- */
- selectedValue: string
-
/**
* modal wrapper height, specify if height is fixed
*/
diff --git a/src/pages/Groups.tsx b/src/pages/Groups.tsx
index af41eb13..4089cfc0 100644
--- a/src/pages/Groups.tsx
+++ b/src/pages/Groups.tsx
@@ -9,56 +9,24 @@ import { useMounted } from "utils/useMounted"
import {
choosePlatformIcon,
createGroupLink,
- msPassedBetween,
orderByMostRecentYear,
} from "utils/groups"
-import { wait } from "utils/functions"
+
import { AnimatedPoliSearchBar } from "components/Groups/AnimatedPoliSearchBar"
import { GroupTile } from "components/Groups/GroupTile"
import { PageWrapper } from "components/Groups/PageWrapper"
import { ModalGroup } from "components/Groups/ModalGroup"
import { ModalGroupItem } from "components/Groups/ModalGroupItem"
-const all = "Tutti"
-
-const deltaTime = 500 //ms
+const deltaTime = 100 //ms
+let searchTimeout: NodeJS.Timeout
export const Groups: MainStackScreen<"Groups"> = () => {
- //keep track of latest search request time (successful or not)
- const [lastSearchTime, setLastSearchTime] = useState(
- undefined
- )
- //for forcing api search request if needeed
- const [needSearching, setNeedSearching] = useState(false)
-
- //for triggering api search request side effect (used in conjunction with `needSearching`)
- //reason : I want to trigger it only if I set needSearching to True, not to False
- const [rescheduleSearch, setRescheduleSearch] = useState(0)
-
- //for triggering api request when user doesnt write in a time span of `deltaTime`
- //reason: I don't want to send api request on every character change.
- const [prevSearch, setPrevSearch] = useState("")
-
const [search, setSearch] = useState("")
- const [isSearching, setIsSearching] = useState(false)
-
- // ? I'd like to typecheck year, course, etc... but I need dynamic type checking
- // ? and it's very ugly, and need to use type guards or some library, so maybe it is not worth it?
- const [year, setYear] = useState(all)
-
- const [course, setCourse] = useState(all)
+ const [filters, setFilters] = useState({})
- const [type, setType] = useState(all)
-
- const [platform, setPlatform] = useState(all)
-
- const [groups, setGroups] = useState(undefined)
-
- //actually displayed groups (Ordered by year)
- const [orderedGroups, setOrderedGroups] = useState(
- undefined
- )
+ const [groups, setGroups] = useState([])
const [isModalShowing, setIsModalShowing] = useState(false)
@@ -68,136 +36,64 @@ export const Groups: MainStackScreen<"Groups"> = () => {
const isMounted = useMounted()
/**
- * Api search request side effect.
- *
- * How this works (general ideas):
- * 1) gets called every time search text changes or rescheduleSearch is incremented (for forcing search request)
- * 2) if len(search) < 5 do nothing and delete stored groups if there are any.
- * 3) if no other searches were done before, send search request to api. Store time of search in `lastSearchTime`.
- * 4) successive requests are allowed if last search request time (successful or not) was more than `deltaTime` (500ms) ago
- * 5) if a request is not allowed, store `lastTimeSearch` anyway, then check `deltaTime` (500ms) later if the user has kept writing.
- * if he has, then do nothing because side effect will be called again, if he hasn't,
- * then force search request with latest search value.
+ * Api search request.
*/
const searchGroups = async () => {
if (isMounted) {
- if (search.length < 5) {
- if (groups !== undefined) {
- setGroups(undefined)
- }
+ if (search.length < 4) {
+ setGroups([])
return
}
try {
- const now = new Date()
- const msPassed = msPassedBetween(lastSearchTime, now)
- if (
- msPassed === undefined ||
- msPassed >= deltaTime ||
- needSearching === true
- ) {
- //update last time search
- setLastSearchTime(now)
- const response = await api.groups.get(
- {
- name: search.trimEnd(),
- year: year === all ? undefined : year,
- degree: course === all ? undefined : course,
- type: type === all ? undefined : type,
- platform: platform === all ? undefined : platform,
- },
- { maxRetries: 1, retryType: RetryType.RETRY_N_TIMES }
- )
- setGroups(response)
- //reset need searching for next render
- setNeedSearching(false)
- } else {
- setLastSearchTime(now)
- await wait(deltaTime)
- //in the next render you have the previous and current search text, and can compare changes
- //this triggers Reschedule Search Side Effect
- setPrevSearch(search)
- }
+ //update last time search
+ const response = await api.groups.get(
+ {
+ name: search.trimEnd(),
+ year: filters.year,
+ platform: filters.platform,
+ type: filters.type,
+ degree: filters.course,
+ },
+ { maxRetries: 1, retryType: RetryType.RETRY_N_TIMES }
+ )
+ setGroups(response)
+
+ //reset need searching for next render
} catch (error) {
console.log(error)
}
}
}
- useEffect(() => {
- void searchGroups()
- }, [search, rescheduleSearch])
- /**
- * Reschedule Search Side Effect
- *
- * evaluate if user stopped writing characters in a time span of `deltaTime` seconds
- */
useEffect(() => {
- if (isMounted) {
- if (prevSearch !== search) {
- //Do nothing
- return
- } else {
- //force search in next frame
- forceSearch()
- }
- }
- }, [prevSearch])
+ clearTimeout(searchTimeout)
+ searchTimeout = setTimeout(() => {
+ void searchGroups()
+ }, deltaTime)
+ }, [search])
//if filters are applied after search, search again
useEffect(() => {
- if (isMounted && groups) {
- forceSearch()
- }
- }, [course, year, type, platform])
-
- //load groups to Ordered groups every time a new response from api arrives.
- useEffect(() => {
- if (isMounted) {
- let newGroups = groups
- //if response from api arrives when we no longer expect it
- if (newGroups && search.length < 5) {
- newGroups = undefined
- }
- if (year === all && newGroups) {
- newGroups = orderByMostRecentYear(newGroups)
- }
- setOrderedGroups(newGroups)
- }
- }, [groups])
-
- //force search in next render
- const forceSearch = () => {
- setRescheduleSearch(rescheduleSearch + 1)
- setNeedSearching(true)
- }
+ if (isMounted && groups) void searchGroups()
+ }, [filters])
return (
Gruppi Corsi
setIsSearching(val)}
- setSearch={val => setSearch(val)}
- groups={orderedGroups}
+ onSearch={val => setSearch(val)}
style={{ marginTop: 36, marginBottom: 22 }}
/>
setYear(val)}
- course={course}
- setCourse={val => setCourse(val)}
- type={type}
- setType={val => setType(val)}
- platform={platform}
- setPlatform={val => setPlatform(val)}
- forceSearch={forceSearch}
+ onFilterChange={filters => setFilters(filters)}
+ filters={filters}
/>
= () => {
}}
>
- {orderedGroups?.map((group, idx) => {
+ {orderByMostRecentYear(
+ groups,
+ filters.year !== undefined
+ )?.map((group, idx) => {
return (
= () => {
})}
- setIsModalShowing(false)}
- onJoin={async (group?: Group) => {
- if (!group?.link_id) {
- return
- }
-
- const link = createGroupLink(
- group.link_id,
- group.platform
- )
- // Checking if the link is supported for links with custom URL scheme.
- const supported = await Linking.canOpenURL(link)
-
- if (supported) {
- // Opening the link with some app
- await Linking.openURL(link)
- }
- }}
- >
-
-
+ setIsModalShowing(false)}
+ onJoin={async (group?: Group) => {
+ if (!group?.link_id) {
+ return
+ }
+
+ const link = createGroupLink(group.link_id, group.platform)
+ // Checking if the link is supported for links with custom URL scheme.
+ const supported = await Linking.canOpenURL(link)
+
+ if (supported) {
+ // Opening the link with some app
+ await Linking.openURL(link)
+ }
+ }}
+ >
+
+
)
}
diff --git a/src/pages/settings/Settings.tsx b/src/pages/settings/Settings.tsx
index f23a2033..4391640c 100644
--- a/src/pages/settings/Settings.tsx
+++ b/src/pages/settings/Settings.tsx
@@ -113,7 +113,6 @@ export const SettingsPage: SettingsStackScreen<"Settings"> = () => {
{
//restore real theme value
setSelectedTheme(theme)
@@ -140,7 +139,6 @@ export const SettingsPage: SettingsStackScreen<"Settings"> = () => {
{
//restore selectedCareer to career
if (career) setSelectedCareer(career)
diff --git a/src/utils/groups.ts b/src/utils/groups.ts
index 87281643..f53ea26e 100644
--- a/src/utils/groups.ts
+++ b/src/utils/groups.ts
@@ -4,7 +4,10 @@ import { platformIcons } from "assets/groups"
* return groups ordered by most recent year using a bubble sort algorithm
* see {@link Groups} Page
*/
-export function orderByMostRecentYear(groups: Group[]) {
+export function orderByMostRecentYear(groups: Group[], isOrdered : boolean) {
+ if(isOrdered){
+ return groups
+ }
let hasChanged: boolean
try {
do {
@@ -81,12 +84,6 @@ export function createGroupLink(idLink: string, platform: string) {
}
}
-export function msPassedBetween(start: Date | undefined, end: Date) {
- if (start === undefined) {
- return undefined
- }
- return end.getTime() - start.getTime()
-}
export function choosePlatformIcon(platform? : string){
if(platform === "TG"){
From 992a968a7f964cc05e74e2c002df0706d4e0ae68 Mon Sep 17 00:00:00 2001
From: DiegoZaff
Date: Wed, 1 Feb 2023 00:37:29 +0100
Subject: [PATCH 14/17] margin fix
---
src/pages/Groups.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/pages/Groups.tsx b/src/pages/Groups.tsx
index 4089cfc0..44aedad2 100644
--- a/src/pages/Groups.tsx
+++ b/src/pages/Groups.tsx
@@ -101,6 +101,7 @@ export const Groups: MainStackScreen<"Groups"> = () => {
flex: 1,
marginTop: 40,
marginBottom: 93,
+ marginHorizontal: 8,
}}
>
From 20dd210d6b13286b59a6d832341c9c5cb795de5d Mon Sep 17 00:00:00 2001
From: DiegoZaff
Date: Wed, 1 Feb 2023 11:11:16 +0100
Subject: [PATCH 15/17] Flatlist and ordered groups fix
---
src/api/groups.ts | 2 +-
src/pages/Groups.tsx | 39 +++++++++++++++++++--------------------
src/utils/groups.ts | 5 +----
3 files changed, 21 insertions(+), 25 deletions(-)
diff --git a/src/api/groups.ts b/src/api/groups.ts
index 29242ea4..64d59351 100644
--- a/src/api/groups.ts
+++ b/src/api/groups.ts
@@ -15,7 +15,7 @@ export interface GroupOptions {
export interface Group {
class: string
office: string
- id?: string
+ id: string
degree?: string
school?: string
link_id: string
diff --git a/src/pages/Groups.tsx b/src/pages/Groups.tsx
index 44aedad2..5ce3d506 100644
--- a/src/pages/Groups.tsx
+++ b/src/pages/Groups.tsx
@@ -1,6 +1,6 @@
import React, { useEffect, useState } from "react"
import { MainStackScreen } from "navigation/NavigationTypes"
-import { Linking, ScrollView, View } from "react-native"
+import { FlatList, Linking, View } from "react-native"
import { Title } from "components/Text"
import { Filters } from "components/Groups/Filters"
import { api, RetryType } from "api"
@@ -40,7 +40,7 @@ export const Groups: MainStackScreen<"Groups"> = () => {
*/
const searchGroups = async () => {
if (isMounted) {
- if (search.length < 4) {
+ if (search.length < 3) {
setGroups([])
return
}
@@ -77,6 +77,9 @@ export const Groups: MainStackScreen<"Groups"> = () => {
if (isMounted && groups) void searchGroups()
}, [filters])
+ const orderedGroups =
+ filters.year === undefined ? orderByMostRecentYear(groups) : groups
+
return (
= () => {
marginHorizontal: 8,
}}
>
-
- {orderByMostRecentYear(
- groups,
- filters.year !== undefined
- )?.map((group, idx) => {
- return (
- {
- setModalGroup(group)
- setIsModalShowing(true)
- }}
- icon={choosePlatformIcon(group.platform)}
- />
- )
- })}
-
+ (
+ {
+ setModalGroup(group.item)
+ setIsModalShowing(true)
+ }}
+ icon={choosePlatformIcon(group.item.platform)}
+ />
+ )}
+ keyExtractor={item => item.id}
+ />
Date: Wed, 1 Feb 2023 23:49:45 +0100
Subject: [PATCH 16/17] fix no members
---
src/api/groups.ts | 1 +
src/components/Groups/GroupTile.tsx | 21 ++++++++++++---------
src/components/Groups/ModalGroupItem.tsx | 22 ++++++++++++----------
src/pages/Groups.tsx | 1 +
4 files changed, 26 insertions(+), 19 deletions(-)
diff --git a/src/api/groups.ts b/src/api/groups.ts
index 64d59351..b2dbc8f7 100644
--- a/src/api/groups.ts
+++ b/src/api/groups.ts
@@ -26,6 +26,7 @@ export interface Group {
permanent_id?: number
last_updated?: string
link_is_working?: string
+ members?: string
}
const client = HttpClient.getInstance()
diff --git a/src/components/Groups/GroupTile.tsx b/src/components/Groups/GroupTile.tsx
index d9852971..fe62bf93 100644
--- a/src/components/Groups/GroupTile.tsx
+++ b/src/components/Groups/GroupTile.tsx
@@ -13,6 +13,7 @@ import { usePalette } from "utils/colors"
export interface GroupTileProps {
text?: string
icon?: { svg: DataSourceParam; width: number; heigth: number }
+ members?: string
onClick?: () => void
}
@@ -84,15 +85,17 @@ export const GroupTile: FC = props => {
{props.text}
-
- -:- members
-
+ {props.members && (
+
+ {props.members} members
+
+ )}
diff --git a/src/components/Groups/ModalGroupItem.tsx b/src/components/Groups/ModalGroupItem.tsx
index 318c0b08..c6eac75b 100644
--- a/src/components/Groups/ModalGroupItem.tsx
+++ b/src/components/Groups/ModalGroupItem.tsx
@@ -69,16 +69,18 @@ export const ModalGroupItem: FC = props => {
{props.group?.class}
-
- --:-- members, --:-- online
-
+ {props.group?.members && (
+
+ {props.group.members} members
+
+ )}
= () => {
renderItem={group => (
{
setModalGroup(group.item)
setIsModalShowing(true)
From d105cab1b26196f292eb1050e10f0d2fa6ae1bd0 Mon Sep 17 00:00:00 2001
From: Tommaso Morganti
Date: Fri, 3 Feb 2023 16:46:52 +0100
Subject: [PATCH 17/17] small styling fixes
---
src/components/Groups/GroupTile.tsx | 2 +-
src/pages/Groups.tsx | 57 +++++++++++++----------------
2 files changed, 26 insertions(+), 33 deletions(-)
diff --git a/src/components/Groups/GroupTile.tsx b/src/components/Groups/GroupTile.tsx
index fe62bf93..bfd5aac3 100644
--- a/src/components/Groups/GroupTile.tsx
+++ b/src/components/Groups/GroupTile.tsx
@@ -23,7 +23,7 @@ export const GroupTile: FC = props => {
const iconSvg = useSVG(props.icon?.svg)
return (
-
+
= () => {
return (
-
+
Gruppi Corsi
setSearch(val)}
@@ -98,32 +92,31 @@ export const Groups: MainStackScreen<"Groups"> = () => {
onFilterChange={filters => setFilters(filters)}
filters={filters}
/>
-
-
- (
- {
- setModalGroup(group.item)
- setIsModalShowing(true)
- }}
- icon={choosePlatformIcon(group.item.platform)}
- />
- )}
- keyExtractor={item => item.id}
- />
-
+ (
+ {
+ setModalGroup(item)
+ setIsModalShowing(true)
+ }}
+ icon={choosePlatformIcon(item.platform)}
+ />
+ )}
+ />