diff --git a/app.json b/app.json
index ab63ad26..ede0ea96 100644
--- a/app.json
+++ b/app.json
@@ -2,7 +2,7 @@
"expo": {
"name": "TAMU SHPE",
"slug": "TAMU-SHPE",
- "version": "1.0.16",
+ "version": "1.0.17",
"owner": "tamu-shpe",
"orientation": "portrait",
"icon": "./assets/icon.png",
diff --git a/assets/calendar-days-solid_black.svg b/assets/calendar-days-solid_black.svg
new file mode 100644
index 00000000..8d38d2cc
--- /dev/null
+++ b/assets/calendar-days-solid_black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/calendar-days-solid_white.svg b/assets/calendar-days-solid_white.svg
new file mode 100644
index 00000000..f8d5d5f4
--- /dev/null
+++ b/assets/calendar-days-solid_white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/clock-solid_black.svg b/assets/clock-solid_black.svg
new file mode 100644
index 00000000..66f32461
--- /dev/null
+++ b/assets/clock-solid_black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/clock-solid_white.svg b/assets/clock-solid_white.svg
new file mode 100644
index 00000000..40b063d1
--- /dev/null
+++ b/assets/clock-solid_white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/location-dot-solid_black.svg b/assets/location-dot-solid_black.svg
new file mode 100644
index 00000000..acd033c1
--- /dev/null
+++ b/assets/location-dot-solid_black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/location-dot-solid_white.svg b/assets/location-dot-solid_white.svg
new file mode 100644
index 00000000..89977cad
--- /dev/null
+++ b/assets/location-dot-solid_white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/package.json b/package.json
index 661ed312..f10f3e1b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "shpe-app",
- "version": "1.0.16",
+ "version": "1.0.17",
"scripts": {
"start": "npx expo start --dev-client",
"test": "jest --coverage=true --verbose --bail --config ./jest.config.ts",
diff --git a/src/api/firebaseUtils.ts b/src/api/firebaseUtils.ts
index 18648bcf..21942aff 100644
--- a/src/api/firebaseUtils.ts
+++ b/src/api/firebaseUtils.ts
@@ -528,6 +528,29 @@ export const getUpcomingEvents = async () => {
return events;
};
+export const getWeekPastEvents = async (): Promise => {
+ const currentTime = new Date();
+ const twoWeeksAgo = new Date(currentTime);
+ twoWeeksAgo.setDate(currentTime.getDate() - 8);
+
+ const eventsRef = collection(db, "events");
+ const q = query(
+ eventsRef,
+ where("endTime", "<", currentTime),
+ where("endTime", ">", twoWeeksAgo),
+ orderBy("endTime", "desc")
+ );
+
+ const querySnapshot = await getDocs(q);
+ const events: SHPEEvent[] = [];
+
+ querySnapshot.forEach(doc => {
+ events.push({ id: doc.id, ...doc.data() } as SHPEEvent);
+ });
+
+ return events;
+};
+
export const getPastEvents = async (numLimit: number, startAfterDoc: any, setEndOfData?: (endOfData: boolean) => void) => {
const currentTime = new Date();
const eventsRef = collection(db, "events");
diff --git a/src/components/MOTMCard.tsx b/src/components/MOTMCard.tsx
index c9735e15..24314dcc 100644
--- a/src/components/MOTMCard.tsx
+++ b/src/components/MOTMCard.tsx
@@ -68,6 +68,9 @@ const MOTMCard: React.FC = ({ navigation }) => {
}, [currentUser])
);
+ if (!MOTM) {
+ return null;
+ }
return (
diff --git a/src/helpers/timeUtils.ts b/src/helpers/timeUtils.ts
index b97e163b..db54f1ca 100644
--- a/src/helpers/timeUtils.ts
+++ b/src/helpers/timeUtils.ts
@@ -65,26 +65,6 @@ export const formatTime = (date: Date): string => {
return `${hour % 12 == 0 ? 12 : hour % 12}:${minute.toString().padStart(2, '0')}${hour > 11 ? "pm" : "am"}`;
}
-export const formatEventTime = (startDate: Date, endDate: Date): string => {
- const startHour = startDate.getHours();
- const endHour = endDate.getHours();
- const startMinute = startDate.getMinutes();
- const endMinute = endDate.getMinutes();
-
- const startAmPm = startHour >= 12 ? "pm" : "am";
- const endAmPm = endHour >= 12 ? "pm" : "am";
-
- const formattedStartTime = `${startHour % 12 === 0 ? 12 : startHour % 12}:${startMinute.toString().padStart(2, '0')}`;
- const formattedEndTime = `${endHour % 12 === 0 ? 12 : endHour % 12}:${endMinute.toString().padStart(2, '0')}${endAmPm}`;
-
- if (startAmPm === endAmPm) {
- return `${formattedStartTime} - ${formattedEndTime}`;
- }
-
- return `${formattedStartTime}${startAmPm} - ${formattedEndTime}`;
-}
-
-
/**
* Constructs a readable string that represents the date and time of day of a given `Date` object and it's timezone offset.
* @param date
@@ -94,7 +74,26 @@ export const formatDateTime = (date: Date): string => {
return `${formatDate(date)}, ${formatTime(date)} GMT${date.getTimezoneOffset() < 0 ? "+" : "-"}${(date.getTimezoneOffset() / 60).toString().padStart(2, '0')}`
}
-export const formatEventDate = (startTime: Date, endTime: Date) => {
+export const formatEventDate = (startTime: Date, endTime: Date): string => {
+ // Formats the date as "Monday, September 16"
+ const formatFullDate = (date: Date): string => {
+ const options: Intl.DateTimeFormatOptions = { weekday: 'long', month: 'long', day: 'numeric' };
+ return date.toLocaleDateString('en-US', options);
+ };
+
+ // Formats the date as "September 16"
+ const formatMonthDay = (date: Date): string => {
+ const options: Intl.DateTimeFormatOptions = { month: 'long', day: 'numeric' };
+ return date.toLocaleDateString('en-US', options);
+ };
+
+ // Formats the date as "September 16, 2024"
+ const formatMonthDayYear = (date: Date): string => {
+ const options: Intl.DateTimeFormatOptions = { month: 'long', day: 'numeric', year: 'numeric' };
+ return date.toLocaleDateString('en-US', options);
+ };
+
+ // Check if start and end times are on the same day, month, or year
const isSameDay = startTime.getDate() === endTime.getDate() &&
startTime.getMonth() === endTime.getMonth() &&
startTime.getFullYear() === endTime.getFullYear();
@@ -103,22 +102,82 @@ export const formatEventDate = (startTime: Date, endTime: Date) => {
startTime.getFullYear() === endTime.getFullYear();
const isSameYear = startTime.getFullYear() === endTime.getFullYear();
- const formatMonthDayOnly = (date: Date): string => {
- const day = date.getDate();
- const month = monthNames[date.getMonth()];
- return `${month} ${day}`;
+ if (isSameDay) {
+ return formatFullDate(startTime); // "Monday, September 16"
+ } else if (isSameMonth) {
+ return `${formatMonthDay(startTime)} - ${endTime.getDate()}`; // "September 16 - 17"
+ } else if (isSameYear) {
+ return `${formatMonthDay(startTime)} - ${formatMonthDay(endTime)}`; // "September 16 - October 18"
+ } else {
+ return `${formatMonthDayYear(startTime)} - ${formatMonthDayYear(endTime)}`; // "December 17, 2024 - December 18, 2025"
}
+};
+export const formatEventDateTime = (startTime: Date, endTime: Date): string => {
+ // Formats the date as "Monday, September 16"
+ const formatFullDate = (date: Date): string => {
+ const options: Intl.DateTimeFormatOptions = { weekday: 'long', month: 'long', day: 'numeric' };
+ return date.toLocaleDateString('en-US', options);
+ };
+
+ // Formats the date as "September 16"
+ const formatMonthDay = (date: Date): string => {
+ const options: Intl.DateTimeFormatOptions = { month: 'long', day: 'numeric' };
+ return date.toLocaleDateString('en-US', options);
+ };
+
+ // Formats the date as "September 16, 2024"
+ const formatMonthDayYear = (date: Date): string => {
+ const options: Intl.DateTimeFormatOptions = { month: 'long', day: 'numeric', year: 'numeric' };
+ return date.toLocaleDateString('en-US', options);
+ };
+
+ // Formats the time as "1:00pm"
+ const formatTime = (date: Date): string => {
+ const hours = date.getHours();
+ const minutes = date.getMinutes();
+ const amPm = hours >= 12 ? 'pm' : 'am';
+ const formattedHour = hours % 12 === 0 ? 12 : hours % 12;
+ const formattedMinute = minutes.toString().padStart(2, '0');
+ return `${formattedHour}:${formattedMinute}${amPm}`;
+ };
+
+ const isSameDay = startTime.getDate() === endTime.getDate() &&
+ startTime.getMonth() === endTime.getMonth() &&
+ startTime.getFullYear() === endTime.getFullYear();
+
+ const isSameMonth = startTime.getMonth() === endTime.getMonth() &&
+ startTime.getFullYear() === endTime.getFullYear();
+
+ const isSameYear = startTime.getFullYear() === endTime.getFullYear();
if (isSameDay) {
- return `${formatDate(startTime)}`;
+ return `${formatFullDate(startTime)}, ${formatTime(startTime)} - ${formatTime(endTime)}`; // "Monday, September 16, 1:00pm - 2:00pm"
} else if (isSameMonth) {
- return `${formatMonthDayOnly(startTime)} - ${endTime.getDate()}`;
+ return `${formatMonthDay(startTime)}, ${formatTime(startTime)} - ${formatMonthDay(endTime)}, ${formatTime(endTime)}`; // "September 16, 1:00pm - September 17, 2:00pm"
} else if (isSameYear) {
- return `${formatMonthDayOnly(startTime)} - ${formatDate(endTime)}`;
+ return `${formatMonthDay(startTime)}, ${formatTime(startTime)} - ${formatMonthDay(endTime)}, ${formatTime(endTime)}`; // "September 16, 1:00pm - October 18, 2:00pm"
} else {
- return `${formatDateWithYear(startTime)} - ${formatDateWithYear(endTime)}`;
+ return `${formatMonthDayYear(startTime)}, ${formatTime(startTime)} - ${formatMonthDayYear(endTime)}, ${formatTime(endTime)}`; // "December 17, 2024, 1:00pm - December 18, 2025, 2:00pm"
}
-};
\ No newline at end of file
+};
+
+export const formatEventTime = (startDate: Date, endDate: Date): string => {
+ // Helper function to format time
+ const formatTime = (date: Date): string => {
+ const hours = date.getHours();
+ const minutes = date.getMinutes();
+ const amPm = hours >= 12 ? 'pm' : 'am';
+ const formattedHour = hours % 12 === 0 ? 12 : hours % 12;
+ const formattedMinute = minutes.toString().padStart(2, '0');
+ return `${formattedHour}:${formattedMinute}${amPm}`;
+ };
+
+ const startFormatted = formatTime(startDate);
+ const endFormatted = formatTime(endDate);
+
+ // Format time range output
+ return `${startFormatted} - ${endFormatted}`;
+};
diff --git a/src/screens/committees/CommitteeInfo.tsx b/src/screens/committees/CommitteeInfo.tsx
index 6f3cec89..136b4d46 100644
--- a/src/screens/committees/CommitteeInfo.tsx
+++ b/src/screens/committees/CommitteeInfo.tsx
@@ -348,12 +348,6 @@ const CommitteeInfo: React.FC = ({ route, navigat
Upcoming Events
- navigation.getParent()?.navigate('EventsTab', { screen: 'EventsScreen', params: { filter: EventType.COMMITTEE_MEETING, committee: firebaseDocName } })}
- >
- View all
-
{
const route = useRoute();
@@ -142,6 +152,15 @@ const EventInfo = ({ navigation }: EventProps) => {
const eventButtonState = getEventButtonState(event, userEventLog);
+ const isSameDay = (startDate: Date, endDate: Date): boolean => {
+ return startDate.getDate() === endDate.getDate() &&
+ startDate.getMonth() === endDate.getMonth() &&
+ startDate.getFullYear() === endDate.getFullYear();
+ };
+ const sameDay = startTime && endTime && isSameDay(startTime.toDate(), endTime.toDate());
+
+
+
return (
@@ -309,41 +328,41 @@ const EventInfo = ({ navigation }: EventProps) => {
{name}
{eventType}
- {committee && (" • " + reverseFormattedFirebaseName(committee))} • {calculateMaxPossiblePoints(event)} points
-
-
- Hosted By {creatorData?.name}
+ {committee && (" • " + reverseFormattedFirebaseName(committee))} • {calculateMaxPossiblePoints(event)} points • {creatorData?.name}
{/* Date, Time and Location */}
-
-
-
- Date
- {(startTime && endTime) ? formatEventDate(startTime.toDate(), endTime.toDate()) : ""}
+
+
+
+ {darkMode ? : }
+
+ {(startTime && endTime) ?
+ (sameDay ?
+ formatEventDate(startTime.toDate(), endTime.toDate())
+ : formatEventDateTime(startTime.toDate(), endTime.toDate())
+ )
+ : ""
+ }
+
+
-
- Time
- {startTime && endTime && formatEventTime(startTime.toDate(), endTime.toDate())}
+
+ {sameDay && (
+
+
+ {darkMode ? : }
+
+
+ {startTime && endTime && formatEventTime(startTime.toDate(), endTime.toDate())}
+
-
+ )}
{locationName && (
- Location
{geolocation ? (
{
handleLinkPress(`https://www.google.com/maps/dir/?api=1&destination=${geolocation.latitude},${geolocation.longitude}`);
}
}}
+ className='flex-row items-center'
>
-
+
+ {darkMode ? : }
+
+
{locationName}
) : (
-
+
{locationName}
)}
@@ -372,8 +395,8 @@ const EventInfo = ({ navigation }: EventProps) => {
{/* Description */}
{(description && description.trim() != "") && (
-
- About Event
+
+ Description
{description}
)}
diff --git a/src/screens/events/Events.tsx b/src/screens/events/Events.tsx
index bdc5cca3..9694286e 100644
--- a/src/screens/events/Events.tsx
+++ b/src/screens/events/Events.tsx
@@ -1,14 +1,12 @@
import { View, Text, TouchableOpacity, ActivityIndicator, ScrollView, Image, useColorScheme } from 'react-native'
-import React, { useCallback, useContext, useEffect, useRef, useState } from 'react'
+import React, { useCallback, useContext, useEffect, useState } from 'react'
import { SafeAreaView } from 'react-native-safe-area-context';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
-import AsyncStorage from '@react-native-async-storage/async-storage';
import { RouteProp, useFocusEffect, useRoute } from '@react-navigation/core';
import { Octicons, FontAwesome6 } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import { StatusBar } from 'expo-status-bar';
-import { auth } from '../../config/firebaseConfig';
-import { getUpcomingEvents, getPastEvents, getCommittees, fetchAndStoreUser } from '../../api/firebaseUtils';
+import { getUpcomingEvents, fetchAndStoreUser, getWeekPastEvents } from '../../api/firebaseUtils';
import { UserContext } from '../../context/UserContext';
import { Images } from '../../../assets';
import { formatTime } from '../../helpers/timeUtils';
@@ -16,43 +14,46 @@ import { truncateStringWithEllipsis } from '../../helpers/stringUtils';
import { EventsStackParams } from '../../types/navigation';
import { EventType, ExtendedEventType, SHPEEvent } from '../../types/events';
import EventCard from './EventCard';
-import { Committee } from '../../types/committees';
import DismissibleModal from '../../components/DismissibleModal';
+interface EventGroups {
+ today: SHPEEvent[];
+ upcoming: SHPEEvent[];
+ past: SHPEEvent[];
+}
+
+
const Events = ({ navigation }: EventsProps) => {
const route = useRoute();
const userContext = useContext(UserContext);
const { userInfo, setUserInfo } = userContext!;
-
const fixDarkMode = userInfo?.private?.privateInfo?.settings?.darkMode;
const useSystemDefault = userInfo?.private?.privateInfo?.settings?.useSystemDefault;
const colorScheme = useColorScheme();
const darkMode = useSystemDefault ? colorScheme === 'dark' : fixDarkMode;
- const filterScrollViewRef = useRef(null);
- const committeeScrollViewRef = useRef(null);
-
const [isLoading, setIsLoading] = useState(true);
- const [todayEvents, setTodayEvents] = useState([]);
- const [upcomingEvents, setUpcomingEvents] = useState([]);
- const [pastEvents, setPastEvents] = useState([]);
- const [selectedFilter, setSelectedFilter] = useState(route.params?.filter || null);
- const [committees, setCommittees] = useState([]);
- const [selectedCommittee, setSelectedCommittee] = useState(route.params?.committee || null);
+ const [mainEvents, setMainEvents] = useState({ today: [], upcoming: [], past: [] });
+ const [intramuralEvents, setIntramuralEvents] = useState({ today: [], upcoming: [], past: [] });
+ const [committeeEvents, setCommitteeEvents] = useState({ today: [], upcoming: [], past: [] });
const [infoVisible, setInfoVisible] = useState(false);
+ const [filter, setFilter] = useState<"main" | "intramural" | "committee">("main");
const hasPrivileges = (userInfo?.publicInfo?.roles?.admin?.valueOf() || userInfo?.publicInfo?.roles?.officer?.valueOf() || userInfo?.publicInfo?.roles?.developer?.valueOf() || userInfo?.publicInfo?.roles?.lead?.valueOf() || userInfo?.publicInfo?.roles?.representative?.valueOf());
+ const selectedEvents = filter === "main" ? mainEvents : filter === "intramural" ? intramuralEvents : committeeEvents;
+
const fetchEvents = async () => {
try {
setIsLoading(true);
const upcomingEventsData = await getUpcomingEvents();
- const pastEventsData = await getPastEvents(3, null);
+ const allPastEvents = await getWeekPastEvents();
const currentTime = new Date();
const today = new Date(currentTime.getFullYear(), currentTime.getMonth(), currentTime.getDate());
+
const todayEvents = upcomingEventsData.filter(event => {
const startTime = event.startTime ? event.startTime.toDate() : new Date(0);
return startTime >= today && startTime < new Date(today.getTime() + 24 * 60 * 60 * 1000);
@@ -62,9 +63,63 @@ const Events = ({ navigation }: EventsProps) => {
return startTime >= new Date(today.getTime() + 24 * 60 * 60 * 1000);
});
- setTodayEvents(todayEvents);
- setUpcomingEvents(upcomingEvents);
- setPastEvents(pastEventsData.events);
+ const mainEventsFiltered = upcomingEventsData.filter(
+ (event: SHPEEvent) =>
+ (hasPrivileges || !event.hiddenEvent) &&
+ event.eventType !== EventType.COMMITTEE_MEETING &&
+ event.eventType !== EventType.INTRAMURAL_EVENT
+ );
+
+ const intramuralEventsFiltered = upcomingEventsData.filter(
+ (event: SHPEEvent) =>
+ (hasPrivileges || !event.hiddenEvent) &&
+ event.eventType === EventType.INTRAMURAL_EVENT
+ );
+
+ const committeeEventsFiltered = upcomingEventsData.filter(
+ (event: SHPEEvent) =>
+ (hasPrivileges || !event.hiddenEvent) &&
+ event.eventType === EventType.COMMITTEE_MEETING
+ );
+
+ const pastMainEvents = allPastEvents.filter(
+ (event: SHPEEvent) =>
+ (hasPrivileges || !event.hiddenEvent) &&
+ event.eventType !== EventType.COMMITTEE_MEETING &&
+ event.eventType !== EventType.INTRAMURAL_EVENT
+ );
+
+ const pastIntramuralEvents = allPastEvents.filter(
+ (event: SHPEEvent) =>
+ (hasPrivileges || !event.hiddenEvent) &&
+ event.eventType === EventType.INTRAMURAL_EVENT
+ );
+
+ const pastCommitteeEvents = allPastEvents.filter(
+ (event: SHPEEvent) =>
+ (hasPrivileges || !event.hiddenEvent) &&
+ event.eventType === EventType.COMMITTEE_MEETING
+ );
+
+
+
+ setMainEvents({
+ today: todayEvents.filter(event => mainEventsFiltered.includes(event)),
+ upcoming: upcomingEvents.filter(event => mainEventsFiltered.includes(event)),
+ past: pastMainEvents,
+ });
+
+ setIntramuralEvents({
+ today: todayEvents.filter(event => intramuralEventsFiltered.includes(event)),
+ upcoming: upcomingEvents.filter(event => intramuralEventsFiltered.includes(event)),
+ past: pastIntramuralEvents,
+ });
+
+ setCommitteeEvents({
+ today: todayEvents.filter(event => committeeEventsFiltered.includes(event)),
+ upcoming: upcomingEvents.filter(event => committeeEventsFiltered.includes(event)),
+ past: pastCommitteeEvents,
+ });
setIsLoading(false);
} catch (error) {
@@ -73,11 +128,6 @@ const Events = ({ navigation }: EventsProps) => {
}
};
- const fetchCommittees = async () => {
- const committeeData = await getCommittees();
- setCommittees(committeeData);
- };
-
useEffect(() => {
const fetchUserData = async () => {
@@ -89,30 +139,8 @@ const Events = ({ navigation }: EventsProps) => {
fetchEvents();
fetchUserData();
- fetchCommittees();
}, [])
- useEffect(() => {
- if (route.params?.filter !== undefined) {
- setSelectedFilter(route.params.filter);
-
- const filterIndex = ["myEvents", "clubWide", ...Object.values(EventType)].indexOf(route.params.filter);
- if (filterIndex !== -1 && filterScrollViewRef.current) {
- const scrollPosition = filterIndex * 115;
- filterScrollViewRef.current.scrollTo({ x: scrollPosition, animated: true });
- }
- }
-
- if (route.params?.committee !== undefined) {
- setSelectedCommittee(route.params.committee);
-
- const committeeIndex = committees.findIndex(committee => committee.firebaseDocName === route.params.committee);
- if (committeeIndex !== -1 && committeeScrollViewRef.current) {
- const scrollPosition = committeeIndex * 100;
- committeeScrollViewRef.current.scrollTo({ x: scrollPosition, animated: true });
- }
- }
- }, [route.params, committees]);
useFocusEffect(
useCallback(() => {
@@ -122,75 +150,6 @@ const Events = ({ navigation }: EventsProps) => {
}, [hasPrivileges])
);
- const handleFilterSelect = (filter?: ExtendedEventType, committee?: string) => {
- // Deselect committee when the same committee is selected
- if (committee) {
- if (selectedCommittee === committee) {
- setSelectedCommittee(null);
- if (!selectedFilter) {
- setSelectedFilter(null);
- }
- } else {
- setSelectedCommittee(committee);
- if (selectedFilter !== EventType.COMMITTEE_MEETING) {
- setSelectedFilter(EventType.COMMITTEE_MEETING);
- }
- }
- return;
- }
-
- // Deselect "Committee Meetings" when no committee is selected
- if (filter === EventType.COMMITTEE_MEETING && selectedFilter === EventType.COMMITTEE_MEETING) {
- setSelectedFilter(null);
- setSelectedCommittee(null);
- return;
- }
-
- // Handle other filters
- if (selectedFilter === filter) {
- setSelectedFilter(null);
- setSelectedCommittee(null);
- } else {
- setSelectedFilter(filter!);
- setSelectedCommittee(null);
- }
- };
-
- const filteredEvents = (events: SHPEEvent[]): SHPEEvent[] => {
- // If no filter is selected, filter out hidden events and committee meetings
- if (!selectedFilter) {
- return events.filter(event =>
- (event.eventType !== EventType.COMMITTEE_MEETING || event.general) &&
- !event.hiddenEvent
- );
- }
-
- if (selectedFilter === 'myEvents') {
- return events.filter(event =>
- (userInfo?.publicInfo?.committees?.includes(event.committee || '') ||
- userInfo?.publicInfo?.interests?.includes(event.eventType || '')) &&
- !event.hiddenEvent
- );
- }
-
- if (selectedFilter === 'clubWide') {
- return events.filter(event => event.general && !event.hiddenEvent);
- }
- // Show hidden events for "Custom Event" filter
- if (selectedFilter === 'Custom Event') {
- return events.filter(event => event.eventType === selectedFilter);
- }
-
- if (selectedFilter === EventType.COMMITTEE_MEETING) {
- return events.filter(event =>
- event.eventType === EventType.COMMITTEE_MEETING &&
- (selectedCommittee ? event.committee === selectedCommittee : true) &&
- !event.hiddenEvent
- );
- }
-
- return events.filter(event => event.eventType === selectedFilter && !event.hiddenEvent);
- };
return (
@@ -205,99 +164,57 @@ const Events = ({ navigation }: EventsProps) => {
{/* Filters */}
-
-
- handleFilterSelect("myEvents")}
+ {
+ setFilter("main")
+ }}
+ >
+
- My Events
-
-
- handleFilterSelect("clubWide")}
+ Main
+
+
+
+ {
+ setFilter("intramural")
+ }}
+ >
+
- Club Wide
-
-
- {Object.values(EventType).map((type) => (
- handleFilterSelect(type)}
- >
- {type}
-
- ))}
-
-
-
- {/* Additional Committee Filter */}
- {selectedFilter === EventType.COMMITTEE_MEETING && (
-
-
- {committees.map(committee => (
- handleFilterSelect(undefined, committee.firebaseDocName)}
- >
- {committee.name}
-
- ))}
-
-
- )}
+ Intramural
+
+
+ {
+ setFilter("committee")
+ }}
+ >
+
+ Committee
+
+
+
{isLoading &&
@@ -307,100 +224,110 @@ const Events = ({ navigation }: EventsProps) => {
{/* Event Listings */}
{!isLoading && (
-
- {filteredEvents(todayEvents).length === 0 && filteredEvents(upcomingEvents).length === 0 && filteredEvents(pastEvents).length === 0 ? (
-
+
+ {selectedEvents.today.length === 0 &&
+ selectedEvents.upcoming.length === 0 &&
+ selectedEvents.past.length === 0 ? (
+
No Events
) : (
{/* Today's Events */}
- {filteredEvents(todayEvents).length !== 0 && (
-
+ {selectedEvents.today.length !== 0 && (
+
Today's Events
- {filteredEvents(todayEvents)?.map((event: SHPEEvent, index) => {
- return (
- 0 && "mt-8"}`}
- style={{
- shadowColor: "#000",
- shadowOffset: {
- width: 0,
- height: 2,
- },
- shadowOpacity: 0.25,
- shadowRadius: 3.84,
-
- elevation: 5,
- }}
- onPress={() => { navigation.navigate("EventInfo", { event: event }) }}
+ {selectedEvents.today.map((event: SHPEEvent, index) => (
+ 0 && "mt-8"}`}
+ style={{
+ shadowColor: "#000",
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.25,
+ shadowRadius: 3.84,
+ elevation: 5,
+ }}
+ onPress={() => {
+ navigation.navigate("EventInfo", { event: event });
+ }}
+ >
+
+
-
-
+
+ {truncateStringWithEllipsis(event.name, 20)}
+
+ {event.locationName ? (
+
+ {truncateStringWithEllipsis(event.locationName, 24)}
+
+ ) : null}
+
+ {formatTime(event.startTime?.toDate()!)}
+
+
+
+ {hasPrivileges && (
+ {
+ navigation.navigate("QRCode", { event: event });
+ }}
+ className="absolute right-0 top-0 p-2 m-2 rounded-full"
+ style={{ backgroundColor: "rgba(0,0,0,0.7)" }}
>
-
- {truncateStringWithEllipsis(event.name, 20)}
- {event.locationName ? (
- {truncateStringWithEllipsis(event.locationName, 24)}
- ) : null}
- {formatTime(event.startTime?.toDate()!)}
-
-
- {hasPrivileges && (
- { navigation.navigate("QRCode", { event: event }) }}
- className='absolute right-0 top-0 p-2 m-2 rounded-full'
- style={{ backgroundColor: 'rgba(0,0,0,0.7)' }}
- >
-
-
- )}
-
- );
- })}
+
+
+ )}
+
+ ))}
)}
{/* Upcoming Events */}
- {filteredEvents(upcomingEvents).length !== 0 && (
-
- Upcoming Events
- {filteredEvents(upcomingEvents)?.map((event: SHPEEvent, index) => {
- return (
- 0 && "mt-8"}`}>
-
-
- );
- })}
+ {selectedEvents.upcoming.length !== 0 && (
+
+
+ Upcoming Events
+
+ {selectedEvents.upcoming.map((event: SHPEEvent, index) => (
+ 0 && "mt-8"}`}>
+
+
+ ))}
)}
{/* Past Events */}
- {filteredEvents(pastEvents).length !== 0 && (
-
- Past Events
- {filteredEvents(pastEvents)?.map((event: SHPEEvent, index) => {
- return (
- 0 && "mt-8"}`}>
-
-
- );
- })}
+ {selectedEvents.past.length !== 0 && (
+
+
+ Past Events
+
+ {selectedEvents.past.map((event: SHPEEvent, index) => (
+ 0 && "mt-8"}`}>
+
+
+ ))}
)}
- {!selectedFilter && (
- navigation.navigate("PastEvents")}>
- View more
-
- )}
+ navigation.navigate("PastEvents")}>
+ View all past events
+
)}
@@ -464,7 +391,7 @@ const Events = ({ navigation }: EventsProps) => {
Event Location Check
- The location check only happens during scans; we do not track you continuously. You are free to leave the area, but if a sign-out scan is required, you must be at the location to sign out.
+ The location check only happens during scans; we do not track you continuously. If a sign-out scan is required, you must be at the location to sign out.
diff --git a/src/screens/events/FinalizeEvent.tsx b/src/screens/events/FinalizeEvent.tsx
index 5e9d678d..36d6de74 100644
--- a/src/screens/events/FinalizeEvent.tsx
+++ b/src/screens/events/FinalizeEvent.tsx
@@ -6,14 +6,21 @@ import { Octicons } from '@expo/vector-icons';
import { UserContext } from '../../context/UserContext';
import { RouteProp, useRoute } from '@react-navigation/core';
import { Images } from '../../../assets';
-import { MillisecondTimes, formatEventDate, formatEventTime } from '../../helpers/timeUtils';
+import { MillisecondTimes, formatEventDate, formatEventDateTime, formatEventTime } from '../../helpers/timeUtils';
import { StatusBar } from 'expo-status-bar';
import { handleLinkPress } from '../../helpers/links';
-import { SHPEEvent } from '../../types/events';
+import { EventType, SHPEEvent } from '../../types/events';
import { LinearGradient } from 'expo-linear-gradient';
import { reverseFormattedFirebaseName } from '../../types/committees';
import InteractButton from '../../components/InteractButton';
import { createEvent } from '../../api/firebaseUtils';
+import CalendarIconBlack from '../../../assets/calendar-days-solid_black.svg'
+import CalendarIconWhite from '../../../assets/calendar-days-solid_white.svg'
+import ClockIconBlack from '../../../assets/clock-solid_black.svg'
+import ClockIconWhite from '../../../assets/clock-solid_white.svg'
+import LocationDotIconBlack from '../../../assets/location-dot-solid_black.svg'
+import LocationDotIconWhite from '../../../assets/location-dot-solid_white.svg'
+
const FinalizeEvent = ({ navigation }: EventProps) => {
const route = useRoute();
@@ -30,6 +37,13 @@ const FinalizeEvent = ({ navigation }: EventProps) => {
const colorScheme = useColorScheme();
const darkMode = useSystemDefault ? colorScheme === 'dark' : fixDarkMode;
+ const isSameDay = (startDate: Date, endDate: Date): boolean => {
+ return startDate.getDate() === endDate.getDate() &&
+ startDate.getMonth() === endDate.getMonth() &&
+ startDate.getFullYear() === endDate.getFullYear();
+ };
+ const sameDay = startTime && endTime && isSameDay(startTime.toDate(), endTime.toDate());
+
return (
@@ -79,51 +93,57 @@ const FinalizeEvent = ({ navigation }: EventProps) => {
{/* General Details */}
{nationalConventionEligible && (
-
- This event is eligible for national convention requirements*
+ This event is eligible for national convention requirements*
+
+ )}
+
+ {(eventType === EventType.STUDY_HOURS) && (
+
+ Feel free to leave the area. Just be sure to scan in and out at the event location to fully earn your points!
)}
+
{name}
{eventType}
- {committee && (" • " + reverseFormattedFirebaseName(committee))} • {calculateMaxPossiblePoints(event)} points
-
-
- Hosted By {userInfo?.publicInfo?.name}
+ {committee && (" • " + reverseFormattedFirebaseName(committee))} • {calculateMaxPossiblePoints(event)} points • {userInfo?.publicInfo?.name}
- {/* Date, Time and Location */}
-
-
-
- Date
- {(startTime && endTime) ? formatEventDate(startTime.toDate(), endTime.toDate()) : ""}
-
-
- Time
- {startTime && endTime && formatEventTime(startTime.toDate(), endTime.toDate())}
+ {/* Date, Time and Location */}
+
+
+
+ {darkMode ? : }
+
+ {(startTime && endTime) ?
+ (sameDay ?
+ formatEventDate(startTime.toDate(), endTime.toDate())
+ : formatEventDateTime(startTime.toDate(), endTime.toDate())
+ )
+ : ""
+ }
+
+ {sameDay && (
+
+
+ {darkMode ? : }
+
+
+ {startTime && endTime && formatEventTime(startTime.toDate(), endTime.toDate())}
+
+
+ )}
+
{locationName && (
- Location
{geolocation ? (
{
handleLinkPress(`https://www.google.com/maps/dir/?api=1&destination=${geolocation.latitude},${geolocation.longitude}`);
}
}}
+ className='flex-row items-center'
>
-
+
+ {darkMode ? : }
+
+
{locationName}
) : (
-
+
{locationName}
)}
)}
+
+
{/* Description */}
{(description && description.trim() != "") && (
-
- About Event
+
+ Description
{description}
)}
@@ -173,9 +199,6 @@ const FinalizeEvent = ({ navigation }: EventProps) => {
}}
/>
-
-
-
)
}
diff --git a/src/screens/events/QRCodeScanningScreen.tsx b/src/screens/events/QRCodeScanningScreen.tsx
index ff153ecc..1427a659 100644
--- a/src/screens/events/QRCodeScanningScreen.tsx
+++ b/src/screens/events/QRCodeScanningScreen.tsx
@@ -1,15 +1,31 @@
-import { View, Text, Button, StyleSheet, TouchableHighlight, TouchableOpacity, Alert, Animated, Easing } from 'react-native';
+import { View, Text, TouchableOpacity, Animated, Easing, Dimensions, PixelRatio } from 'react-native';
import React, { useEffect, useRef, useState } from 'react';
-import { CameraView, Camera } from 'expo-camera';
+import { CameraView, Camera, BarcodeBounds } from 'expo-camera';
import { SafeAreaView } from 'react-native-safe-area-context';
-import { MainStackParams } from '../../types/navigation';
import { Octicons } from '@expo/vector-icons';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
+import { MainStackParams } from '../../types/navigation';
+
+const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
+
+type BarCodeScannedResult = {
+ type: string;
+ data: string;
+ bounds?: BarcodeBounds
+};
const QRCodeScanningScreen = ({ navigation }: NativeStackScreenProps) => {
const [hasCameraPermissions, setHasCameraPermissions] = useState(null);
- const [scanned, setScanned] = useState(false);
+ const [boxColor, setBoxColor] = useState('#FFFFFF');
+ const [validScanned, setValidScanned] = useState(false);
+
const pulseAnim = useRef(new Animated.Value(1)).current;
+ const boxTop = useRef(new Animated.Value((screenHeight / 2) - 240)).current;
+ const boxLeft = useRef(new Animated.Value((screenWidth / 2) - 120)).current;
+ const boxWidth = useRef(new Animated.Value(240)).current;
+ const boxHeight = useRef(new Animated.Value(240)).current;
+ const lastScale = useRef(1);
+
useEffect(() => {
const getBarCodeScannerPermissions = async () => {
@@ -27,13 +43,13 @@ const QRCodeScanningScreen = ({ navigation }: NativeStackScreenProps pulse());
};
@@ -41,27 +57,54 @@ const QRCodeScanningScreen = ({ navigation }: NativeStackScreenProps {
- console.log('Data Received', `Bar code with type ${type} and data ${data} has been scanned!`);
- setScanned(true);
- const dataRegex: RegExp = /^tamu-shpe:\/\/event\?id=[a-zA-z0-9]+&mode=(sign-in|sign-out)$/i;
- if (!dataRegex.test(data)) {
- Alert.alert("Invalid QR Code", "Either this QR Code is invalid or was misscanned. Please try again.", [
- {
- text: 'ok',
- onPress: () => {
- setScanned(false);
- }
- }
- ]);
+ const handleBarCodeScanned = ({ bounds, type, data }: BarCodeScannedResult) => {
+ if (validScanned) {
+ return;
}
- else {
- const linkVariables = data.split('?')[1].split('&');
- const id = linkVariables[0].split('=')[1];
- const mode = linkVariables[1].split('=')[1];
- if (id && mode === 'sign-in' || mode === 'sign-out') {
- navigation.navigate("EventVerificationScreen", { id, mode });
+
+ const dataRegex = /^tamu-shpe:\/\/event\?id=[a-zA-Z0-9]+&mode=(sign-in|sign-out)$/i;
+ if (dataRegex.test(data)) {
+ setValidScanned(true);
+ console.log('Data Received', `Bar code with type ${type} and data ${data} has been scanned!`);
+ if (bounds) {
+ setBoxColor('#FD652F');
+
+ Animated.parallel([
+ Animated.timing(boxTop, {
+ toValue: bounds.origin.y,
+ duration: 90,
+ easing: Easing.inOut(Easing.ease),
+ useNativeDriver: false,
+ }),
+ Animated.timing(boxLeft, {
+ toValue: bounds.origin.x,
+ duration: 90,
+ easing: Easing.inOut(Easing.ease),
+ useNativeDriver: false,
+ }),
+ Animated.timing(boxWidth, {
+ toValue: bounds.size.width,
+ duration: 90,
+ easing: Easing.inOut(Easing.ease),
+ useNativeDriver: false,
+ }),
+ Animated.timing(boxHeight, {
+ toValue: bounds.size.height,
+ duration: 90,
+ easing: Easing.inOut(Easing.ease),
+ useNativeDriver: false,
+ }),
+ ]).start();
}
+
+ setTimeout(() => {
+ const linkVariables = data.split('?')[1].split('&');
+ const id = linkVariables[0].split('=')[1];
+ const mode = linkVariables[1].split('=')[1];
+ if (id && (mode === 'sign-in' || mode === 'sign-out')) {
+ navigation.navigate('EventVerificationScreen', { id, mode });
+ }
+ }, 500);
}
};
@@ -74,34 +117,53 @@ const QRCodeScanningScreen = ({ navigation }: NativeStackScreenProps
- {/* Header */}
Scanner
- navigation.goBack()} >
+ navigation.goBack()}>
-
- {/* Pulsing Effect */}
-
-
-
-
-
-
-
-
-
-
+ {/* Pulsing Effect with Animated Transition */}
+
+
+
+
+
+
+
+
@@ -112,4 +174,4 @@ const QRCodeScanningScreen = ({ navigation }: NativeStackScreenProps) =>
- navigation.getParent()?.navigate('EventsTab', { screen: 'EventsScreen', params: { filter: 'myEvents' } })}
- >
- View all
-