diff --git a/app/app.tsx b/app/app.tsx index c753b4b..1655021 100644 --- a/app/app.tsx +++ b/app/app.tsx @@ -12,28 +12,17 @@ import "./i18n" import "./utils/ignoreWarnings" import { useFonts } from "expo-font" -import React, { useLayoutEffect, useState } from "react" -import { - initialWindowMetrics, - SafeAreaProvider, - useSafeAreaInsets, -} from "react-native-safe-area-context" -import * as Updates from "expo-updates" +import React, { useLayoutEffect } from "react" +import { initialWindowMetrics, SafeAreaProvider } from "react-native-safe-area-context" import { AppNavigator, useNavigationPersistence } from "./navigators" import { ErrorBoundary } from "./screens/ErrorScreen/ErrorBoundary" import * as storage from "./utils/storage" -import { colors, customFontsToLoad, spacing } from "./theme" +import { customFontsToLoad } from "./theme" import { setupReactotron } from "./services/reactotron/reactotron" import Config from "./config" import { QueryClientProvider } from "@tanstack/react-query" -import messaging from "@react-native-firebase/messaging" -import Toast, { BaseToast, ToastConfig } from "react-native-toast-message" -import { $baseSecondaryStyle, $baseStyle } from "./components" -import { Dimensions, ViewStyle } from "react-native" import { queryClient } from "./services/api/react-query" -import { reportCrash } from "./utils/crashReporting" -import { useAppState } from "./hooks" -import { Modal } from "./components/Modal" +import { CustomToast, OTAUpdates } from "./components" // Set up Reactotron, which is a free desktop app for inspecting and debugging // React Native apps. Learn more here: https://github.com/infinitered/reactotron @@ -56,96 +45,6 @@ interface AppProps { hideSplashScreen: () => Promise } -// Setting up our custom Toast component -const CustomToast = () => { - const insets = useSafeAreaInsets() - - useLayoutEffect(() => { - // handle a new push notification received while the app is in "foreground" state - const unsubscribe = messaging().onMessage(async (remoteMessage) => { - if ( - remoteMessage.notification && - (remoteMessage.notification.title || remoteMessage.notification.body) - ) { - Toast.show({ - text1: remoteMessage.notification.title, - text2: remoteMessage.notification.body, - }) - } - }) - return unsubscribe - }) - - const toastConfig: ToastConfig = { - success: (props) => ( - - ), - } - - return -} - -// Setting up our OTA Updates component -const OTAUpdates = () => { - const [isModalVisible, setIsModalVisible] = useState(false) - const [isUpdating, setIsUpdating] = useState(false) - - async function fetchAndRestartApp() { - const fetchUpdate = await Updates.fetchUpdateAsync() - if (fetchUpdate.isNew) { - await Updates.reloadAsync() - } else { - setIsModalVisible(false) - setIsUpdating(false) - reportCrash("Fetch Update failed") - } - } - - async function onFetchUpdateAsync() { - if (__DEV__ || process.env.NODE_ENV === "development") return - try { - const update = await Updates.checkForUpdateAsync() - setIsModalVisible(update.isAvailable) - } catch (error) { - reportCrash(error) - } - } - - useAppState({ - match: /background/, - nextAppState: "active", - callback: onFetchUpdateAsync, - }) - - return ( - { - setIsUpdating(true) - await fetchAndRestartApp() - }, - label: isUpdating ? "ota.confirmLabelUpdating" : "ota.confirmLabel", - disabled: isUpdating, - }} - cancelOnPress={{ - cta: () => { - setIsModalVisible(false) - }, - label: "ota.cancelLabel", - }} - isVisible={isModalVisible} - /> - ) -} - /** * This is the root component of our app. */ @@ -196,15 +95,3 @@ function App(props: AppProps) { } export default App - -const $toast: ViewStyle = { - backgroundColor: colors.palette.neutral400, - borderLeftWidth: 0, - borderRadius: spacing.extraSmall, - width: Dimensions.get("window").width - spacing.extraSmall * 2, -} - -const $toastContainer: ViewStyle = { - paddingHorizontal: spacing.large, - paddingVertical: spacing.medium, -} diff --git a/app/components/CustomToast.tsx b/app/components/CustomToast.tsx new file mode 100644 index 0000000..3b06b64 --- /dev/null +++ b/app/components/CustomToast.tsx @@ -0,0 +1,54 @@ +import React, { useLayoutEffect } from "react" +import { useSafeAreaInsets } from "react-native-safe-area-context" +import { colors, spacing } from "../theme" +import messaging from "@react-native-firebase/messaging" +import Toast, { BaseToast, ToastConfig } from "react-native-toast-message" +import { $baseSecondaryStyle, $baseStyle } from "./" +import { Dimensions, ViewStyle } from "react-native" + +// Setting up our custom Toast component +export const CustomToast = () => { + const insets = useSafeAreaInsets() + + useLayoutEffect(() => { + // handle a new push notification received while the app is in "foreground" state + const unsubscribe = messaging().onMessage(async (remoteMessage) => { + if ( + remoteMessage.notification && + (remoteMessage.notification.title || remoteMessage.notification.body) + ) { + Toast.show({ + text1: remoteMessage.notification.title, + text2: remoteMessage.notification.body, + }) + } + }) + return unsubscribe + }) + + const toastConfig: ToastConfig = { + success: (props) => ( + + ), + } + + return +} + +const $toast: ViewStyle = { + backgroundColor: colors.palette.neutral400, + borderLeftWidth: 0, + borderRadius: spacing.extraSmall, + width: Dimensions.get("window").width - spacing.extraSmall * 2, +} + +const $toastContainer: ViewStyle = { + paddingHorizontal: spacing.large, + paddingVertical: spacing.medium, +} diff --git a/app/components/OTAUpdates.tsx b/app/components/OTAUpdates.tsx new file mode 100644 index 0000000..e1649a7 --- /dev/null +++ b/app/components/OTAUpdates.tsx @@ -0,0 +1,60 @@ +import React, { useState } from "react" +import * as Updates from "expo-updates" +import { reportCrash } from "../utils/crashReporting" +import { useAppState } from "../hooks" +import { Modal } from "./Modal" + +// Setting up our OTA Updates component +export const OTAUpdates = () => { + const [isModalVisible, setIsModalVisible] = useState(false) + const [isUpdating, setIsUpdating] = useState(false) + + async function fetchAndRestartApp() { + const fetchUpdate = await Updates.fetchUpdateAsync() + if (fetchUpdate.isNew) { + await Updates.reloadAsync() + } else { + setIsModalVisible(false) + setIsUpdating(false) + reportCrash("Fetch Update failed") + } + } + + async function onFetchUpdateAsync() { + if (__DEV__ || process.env.NODE_ENV === "development") return + try { + const update = await Updates.checkForUpdateAsync() + setIsModalVisible(update.isAvailable) + } catch (error) { + reportCrash(error) + } + } + + useAppState({ + match: /background/, + nextAppState: "active", + callback: onFetchUpdateAsync, + }) + + return ( + { + setIsUpdating(true) + await fetchAndRestartApp() + }, + label: isUpdating ? "ota.confirmLabelUpdating" : "ota.confirmLabel", + disabled: isUpdating, + }} + cancelOnPress={{ + cta: () => { + setIsModalVisible(false) + }, + label: "ota.cancelLabel", + }} + isVisible={isModalVisible} + /> + ) +} diff --git a/app/components/index.ts b/app/components/index.ts index 48b5c40..d746c10 100644 --- a/app/components/index.ts +++ b/app/components/index.ts @@ -5,12 +5,15 @@ export * from "./Button" export * from "./ButtonLink" export * from "./Card" export * from "./carousel/Carousel" +export * from "./CustomToast" export * from "./EmptyState" export * from "./Header" export * from "./Icon" export * from "./IconButton" export * from "./ListItem" export * from "./MediaButton" +export * from "./Modal" +export * from "./OTAUpdates" export * from "./Screen" export * from "./ScrollToButton" export * from "./Tag" diff --git a/app/screens/ChatScreen/ChatScreen.tsx b/app/screens/ChatScreen/ChatScreen.tsx index 2e9b941..14bc6f2 100644 --- a/app/screens/ChatScreen/ChatScreen.tsx +++ b/app/screens/ChatScreen/ChatScreen.tsx @@ -5,6 +5,7 @@ import { translate } from "../../i18n" import { GiftedChat, IMessage } from "react-native-gifted-chat" import AsyncStorage from "@react-native-async-storage/async-storage" import { aiPrompt } from "./ai" +import { View, ViewStyle } from "react-native" const NUMBER_OF_MESSAGES_TO_SEND = 8 const chatbotAvatarURL = @@ -87,51 +88,59 @@ export const ChatScreen: React.FunctionComponent> = () => return ( <> {uuid && ( - { - const appendedMessages = [ - ...newMessages.map((message) => ({ ...message, _id: Date.now() })), - ...messages, - ] - saveMessages(() => appendedMessages) - setTypingIndicator(true) - - // ask Claude for AI response - // first, build the prompt using the last 15 messages - const prompt = appendedMessages - .slice(0, NUMBER_OF_MESSAGES_TO_SEND) - .reverse() - .map( - (message) => (message.user._id === uuid ? "Human: " : "Assistant: ") + message.text, - ) - .join("\n\n") - - // turn on the GiftedChat "typing" indicator - // then, ask Claude for a response - const response = await aiPrompt({ prompt, userId: uuid }) - const claudeMessage = { - _id: Date.now(), - text: response.completion.trim(), - createdAt: new Date(), - user: { - _id: 2, - name: chatbotName, - avatar: chatbotAvatarURL, - }, - } - setTypingIndicator(false) - saveMessages(() => [claudeMessage, ...appendedMessages]) - }} - user={{ - _id: uuid, - }} - textInputProps={{ - testID: "aiChatInput", - }} - /> + + { + const appendedMessages = [ + ...newMessages.map((message) => ({ ...message, _id: Date.now() })), + ...messages, + ] + saveMessages(() => appendedMessages) + setTypingIndicator(true) + + // ask Claude for AI response + // first, build the prompt using the last 15 messages + const prompt = appendedMessages + .slice(0, NUMBER_OF_MESSAGES_TO_SEND) + .reverse() + .map( + (message) => + (message.user._id === uuid ? "Human: " : "Assistant: ") + message.text, + ) + .join("\n\n") + + // turn on the GiftedChat "typing" indicator + // then, ask Claude for a response + const response = await aiPrompt({ prompt, userId: uuid }) + const claudeMessage = { + _id: Date.now(), + text: response.completion.trim(), + createdAt: new Date(), + user: { + _id: 2, + name: chatbotName, + avatar: chatbotAvatarURL, + }, + } + setTypingIndicator(false) + saveMessages(() => [claudeMessage, ...appendedMessages]) + }} + user={{ + _id: uuid, + }} + textInputProps={{ + testID: "aiChatInput", + }} + listViewProps={{ keyboardDismissMode: "on-drag" }} + /> + )} ) } + +const $root: ViewStyle = { + flex: 1, +} diff --git a/app/screens/InfoScreen/CodeOfConductScreen.tsx b/app/screens/InfoScreen/CodeOfConductScreen.tsx index c017a69..0116bff 100644 --- a/app/screens/InfoScreen/CodeOfConductScreen.tsx +++ b/app/screens/InfoScreen/CodeOfConductScreen.tsx @@ -72,6 +72,5 @@ const $phoneNumber: TextStyle = { } const $codeOfConductHeading: TextStyle = { - marginTop: spacing.extraLarge * 2, marginBottom: spacing.extraSmall, } diff --git a/app/screens/InfoScreen/CreditsScreen.tsx b/app/screens/InfoScreen/CreditsScreen.tsx index 1fe8eea..3ea9cb1 100644 --- a/app/screens/InfoScreen/CreditsScreen.tsx +++ b/app/screens/InfoScreen/CreditsScreen.tsx @@ -141,7 +141,6 @@ const $container: ViewStyle = { } const $heading: TextStyle = { - marginTop: spacing.extraLarge, paddingHorizontal: spacing.large, marginBottom: spacing.large, } diff --git a/app/screens/InfoScreen/InfoScreen.tsx b/app/screens/InfoScreen/InfoScreen.tsx index de364a9..8d536c7 100644 --- a/app/screens/InfoScreen/InfoScreen.tsx +++ b/app/screens/InfoScreen/InfoScreen.tsx @@ -83,7 +83,6 @@ const $root: ViewStyle = { } const $screenHeading: ViewStyle = { - marginTop: spacing.extraLarge, marginBottom: spacing.medium, paddingHorizontal: spacing.large, } diff --git a/app/screens/InfoScreen/OurSponsorsScreen.tsx b/app/screens/InfoScreen/OurSponsorsScreen.tsx index 01ca95b..74f5df3 100644 --- a/app/screens/InfoScreen/OurSponsorsScreen.tsx +++ b/app/screens/InfoScreen/OurSponsorsScreen.tsx @@ -123,6 +123,5 @@ const $container: ViewStyle = { } const $heading: TextStyle = { - marginTop: spacing.extraLarge, paddingHorizontal: spacing.large, } diff --git a/app/screens/TalkDetailsScreen/WorkshopDetailsScreen.tsx b/app/screens/TalkDetailsScreen/WorkshopDetailsScreen.tsx index c30df0f..29d10fb 100644 --- a/app/screens/TalkDetailsScreen/WorkshopDetailsScreen.tsx +++ b/app/screens/TalkDetailsScreen/WorkshopDetailsScreen.tsx @@ -124,7 +124,6 @@ const $speakerPanelDescription: TextStyle = { } const $title: TextStyle = { - marginTop: spacing.extraLarge, marginBottom: spacing.extraSmall, } diff --git a/app/services/api/webflow-api.ts b/app/services/api/webflow-api.ts index c60f177..e6f5178 100644 --- a/app/services/api/webflow-api.ts +++ b/app/services/api/webflow-api.ts @@ -75,7 +75,7 @@ const scheduledEventQueries = [ export const prefetchScheduledEvents = async () => { scheduledEventQueries.forEach(async (query) => { - await queryClient.prefetchQuery(query) + await queryClient.prefetchQuery(query as UseQueryOptions) }) }