Skip to content

Commit

Permalink
feat: pull to refresh functionality with RefreshControl
Browse files Browse the repository at this point in the history
  • Loading branch information
dlustre committed Jun 17, 2024
1 parent 5edcdd7 commit 7716b7c
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 108 deletions.
14 changes: 7 additions & 7 deletions apps/expo/app.config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import type { ExpoConfig } from "expo/config";
import type { ConfigContext, ExpoConfig } from "expo/config";

const image = "./assets/zotmeal.png";
const name = "ZotMeal";
const backgroundColor = "#161B22";

const defineConfig = (): ExpoConfig => ({
name: "expo",
slug: "expo",
scheme: "expo",
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
name,
slug: name.toLowerCase(),
scheme: name.toLowerCase(),
version: "0.1.0",
orientation: "portrait",
icon: image,
Expand Down Expand Up @@ -43,5 +45,3 @@ const defineConfig = (): ExpoConfig => ({
},
plugins: ["expo-router", "expo-font"],
});

export default defineConfig;
20 changes: 20 additions & 0 deletions apps/expo/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/** @type {import("@babel/core").ConfigFunction} */
module.exports = function (api) {
api.cache(true);
return {
presets: ["babel-preset-expo", "@babel/preset-typescript"],
plugins: [
[
"@tamagui/babel-plugin",
{
components: ["tamagui"],
config: "./tamagui.config.ts",
logTimings: true,
disableExtraction: process.env.NODE_ENV === "development",
},
],
// NOTE: this is only necessary if you are using reanimated for animations
"react-native-reanimated/plugin",
],
};
};
53 changes: 3 additions & 50 deletions apps/expo/src/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,77 +2,30 @@ import { config } from "@tamagui/config/v3";

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

import { useState } from "react";
import { Platform } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useFonts } from "expo-font";
import { Stack } from "expo-router";
import { StatusBar } from "expo-status-bar";
import { ClerkProvider } from "@clerk/clerk-expo";
import { Info } from "@tamagui/lucide-icons";
import { ToastProvider, ToastViewport } from "@tamagui/toast";
import {
Button,
createTamagui,
TamaguiProvider,
Text,
Theme,
View,
} from "tamagui";
import { createTamagui, TamaguiProvider, Theme } from "tamagui";

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

const tamaguiConfig = createTamagui(config);

const DevInfo = () => {
const [open, setOpen] = useState(false);

return (
<View
onPress={() => setOpen(!open)}
zIndex={10}
width={open ? "60%" : "10%"}
height={open ? "25%" : "10%"}
position="absolute"
bottom={30}
right={10}
>
{open ? (
<Button
unstyled
width="100%"
height="100%"
backgroundColor="red"
padding="$4"
>
<Text fontSize="$3" fontWeight="bold">
Platform.OS: {Platform.OS}
{"\n"}
baseurl: {getBaseUrl()}
{"\n"}
env: {JSON.stringify(env, null, 2)}
</Text>
</Button>
) : (
<Info size="$5" color="red" />
)}
</View>
);
};

