Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: calendar #5

Merged
merged 1 commit into from
May 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading