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

Frontend changes branched #456

Merged
merged 11 commits into from
Jun 4, 2024
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20.12.1
lts/*
9 changes: 9 additions & 0 deletions apps/expo/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Config } from "jest";

export default {
verbose: true,
preset: "jest-expo",
transformIgnorePatterns: [
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)",
],
} satisfies Config;
11 changes: 10 additions & 1 deletion apps/expo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"ios": "expo run:ios",
"format": "prettier --check . --ignore-path ../../.gitignore",
"lint": "eslint .",
"_test": "jest",
"typecheck": "tsc --noEmit"
},
"dependencies": {
Expand Down Expand Up @@ -46,6 +47,7 @@
"expo-splash-screen": "~0.26.4",
"expo-status-bar": "~1.11.1",
"react": "18.2.0",
"react-datepicker": "^6.9.0",
"react-dom": "18.2.0",
"react-native": "~0.73.6",
"react-native-css-interop": "~0.0.34",
Expand All @@ -62,15 +64,22 @@
"devDependencies": {
"@babel/core": "^7.24.0",
"@babel/preset-env": "^7.24.0",
"@babel/preset-typescript": "^7.24.6",
"@babel/runtime": "^7.24.0",
"@jest/globals": "^29.7.0",
"@testing-library/react-native": "^12.5.1",
"@types/react-datepicker": "^6.2.0",
"@zotmeal/eslint-config": "workspace:^0.2.0",
"@zotmeal/prettier-config": "workspace:^0.1.0",
"@zotmeal/tailwind-config": "workspace:^0.1.0",
"@zotmeal/tsconfig": "workspace:^0.1.0",
"@zotmeal/ui": "workspace:^",
"eslint": "^8.57.0",
"jest": "^29.7.0",
"jest-expo": "^50.0.4",
"prettier": "^3.2.5",
"react-test-renderer": "^18.2.0",
"tailwindcss": "^3.4.3",
"ts-jest": "^29.1.4",
"typescript": "^5.4.3"
},
"eslintConfig": {
Expand Down
10 changes: 10 additions & 0 deletions apps/expo/src/__tests__/app.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from "react";
import renderer from "react-test-renderer";
import { Logo } from "~/components";

describe("<App />", () => {
it("has 1 child", () => {
const tree = renderer.create(<Logo />).toJSON();
expect(tree.children.length).toBe(1);
});
});
35 changes: 6 additions & 29 deletions apps/expo/src/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,36 @@ import { config } from "@tamagui/config/v3";

import "@tamagui/core/reset.css";

import type { TokenCache } from "@clerk/clerk-expo/dist/cache";
import type { FontSource } from "expo-font";
import { useColorScheme } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useFonts } from "expo-font";
import { Stack } from "expo-router";
import * as SecureStore from "expo-secure-store";
import { StatusBar } from "expo-status-bar";
import { ClerkProvider } from "@clerk/clerk-expo";
import InterBold from "@tamagui/font-inter/otf/Inter-Bold.otf";
import Inter from "@tamagui/font-inter/otf/Inter-Medium.otf";
import { ToastProvider, ToastViewport } from "@tamagui/toast";
import { createTamagui, TamaguiProvider, Theme } from "tamagui";

import Logo from "~/components/Logo";
import { Logo } from "~/components";
import { HamburgerMenu } from "~/components/navigation/HamburgerMenu";
import { TRPCProvider } from "~/utils";
import { TRPCProvider, useZotmealColorScheme } from "~/utils";
import { tokenCache } from "~/utils/tokenCache";
import { env } from "../utils/env";

// Main layout of the app
// It wraps your pages with the providers they need

const tamaguiConfig = createTamagui(config);

const tokenCache: TokenCache = {
async getToken(key: string) {
try {
return SecureStore.getItemAsync(key);
} catch (err) {
return null;
}
},
async saveToken(key: string, value: string) {
try {
await SecureStore.setItemAsync(key, value);
} catch (err) {
console.error(err);
}
},
};

export default function RootLayout() {
const [loaded] = useFonts({
Inter: Inter as FontSource,
InterBold: InterBold as FontSource,
});

const colorScheme = useColorScheme();
const colorScheme = useZotmealColorScheme();

const { bottom, left, right } = useSafeAreaInsets();

if (!loaded) {
return null;
}
if (!loaded) return null;

return (
<TRPCProvider>
Expand Down
7 changes: 3 additions & 4 deletions apps/expo/src/app/events/event/[title].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ import {
Separator,
Square,
Text,
View,
XStack,
YStack,
} from "tamagui";

import type { Event } from "@zotmeal/db";

import useZotmealStore from "~/utils/useZotmealStore";
import { useZotmealStore } from "~/utils";

export default function Event() {
const { title } = useGlobalSearchParams();
Expand Down Expand Up @@ -57,9 +58,6 @@ export default function Event() {
justifyContent: "center",
alignItems: "center",
}}
contentInset={{
bottom: 100,
}}
>
<YStack
justifyContent="center"
Expand Down Expand Up @@ -130,6 +128,7 @@ export default function Event() {
</Accordion.Content>
</Accordion.Item>
</Accordion>
<View height={100} />
</ScrollView>
</>
);
Expand Down
54 changes: 30 additions & 24 deletions apps/expo/src/app/events/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { useEffect } from "react";
import { Link } from "expo-router";
import { CalendarX2 } from "@tamagui/lucide-icons";
import { format } from "date-fns";
import { H3, Image, Tabs, Text, YStack } from "tamagui";
import { H3, Image, Spinner, Tabs, Text, View, YStack } from "tamagui";

import type { Event } from "@zotmeal/db";
import { getRestaurantNameById } from "@zotmeal/utils";

import { RestaurantTabs } from "~/components";
import { useZotmealStore } from "~/utils";
import { api } from "~/utils/api";
import useZotmealStore from "~/utils/useZotmealStore";

// Create a context for events, default value is a test event
const _testData = {
Expand Down Expand Up @@ -95,32 +96,37 @@ export default function Events() {
setBrandywineEvents(brandywineEvents);
}, [eventsQuery.data, setAnteateryEvents, setBrandywineEvents]);

if (eventsQuery?.isLoading) {
return <Text>Loading...</Text>;
}
// TODO: show a toast if there is an error
if (eventsQuery?.isError) console.error(eventsQuery.error);

if (eventsQuery?.isError) {
return <Text>Error: {eventsQuery.error.message}</Text>;
}

if (!anteateryEvents || !brandywineEvents) {
return <Text>No events found</Text>;
}
const EventsContent = () =>
eventsQuery.isLoading ? (
<Spinner size="large" marginTop="$10" />
) : brandywineEvents && anteateryEvents ? (
<>
{[brandywineEvents, anteateryEvents].map((events, index) => (
<Tabs.Content
key={index}
value={getRestaurantNameById(index === 0 ? "3314" : "3056")}
>
<YStack>
{events.map((event, index) => (
<EventCard key={index} event={event} />
))}
</YStack>
</Tabs.Content>
))}
</>
) : (
<View alignItems="center">
<CalendarX2 size="$10" />
<Text>Events not found</Text>
</View>
);

return (
<RestaurantTabs>
{[brandywineEvents, anteateryEvents].map((events, index) => (
<Tabs.Content
key={index}
value={getRestaurantNameById(index === 0 ? "3314" : "3056")}
>
<YStack>
{events.map((event, index) => (
<EventCard key={index} event={event} />
))}
</YStack>
</Tabs.Content>
))}
<EventsContent />
</RestaurantTabs>
);
}
52 changes: 52 additions & 0 deletions apps/expo/src/app/home/_components/date-picker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { useState } from "react";
import { Platform } from "react-native";
import DateTimePicker from "@react-native-community/datetimepicker";
import { CalendarDays } from "@tamagui/lucide-icons";
import { endOfWeek, startOfWeek } from "date-fns";
import { Button } from "tamagui";

/**
* Native date picker for iOS and Android.
*
* Platform handling from an issue thread:
*
* @see https://github.com/react-native-datetimepicker/datetimepicker/issues/54
*/
export const UniversalDatePicker = ({
date,
setDate,
}: Readonly<{ date: Date; setDate: (date: Date) => void }>) => {
const [showDatePicker, setShowDatePicker] = useState<boolean>(true);

return (
<>
{Platform.OS === "android" && (
<Button
onPress={() => setShowDatePicker(true)}
icon={CalendarDays}
scaleIcon={1.5}
size="$5"
borderRadius="$10"
pressTheme
>
{date.toLocaleDateString("en-US")}
</Button>
)}
{showDatePicker && (
<DateTimePicker
value={date}
mode="date"
minimumDate={startOfWeek(new Date())}
maximumDate={endOfWeek(new Date())}
onChange={(_, selectedDate) => {
// hide date picker on android
setShowDatePicker(Platform.OS === "ios");
if (selectedDate) {
setDate(selectedDate);
}
}}
/>
)}
</>
);
};
49 changes: 49 additions & 0 deletions apps/expo/src/app/home/_components/date-picker.web.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from "react";
import { endOfWeek, startOfWeek } from "date-fns";
import DatePicker from "react-datepicker";

import "react-datepicker/dist/react-datepicker.css";

import { CalendarDays } from "@tamagui/lucide-icons";
import { Button, ButtonProps, TamaguiElement } from "tamagui";

interface UniversalDatePickerProps {
date: Date;
setDate: (date: Date) => void;
}

interface CustomInputProps {
value: HTMLInputElement["value"];
onClick: ButtonProps["onPress"];
}

/**
* Universal date picker for web.
*/
export const UniversalDatePicker = ({
date,
setDate,
}: Readonly<UniversalDatePickerProps>) => {
/**
* Courtesy of issue thread:
* @see https://github.com/Hacker0x01/react-datepicker/issues/2165#issuecomment-711032947
*/
const CustomInput = (
{ value, onClick }: CustomInputProps,
ref: React.Ref<TamaguiElement>,
) => (
<Button icon={CalendarDays} onPress={onClick} ref={ref}>
{value}
</Button>
);

return (
<DatePicker
customInput={React.createElement(React.forwardRef(CustomInput))}
selected={date}
minDate={startOfWeek(new Date())}
maxDate={endOfWeek(new Date())}
onChange={(prev) => setDate(prev ?? new Date())}
/>
);
};
Loading
Loading