export default function RootLayout() {
const [loaded] = useFonts({
Inter: require("@tamagui/font-inter/otf/Inter-Medium.otf"),
InterBold: require("@tamagui/font-inter/otf/Inter-Bold.otf"),
});

const colorScheme = useZotmealColorScheme();
const { top, ...insets } = useSafeAreaInsets();
const insets = useSafeAreaInsets();

if (!loaded) return null;

Expand Down
65 changes: 35 additions & 30 deletions apps/expo/src/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React from "react";
import { Platform } from "react-native";
import { AlertTriangle, RefreshCw } from "@tamagui/lucide-icons";
import { addDays, isWithinInterval } from "date-fns";
import { Platform, RefreshControl } from "react-native";
import { AlertTriangle } from "@tamagui/lucide-icons";
import { isWithinInterval } from "date-fns";
import {
Button,
ScrollView,
Spinner,
Tabs,
Expand All @@ -23,7 +22,7 @@ import { UniversalDatePicker } from "../components/ui/UniversalDatePicker";

export default function Home() {
const theme = useTheme();
const [date, setDate] = React.useState(addDays(new Date(), -1));
const [date, setDate] = React.useState<Date>(new Date());
const [period, setPeriod] = React.useState<string | null>(null);
const [restaurant, setRestaurant] =
React.useState<keyof ZotmealData>("brandywine");
Expand Down Expand Up @@ -68,9 +67,13 @@ export default function Home() {
);

// ! Not sure if this is actually working but we do want debouncing for the refresh button
const refetchWithDebounce = useDebounce(() => query.refetch(), 1000, {
leading: true,
});
const refetchWithDebounce = useDebounce(
async () => await query.refetch(),
1000,
{
leading: true,
},
);

// const toast = useToastController();
// useEffect(() => {
Expand All @@ -87,12 +90,13 @@ export default function Home() {
// }, [data, toast]);
// TODO: show a toast if there is an error

const brandywineMenuAtPeriod = brandywineInfo?.menus.find(
// Get the stations for the current period
const brandywineStations = brandywineInfo?.menus.find(
(menu) => menu.period.name === period,
);
const anteateryMenuAtPeriod = anteateryInfo?.menus.find(
)?.stations;
const anteateryStations = anteateryInfo?.menus.find(
(menu) => menu.period.name === period,
);
)?.stations;

// TODO: make it not possible to click into the menu if it's loading
const MenuContent = () => (
Expand All @@ -113,8 +117,8 @@ export default function Home() {
marginVertical={200}
/>
) : null}
{brandywineInfo && brandywineMenuAtPeriod ? (
<StationTabs stations={brandywineMenuAtPeriod?.stations} />
{brandywineInfo && brandywineStations ? (
<StationTabs stations={brandywineStations} />
) : query.isPending ? null : (
<View alignItems="center">
<AlertTriangle size="$10" />
Expand All @@ -138,8 +142,8 @@ export default function Home() {
marginVertical={200}
/>
) : null}
{anteateryInfo && anteateryMenuAtPeriod ? (
<StationTabs stations={anteateryMenuAtPeriod?.stations} />
{anteateryInfo && anteateryStations ? (
<StationTabs stations={anteateryStations} />
) : query.isPending ? null : (
<View alignItems="center">
<AlertTriangle size="$10" />
Expand All @@ -157,7 +161,20 @@ export default function Home() {
anteateryStatus={currentAnteateryPeriod ? "open" : "closed"}
brandywineStatus={currentBrandywinePeriod ? "open" : "closed"}
>
<ScrollView>
<ScrollView
refreshControl={
<RefreshControl
/**
* ! The indicator is hidden for now since it doesn't seem to show up on state change for the date.
* If there is a way to show this indicator when the user changes the date, we can use this indicator instead.
*
* @see https://github.com/facebook/react-native/issues/24855
*/
refreshing={false}
onRefresh={refetchWithDebounce}
/>
}
>
<EventToast />

<XStack
Expand All @@ -170,19 +187,7 @@ export default function Home() {
setPeriod={setPeriod}
color={theme.color?.val as string}
/>
<UniversalDatePicker
date={date}
setDate={(date) => {
setDate(date);
refetchWithDebounce();
}}
/>
<Button
disabled={query.isLoading}
opacity={query.isLoading ? 0.5 : 1}
onPress={refetchWithDebounce}
icon={<RefreshCw />}
/>
<UniversalDatePicker date={date} setDate={setDate} />
</XStack>
{/*
<ScrollView horizontal>
Expand Down
4 changes: 2 additions & 2 deletions apps/expo/src/app/item/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ export default function MenuItem() {

const NutritionFacts = ({
nutritionInfo,
}: {
}: Readonly<{
nutritionInfo: NutritionInfo;
}) => (
}>) => (
<YStack
padding="$3"
borderWidth={2}
Expand Down
1 change: 0 additions & 1 deletion apps/expo/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export * from "./navigation";
export * from "./ui";
export * from "./menu";
43 changes: 43 additions & 0 deletions apps/expo/src/components/ui/DevInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from "react";
import { Platform } from "react-native";
import { Info } from "@tamagui/lucide-icons";
import { Button, Text, View } from "tamagui";

import { getBaseUrl } from "~/utils/api";
import { env } from "~/utils/env";

export function DevInfo() {
const [open, setOpen] = React.useState(false);

return (
<View
onPress={() => setOpen(!open)}
zIndex={10}
width={open ? "60%" : "10%"}
height={open ? "25%" : "10%"}
position="absolute"
bottom={30}
right={10}
>
{open ? (
<Button
unstyled
width="100%"
height="100%"
backgroundColor="red"
padding="$4"
>
<Text fontSize="$3" fontWeight="bold">
Platform.OS: {Platform.OS}
{"\n"}
baseurl: {getBaseUrl()}
{"\n"}
env: {JSON.stringify(env, null, 2)}
</Text>
</Button>
) : (
<Info size="$5" color="red" />
)}
</View>
);
}
8 changes: 4 additions & 4 deletions apps/expo/src/components/ui/UniversalDatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ export const UniversalDatePicker = ({
date,
setDate,
}: Readonly<{ date: Date; setDate: (date: Date) => void }>) => {
const [showDatePicker, setShowDatePicker] = useState<boolean>(false);
const [showDatePicker, setShowDatePicker] = useState<boolean>(
Platform.OS === "ios",
);

return (
<>
Expand All @@ -41,9 +43,7 @@ export const UniversalDatePicker = ({
onChange={(_, selectedDate) => {
// hide date picker on android
setShowDatePicker(Platform.OS === "ios");
if (selectedDate) {
setDate(selectedDate);
}
if (selectedDate) setDate(selectedDate);
}}
/>
)}
Expand Down
1 change: 1 addition & 0 deletions apps/expo/src/components/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from "./UniversalDatePicker";
export * from "./EventToast";
export * from "./DishCard";
export * from "./StationTabs";
export * from "./DevInfo";
16 changes: 10 additions & 6 deletions apps/expo/src/hooks/useWarmUpBrowser.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import React from "react";
import { Platform } from "react-native";
import * as WebBrowser from "expo-web-browser";

export const useWarmUpBrowser = () => {
React.useEffect(() => {
void WebBrowser.warmUpAsync();
return () => void WebBrowser.coolDownAsync();
}, []);
};
export const useWarmUpBrowser =
Platform.OS === "web"
? () => {}
: () => {
React.useEffect(() => {
void WebBrowser.warmUpAsync();
return () => void WebBrowser.coolDownAsync();
}, []);
};
1 change: 0 additions & 1 deletion apps/expo/src/hooks/useWarmUpBrowser.web.tsx

This file was deleted.

8 changes: 4 additions & 4 deletions apps/server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ For detailed instructions, please refer to the [documentation](https://www.serve

Depending on your preferred package manager, follow the instructions below to deploy your project.

> **Requirements**: NodeJS `lts/fermium (v.14.15.0)`. If you're using [nvm](https://github.com/nvm-sh/nvm), run `nvm use` to ensure you're using the same Node version in local and in your lambda's runtime.
> **Requirements**: Node version: `lts/*`. If you're using [nvm](https://github.com/nvm-sh/nvm), run `nvm use` to ensure you're using the same Node version in local and in your lambda's runtime.
### Using NPM

Expand Down Expand Up @@ -43,7 +43,7 @@ Check the [sls invoke local command documentation](https://www.serverless.com/fr

Copy and replace your `url` - found in Serverless `deploy` command output - and `name` parameter in the following `curl` command in your terminal or in Postman to test your newly deployed application.

```
```sh
curl --location --request POST 'https://myApiEndpoint/dev/hello' \
--header 'Content-Type: application/json' \
--data-raw '{
Expand All @@ -60,7 +60,7 @@ The project code base is mainly located within the `src` folder. This folder is
- `functions` - containing code base and configuration for your lambda functions
- `libs` - containing shared code base between your lambdas

```
```sh
.
├── src
│ ├── functions # Lambda configuration and source code folder
Expand All @@ -87,7 +87,7 @@ The project code base is mainly located within the `src` folder. This folder is
### 3rd party libraries

- [json-schema-to-ts](https://github.com/ThomasAribart/json-schema-to-ts) - uses JSON-Schema definitions used by API Gateway for HTTP request validation to statically generate TypeScript types in your lambda's handler code base
- [middy](https://github.com/middyjs/middy) - middleware engine for Node.Js lambda. This template uses [http-json-body-parser](https://github.com/middyjs/middy/tree/master/packages/http-json-body-parser) to convert API Gateway `event.body` property, originally passed as a stringified JSON, to its corresponding parsed object
- [middy](https://github.com/middyjs/middy) - middleware engine for Node.js lambda. This template uses [http-json-body-parser](https://github.com/middyjs/middy/tree/master/packages/http-json-body-parser) to convert API Gateway `event.body` property, originally passed as a stringified JSON, to its corresponding parsed object
- [@serverless/typescript](https://github.com/serverless/typescript) - provides up-to-date TypeScript definitions for your `serverless.ts` service file

### Advanced usage
Expand Down
Loading

0 comments on commit 7716b7c

Please sign in to comment.