Skip to content

Commit

Permalink
feat: calendar
Browse files Browse the repository at this point in the history
  • Loading branch information
sverben committed May 25, 2024
1 parent 4e6681e commit 56ea6db
Show file tree
Hide file tree
Showing 13 changed files with 2,653 additions and 2,548 deletions.
13 changes: 11 additions & 2 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ import { useColorScheme } from "react-native";
import merge from "deepmerge";
import WebScreen from "./src/screens/web";
import * as Notifications from "expo-notifications";
import SearchScreen from "./src/screens/feed/search";
import { Item as InventoryItem } from "./src/screens/feed/search";
import SearchScreen, { Item as InventoryItem } from "./src/screens/feed/search";
import ItemScreen from "./src/screens/feed/item";
import { SerializedComponent } from "unfucked-ical";
import EventScreen from "./src/screens/calendar/event";

Notifications.setNotificationHandler({
handleNotification: async () => ({
Expand All @@ -47,6 +48,7 @@ export type StackParamList = {
Web: { source: string; title: string };
Search: undefined;
Item: { item: InventoryItem; title: string };
Event: { event: SerializedComponent; title: string };
};

const Stack = createStackNavigator<StackParamList>();
Expand Down Expand Up @@ -127,6 +129,13 @@ export default function App() {
title: route.params.title,
})}
/>
<Stack.Screen
name={"Event"}
component={EventScreen}
options={({ route }) => ({
title: route.params.title,
})}
/>
</Stack.Navigator>
</AuthProvider>
</PaperProvider>
Expand Down
44 changes: 26 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,49 @@
"web": "expo start --web"
},
"dependencies": {
"@react-native-community/datetimepicker": "8.0.1",
"@react-navigation/native": "^6.1.17",
"@react-navigation/stack": "^6.3.29",
"base-64": "^1.0.0",
"expo": "~50.0.14",
"expo-asset": "~9.0.2",
"expo-auth-session": "~5.4.0",
"expo-av": "~13.10.5",
"expo-device": "~5.9.3",
"expo-image-picker": "~14.7.1",
"expo-notifications": "~0.27.6",
"expo-secure-store": "~12.8.1",
"expo-status-bar": "~1.11.1",
"date-fns": "^3.6.0",
"date-fns-tz": "^3.1.3",
"expo": "~51.0.8",
"expo-asset": "~10.0.6",
"expo-auth-session": "~5.5.2",
"expo-av": "~14.0.5",
"expo-device": "~6.0.2",
"expo-image-picker": "~15.0.5",
"expo-notifications": "~0.28.3",
"expo-secure-store": "~13.0.1",
"expo-status-bar": "~1.12.1",
"fast-html-parser": "^1.0.1",
"html-to-text": "^9.0.5",
"jotai": "^2.8.0",
"jsonwebtoken": "^9.0.2",
"jwt-decode": "^4.0.0",
"react": "18.2.0",
"react-native": "0.73.6",
"react-native-gesture-handler": "~2.14.0",
"react-native-pager-view": "6.2.3",
"react": "18.3.1",
"react-native": "0.74.1",
"react-native-gesture-handler": "~2.16.2",
"react-native-maps": "1.15.3",
"react-native-pager-view": "6.3.1",
"react-native-paper": "^5.12.3",
"react-native-safe-area-context": "4.8.2",
"react-native-screens": "~3.29.0",
"react-native-safe-area-context": "4.10.1",
"react-native-screens": "~3.31.1",
"react-native-vector-icons": "^10.0.3",
"react-native-webview": "13.6.4",
"react-native-webview": "13.10.2",
"rrule": "^2.8.1",
"rss-to-json": "^2.1.1"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@types/base-64": "^1.0.2",
"@types/fast-html-parser": "^1.0.4",
"@types/react": "~18.2.45",
"@types/html-to-text": "^9.0.4",
"@types/react": "~18.3.3",
"@types/react-native-vector-icons": "^6.4.18",
"prettier": "^3.2.5",
"typescript": "^5.1.3"
"typescript": "^5.1.3",
"unfucked-ical": "^1.0.0"
},
"private": true
}
28 changes: 18 additions & 10 deletions src/components/feed/feed.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import {useContext, useEffect} from "react";
import AuthContext, {Authed} from "../../auth";
import {useAtom} from "jotai";
import {ActionType, feedAtom, getAnnouncements, getRSSFeed, sortFeeds,} from "../../stores/feed";
import {ActivityIndicator, Card} from "react-native-paper";
import { useContext, useEffect } from "react";
import AuthContext, { Authed } from "../../auth";
import { useAtom } from "jotai";
import {
feedAtom,
getAnnouncements,
getRSSFeed,
sortFeeds,
} from "../../stores/feed";
import { ActivityIndicator, Card } from "react-native-paper";
import Item from "./item";
import {Asset} from "expo-asset";

