diff --git a/package.json b/package.json index 3d80ad5..ae9bef9 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "@react-navigation/devtools": "^6.0.20", "@react-navigation/drawer": "^6.6.6", "@react-navigation/native": "^6.1.9", + "@react-navigation/stack": "^6.3.20", "@tanstack/react-query": "^5.8.3", "expo": "^49.0.3", "expo-splash-screen": "~0.20.5", diff --git a/src/components/drawer/routes.ts b/src/components/drawer/routes.ts index c5cfa84..c31eb9f 100644 --- a/src/components/drawer/routes.ts +++ b/src/components/drawer/routes.ts @@ -3,6 +3,7 @@ import RestoMenu from "../../routes/resto"; import EventView from "../../routes/events"; import { DrawerEntry } from "../../types/drawer"; import SchamperView from "../../routes/schamper"; +import LibrariesView from "../../routes/libraries"; import { SchamperIcon } from "../icons/SchamperIcon"; export const routes: DrawerEntry[] = [ @@ -26,4 +27,9 @@ export const routes: DrawerEntry[] = [ element: SchamperView, icon: SchamperIcon, }, + { + name: "Libraries", + element: LibrariesView, + icon: "book", + } ]; diff --git a/src/components/feed/LibraryDetailsComponent.tsx b/src/components/feed/LibraryDetailsComponent.tsx new file mode 100644 index 0000000..4fdbeb5 --- /dev/null +++ b/src/components/feed/LibraryDetailsComponent.tsx @@ -0,0 +1,72 @@ +import { Surface, Text, TouchableRipple, useTheme } from "react-native-paper"; +import type { Library } from "../../types/stores"; +import { Pressable, StyleSheet, View } from "react-native"; +import * as WebBrowser from "expo-web-browser"; +export const LibraryDetailsComponent = ({ library }: { library: Library }) => { + const theme = useTheme(); + + return ( + + + + + {library.name_nl} + + + + {library.address.join(" ")} + {library.email} + {library.telephone} + + + + + WebBrowser.openBrowserAsync(library.link)}> + Go to site + + + ); +}; + +const styles = StyleSheet.create({ + container: { + display: "flex", + flexDirection: "column", + marginVertical: 5, + width: "95%", + borderRadius: 15, + overflow: "hidden", + }, + textContainer: { + padding: 15, + color: "#ddd", + }, + title: { + fontWeight: "600", + color: "#000", + }, + additionalInfo: { + marginTop: 5, + display: "flex", + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + color: "#ddd", + }, + smallText: { + color: "#000", + fontWeight: "900", + }, + button: { + alignItems: 'center', + justifyContent: 'center', + paddingVertical: 12, + paddingHorizontal: 32, + borderRadius: 4, + elevation: 3, + width: 200, + alignSelf: 'center', + marginBottom: 10, + }, +}); + diff --git a/src/components/feed/LibraryListItemComponent.tsx b/src/components/feed/LibraryListItemComponent.tsx new file mode 100644 index 0000000..2966b38 --- /dev/null +++ b/src/components/feed/LibraryListItemComponent.tsx @@ -0,0 +1,104 @@ +import { Surface, Text, TouchableRipple } from "react-native-paper"; +import type { Library } from "../../types/stores"; +import { GestureResponderEvent, Pressable, Image, StyleSheet, View } from "react-native"; +import { useEffect, useState } from "react"; +import * as WebBrowser from "expo-web-browser"; + + +export const LibraryListItemComponent = ({ library, onPress, showDetails }: { library: Library, onPress: ((event: GestureResponderEvent) => void), showDetails: boolean}) => { + const [imgSize, setImgSize] = useState({ width: 0, height: 1 }); + + useEffect(() => { + Image.getSize(library.image_url, (w, h) => { + setImgSize({ + width: w, + height: h, + }); + }); + }, [library.image_url]); + + return ( + + + <> + + + + {library.name_nl} + + + + {library.address.join(" ")} + { + showDetails ? + <> + {library.email} + {library.telephone} + + : + <> + } + + + + + + { + showDetails ? + WebBrowser.openBrowserAsync(library.link)}> + Go to site + + : + <> + } + + ); +}; +const styles = StyleSheet.create({ + container: { + display: "flex", + flexDirection: "column", + marginVertical: 5, + width: "95%", + borderRadius: 15, + overflow: "hidden", + }, + textContainer: { + padding: 15, + color: "#ddd", + }, + title: { + fontWeight: "600", + color: "#000", + }, + additionalInfo: { + marginTop: 5, + display: "flex", + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + color: "#ddd", + }, + smallText: { + color: "#000", + fontWeight: "900", + }, + button: { + alignItems: 'center', + justifyContent: 'center', + paddingVertical: 12, + paddingHorizontal: 32, + borderRadius: 4, + elevation: 3, + width: 200, + alignSelf: 'center', + marginBottom: 10, + }, +}); + diff --git a/src/constant.ts b/src/constant.ts index 7dff0af..e7377c5 100644 --- a/src/constant.ts +++ b/src/constant.ts @@ -1,4 +1,5 @@ export const ENDPOINTS = { HYDRA_V1: "https://hydra.ugent.be/api/1.0/", HYDRA_V2: "https://hydra.ugent.be/api/2.0/", + LIBRARIES: "https://widgets.lib.ugent.be/" }; diff --git a/src/routes/libraries.tsx b/src/routes/libraries.tsx new file mode 100644 index 0000000..a724815 --- /dev/null +++ b/src/routes/libraries.tsx @@ -0,0 +1,28 @@ +import { useLibrariesQuery } from "../stores/libraries"; +import { LibraryListItemComponent } from "../components/feed/LibraryListItemComponent"; +import { StyleSheet, ScrollView } from "react-native"; +import { useState } from "react"; +import { Library } from "../types/stores"; + + +const LibrariesView = () => { + const { data: libraryRequest } = useLibrariesQuery(); + const [selectedLibrary, setSelectedLibrary] = useState(null); + + return ( + + {libraryRequest.libraries.map(lib => ( + setSelectedLibrary(lib)} showDetails={lib==selectedLibrary}/> + ))} + + ); +}; + +const styles = StyleSheet.create({ + container: { + display: "flex", + alignItems: "center", + }, +}); + +export default LibrariesView; diff --git a/src/stores/libraries.ts b/src/stores/libraries.ts new file mode 100644 index 0000000..b711d75 --- /dev/null +++ b/src/stores/libraries.ts @@ -0,0 +1,18 @@ +import { LibraryRequest } from "../types/stores"; +import { ENDPOINTS } from "../constant"; +import { useSuspenseQuery } from "@tanstack/react-query"; +import { useFocusNotifyOnChangeProps } from "../lib/hooks/useFocusNotifyOnChangeProps"; + +export const useLibrariesQuery = () => { + const notifyOnChangeProps = useFocusNotifyOnChangeProps(); + + const schamperQuery = useSuspenseQuery({ + queryKey: ["libraries"], + queryFn: async () => { + const res = await fetch(`${ENDPOINTS.LIBRARIES}/library_groups/main.json`); + return res.json(); + }, + notifyOnChangeProps, + }); + return schamperQuery; +}; diff --git a/src/types/stores.ts b/src/types/stores.ts index 465ed64..938dfd7 100644 --- a/src/types/stores.ts +++ b/src/types/stores.ts @@ -14,3 +14,39 @@ export declare type Article = { // ISO date string pub_date: string; }; + +export declare type LibraryRequest = { + libraries: Library[]; +}; + +export declare type Library = { + name_en: string; + has_hours: boolean; + lat: number; + image_url: string; + telephone: string[]; + reading_room: string; + link_nl: string; + created_at: string; + door_number: string; + email: string; + department: string; + contact: string; + pickup_locations: string[]; + updated_at: string; + link_en: string; + active: boolean; + email_acquisition: string; + cubee_id: string; + link: string; + sap_id: string; + shipment_library_code: string; + name: string; + faculty: string; + code: string; + thumbnail_url: string; + address: string[]; + name_nl: string; + campus: string; + long: number; +}; diff --git a/yarn.lock b/yarn.lock index 378701c..e8ed9d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2075,6 +2075,15 @@ dependencies: nanoid "^3.1.23" +"@react-navigation/stack@^6.3.20": + version "6.3.20" + resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-6.3.20.tgz#8eec944888f317bb1ba1ff30e7f513806bea16c2" + integrity sha512-vE6mgZzOgoa5Uy7ayT97Cj+ZIK7DK+JBYVuKUViILlWZy6IWK7HFDuqoChSbZ1ajTIfAxj/acVGg1jkbAKsToA== + dependencies: + "@react-navigation/elements" "^1.3.21" + color "^4.2.3" + warn-once "^0.1.0" + "@segment/loosely-validate-event@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz#87dfc979e5b4e7b82c5f1d8b722dfd5d77644681"