export default function Feed() {
const authState = useContext(AuthContext);
Expand All @@ -13,10 +17,14 @@ export default function Feed() {
useEffect(() => {
async function getFeeds() {
const feeds = await Promise.all([
getRSSFeed(),
getAnnouncements(authState.authenticated === Authed.AUTHENTICATED ? await authState.token : null),
]);
setFeed(sortFeeds(...feeds))
getRSSFeed(),
getAnnouncements(
authState.authenticated === Authed.AUTHENTICATED
? await authState.token
: null,
),
]);
setFeed(sortFeeds(...feeds));
}

getFeeds().then();
Expand Down
6 changes: 6 additions & 0 deletions src/components/feed/item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export default function Item({ item }: { item: FeedItem }) {
});
break;
}
case ActionType.EVENT: {
navigation.navigate("Event", {
event: item.action.event,
title: item.title,
});
}
}
}

Expand Down
31 changes: 21 additions & 10 deletions src/components/register/listing.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import {useContext, useEffect} from "react";
import AuthContext, {Authed} from "../../auth";
import {useAtom} from "jotai";
import {getSlots, Slot, slotsAtom} from "../../stores/register";
import {ActivityIndicator, Avatar, Card, IconButton,} from "react-native-paper";
import {StyleSheet, TouchableOpacity} from "react-native";
import {useNavigation} from "@react-navigation/native";
import {NativeStackNavigationProp} from "react-native-screens/native-stack";
import {StackParamList} from "../../../App";
import { useContext, useEffect } from "react";
import AuthContext, { Authed } from "../../auth";
import { useAtom } from "jotai";
import { getSlots, Slot, slotsAtom } from "../../stores/register";
import {
ActivityIndicator,
Avatar,
Card,
IconButton,
} from "react-native-paper";
import { StyleSheet, TouchableOpacity } from "react-native";
import { useNavigation } from "@react-navigation/native";
import { NativeStackNavigationProp } from "react-native-screens/native-stack";
import { StackParamList } from "../../../App";

type SlotNavigationProps = NativeStackNavigationProp<StackParamList>;

Expand Down Expand Up @@ -37,7 +42,13 @@ export default function Listing() {

useEffect(() => {
async function fetchDays() {
setSlots(await getSlots(authState.authenticated === Authed.AUTHENTICATED ? await authState.token : null));
setSlots(
await getSlots(
authState.authenticated === Authed.AUTHENTICATED
? await authState.token
: null,
),
);
}

fetchDays().then();
Expand Down
129 changes: 129 additions & 0 deletions src/screens/calendar/calendar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { Appbar, Button, Card, Icon, Text } from "react-native-paper";
import { Platform, ScrollView, StyleSheet, View } from "react-native";
import DateTimePicker, {
DateTimePickerAndroid,
} from "@react-native-community/datetimepicker";
import { useEffect, useState } from "react";
import { parse, VEvent } from "unfucked-ical";
import Item from "../../components/feed/item";
import { ActionType, FeedItem, sortFeeds } from "../../stores/feed";

export default function CalendarScreen() {
const [date, setDate] = useState(new Date());
const [events, setEvents] = useState<VEvent[]>([]);
const [items, setItems] = useState<FeedItem[]>([]);

useEffect(() => {
async function getEvents() {
const res = await fetch("https://www.djoamersfoort.nl/feed/eo-events/");
const data = await res.text();

setEvents(parse(data).events);
}

getEvents().then();
}, []);

useEffect(() => {
function nextDate(event: VEvent) {
if (event.rrule) {
const next = event.rrule.after(
date > event.timeStart ? date : event.timeStart,
);
if (next) return next;
}

return event.timeStart;
}

date.setHours(0, 0, 0, 0);
const items = events
.map((event) => {
const date = nextDate(event);

return {
icon: event.rrule ? "calendar" : "calendar-alert",
title: event.summary || "unknown",
description: date.toLocaleDateString("nl-NL"),
date: date.getTime(),
action: {
type: ActionType.EVENT,
event: event.serialize(),
},
} satisfies FeedItem;
})
.filter((item) => item.date > date.getTime());

setItems(sortFeeds(items).reverse());
}, [date, events]);

return (
<>
<Appbar.Header>
<Appbar.Content title={"Agenda"} />
</Appbar.Header>
<ScrollView>
<View style={styles.container}>
<Card>
<Card.Content style={styles.header}>
<View style={styles.headerText}>
<Icon size={18} source={"calendar"} />
<Text variant={"titleMedium"}>Vanaf</Text>
</View>

{Platform.OS === "ios" && (
<DateTimePicker
value={date}
onChange={(_, date) => setDate(date as Date)}
mode={"date"}
/>
)}
{Platform.OS === "android" && (
<Button
mode={"elevated"}
onPress={() => {
DateTimePickerAndroid.open({
value: date,
onChange: (_, date) => setDate(date as Date),
mode: "date",
});
}}
>
{date.toLocaleDateString("nl-NL")}
</Button>
)}
</Card.Content>
</Card>

<Card>
<Card.Title title={"Bijzonderheden"} titleVariant={"titleMedium"} />
<Card.Content style={{ gap: 10 }}>
{items.map((item, index) => (
<Item item={item} key={index} />
))}
</Card.Content>
</Card>
</View>
</ScrollView>
</>
);
}

const styles = StyleSheet.create({
container: {
padding: 10,
gap: 10,
},
header: {
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
headerText: {
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: 5,
},
});
Loading

0 comments on commit 56ea6db

Please sign in to comment.