diff --git a/packages/common/lib/graphql-client-public/gql.ts b/packages/common/lib/graphql-client-public/gql.ts index 31a59067..f23c763e 100644 --- a/packages/common/lib/graphql-client-public/gql.ts +++ b/packages/common/lib/graphql-client-public/gql.ts @@ -19,8 +19,7 @@ const documents = { "\n fragment NotificationFragment on NotificationResource {\n uuid\n title\n body\n url\n }\n": types.NotificationFragmentFragmentDoc, "\n fragment NotificationDeliveryFragment on NotificationDeliveryResource {\n uuid\n sentAt\n notification {\n ...NotificationFragment\n }\n }\n": types.NotificationDeliveryFragmentFragmentDoc, "\n query useAllowedLoginTypes {\n activeConfiguration(key: \"ALLOWED_LOGIN_TYPES\") {\n data {\n ...SimpleConfig\n }\n }\n }\n": types.UseAllowedLoginTypesDocument, - "\n query useMarathonStart {\n activeConfiguration(key: \"MARATHON_START\") {\n data {\n ...SimpleConfig\n }\n }\n }\n": types.UseMarathonStartDocument, - "\n query useMarathonEnd {\n activeConfiguration(key: \"MARATHON_END\") {\n data {\n ...SimpleConfig\n }\n }\n }\n": types.UseMarathonEndDocument, + "\n query MarathonTime {\n nextMarathon {\n startDate\n endDate\n }\n }\n": types.MarathonTimeDocument, "\n query useTabBarConfig {\n activeConfiguration(key: \"TAB_BAR_CONFIG\") {\n data {\n ...SimpleConfig\n }\n }\n }\n": types.UseTabBarConfigDocument, "\n query AuthState {\n me {\n data {\n uuid\n }\n }\n loginState {\n role {\n dbRole\n committeeIdentifier\n committeeRole\n }\n loggedIn\n authSource\n }\n }\n": types.AuthStateDocument, "\n mutation SetDevice($input: RegisterDeviceInput!) {\n registerDevice(input: $input) {\n ok\n }\n }\n": types.SetDeviceDocument, @@ -81,11 +80,7 @@ export function graphql(source: "\n query useAllowedLoginTypes {\n activeCon /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n query useMarathonStart {\n activeConfiguration(key: \"MARATHON_START\") {\n data {\n ...SimpleConfig\n }\n }\n }\n"): (typeof documents)["\n query useMarathonStart {\n activeConfiguration(key: \"MARATHON_START\") {\n data {\n ...SimpleConfig\n }\n }\n }\n"]; -/** - * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. - */ -export function graphql(source: "\n query useMarathonEnd {\n activeConfiguration(key: \"MARATHON_END\") {\n data {\n ...SimpleConfig\n }\n }\n }\n"): (typeof documents)["\n query useMarathonEnd {\n activeConfiguration(key: \"MARATHON_END\") {\n data {\n ...SimpleConfig\n }\n }\n }\n"]; +export function graphql(source: "\n query MarathonTime {\n nextMarathon {\n startDate\n endDate\n }\n }\n"): (typeof documents)["\n query MarathonTime {\n nextMarathon {\n startDate\n endDate\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/packages/common/lib/graphql-client-public/graphql.ts b/packages/common/lib/graphql-client-public/graphql.ts index aa4a6320..0639a494 100644 --- a/packages/common/lib/graphql-client-public/graphql.ts +++ b/packages/common/lib/graphql-client-public/graphql.ts @@ -1755,21 +1755,10 @@ export type UseAllowedLoginTypesQuery = { readonly __typename?: 'Query', readonl & { ' $fragmentRefs'?: { 'SimpleConfigFragment': SimpleConfigFragment } } ) } }; -export type UseMarathonStartQueryVariables = Exact<{ [key: string]: never; }>; +export type MarathonTimeQueryVariables = Exact<{ [key: string]: never; }>; -export type UseMarathonStartQuery = { readonly __typename?: 'Query', readonly activeConfiguration: { readonly __typename?: 'GetConfigurationByUuidResponse', readonly data: ( - { readonly __typename?: 'ConfigurationResource' } - & { ' $fragmentRefs'?: { 'SimpleConfigFragment': SimpleConfigFragment } } - ) } }; - -export type UseMarathonEndQueryVariables = Exact<{ [key: string]: never; }>; - - -export type UseMarathonEndQuery = { readonly __typename?: 'Query', readonly activeConfiguration: { readonly __typename?: 'GetConfigurationByUuidResponse', readonly data: ( - { readonly __typename?: 'ConfigurationResource' } - & { ' $fragmentRefs'?: { 'SimpleConfigFragment': SimpleConfigFragment } } - ) } }; +export type MarathonTimeQuery = { readonly __typename?: 'Query', readonly nextMarathon?: { readonly __typename?: 'MarathonResource', readonly startDate: Date | string, readonly endDate: Date | string } | null }; export type UseTabBarConfigQueryVariables = Exact<{ [key: string]: never; }>; @@ -1885,8 +1874,7 @@ export const ScoreBoardFragmentFragmentDoc = {"kind":"Document","definitions":[{ export const HighlightedTeamFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"HighlightedTeamFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TeamResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"legacyStatus"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]} as unknown as DocumentNode; export const MyTeamFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MyTeamFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TeamResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"totalPoints"}},{"kind":"Field","name":{"kind":"Name","value":"pointEntries"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personFrom"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"linkblue"}}]}},{"kind":"Field","name":{"kind":"Name","value":"points"}}]}},{"kind":"Field","name":{"kind":"Name","value":"members"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"person"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"linkblue"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; export const UseAllowedLoginTypesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"useAllowedLoginTypes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeConfiguration"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"key"},"value":{"kind":"StringValue","value":"ALLOWED_LOGIN_TYPES","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SimpleConfig"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SimpleConfig"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ConfigurationResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]} as unknown as DocumentNode; -export const UseMarathonStartDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"useMarathonStart"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeConfiguration"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"key"},"value":{"kind":"StringValue","value":"MARATHON_START","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SimpleConfig"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SimpleConfig"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ConfigurationResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]} as unknown as DocumentNode; -export const UseMarathonEndDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"useMarathonEnd"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeConfiguration"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"key"},"value":{"kind":"StringValue","value":"MARATHON_END","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SimpleConfig"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SimpleConfig"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ConfigurationResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]} as unknown as DocumentNode; +export const MarathonTimeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MarathonTime"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nextMarathon"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"startDate"}},{"kind":"Field","name":{"kind":"Name","value":"endDate"}}]}}]}}]} as unknown as DocumentNode; export const UseTabBarConfigDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"useTabBarConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeConfiguration"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"key"},"value":{"kind":"StringValue","value":"TAB_BAR_CONFIG","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SimpleConfig"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SimpleConfig"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ConfigurationResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]} as unknown as DocumentNode; export const AuthStateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AuthState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"me"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"loginState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dbRole"}},{"kind":"Field","name":{"kind":"Name","value":"committeeIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"committeeRole"}}]}},{"kind":"Field","name":{"kind":"Name","value":"loggedIn"}},{"kind":"Field","name":{"kind":"Name","value":"authSource"}}]}}]}}]} as unknown as DocumentNode; export const SetDeviceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SetDevice"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RegisterDeviceInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"registerDevice"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/mobile/index.js b/packages/mobile/index.js index c04fea02..f6265e74 100644 --- a/packages/mobile/index.js +++ b/packages/mobile/index.js @@ -8,6 +8,7 @@ import { LogBox } from "react-native"; import "react-native-url-polyfill/auto"; import App from "./App"; +import { overrideApiBaseUrl } from "./src/common/apiUrl"; import { Logger } from "./src/common/logger/Logger"; Logger.debug("Starting app"); @@ -40,6 +41,15 @@ if (isDevelopmentBuild()) { console.log(values); }, }, + { + name: "Override url", + callback: async () => { + Logger.log("Overriding url"); + overrideApiBaseUrl( + prompt("Enter the url to override or blank for default") + ); + }, + }, ]).catch((error) => console.error(error)); } diff --git a/packages/mobile/src/common/apiUrl.ts b/packages/mobile/src/common/apiUrl.ts index 3877049c..3ff1f8fa 100644 --- a/packages/mobile/src/common/apiUrl.ts +++ b/packages/mobile/src/common/apiUrl.ts @@ -10,5 +10,22 @@ // : "http://localhost:8000"; // } -export const API_BASE_URL = +import { reloadAsync } from "expo-updates"; + +import { Logger } from "./logger/Logger"; + +export let API_BASE_URL = process.env.EXPO_PUBLIC_API_BASE_URL || "https://app.danceblue.org"; + +export function overrideApiBaseUrl(newUrl: string) { + API_BASE_URL = + newUrl || + process.env.EXPO_PUBLIC_API_BASE_URL || + "https://app.danceblue.org"; + reloadAsync().catch((error: unknown) => { + Logger.error("Failed to reload app", { + error, + source: "overrideApiBaseUrl", + }); + }); +} diff --git a/packages/mobile/src/common/hooks/useMarathonTime.ts b/packages/mobile/src/common/hooks/useMarathonTime.ts index bf31333d..04279b07 100644 --- a/packages/mobile/src/common/hooks/useMarathonTime.ts +++ b/packages/mobile/src/common/hooks/useMarathonTime.ts @@ -1,34 +1,20 @@ -import { SimpleConfigFragment } from "@common/fragments/Configuration"; -import { log, logError } from "@common/logging"; -import { - getFragmentData, - graphql, -} from "@ukdanceblue/common/dist/graphql-client-public"; +import { Logger } from "@common/logger/Logger"; +import { dateTimeFromSomething } from "@ukdanceblue/common"; +import { graphql } from "@ukdanceblue/common/dist/graphql-client-public"; import { DateTime } from "luxon"; import { useEffect, useMemo } from "react"; import { useQuery } from "urql"; export interface MarathonTime { - startTime: DateTime, - endTime: DateTime, + startTime: DateTime; + endTime: DateTime; } -const useMarathonStartQuery = graphql(/* GraphQL */ ` - query useMarathonStart { - activeConfiguration(key: "MARATHON_START") { - data { - ...SimpleConfig - } - } - } -`); - -const useMarathonEndQuery = graphql(/* GraphQL */ ` - query useMarathonEnd { - activeConfiguration(key: "MARATHON_END") { - data { - ...SimpleConfig - } +const marathonTimeQuery = graphql(/* GraphQL */ ` + query MarathonTime { + nextMarathon { + startDate + endDate } } `); @@ -37,68 +23,53 @@ export function useMarathonTime(): { timesLoading: boolean; marathonTime: MarathonTime; } { - const [{ data: startData, fetching: startFetch, error: startError }] = useQuery({ - query: useMarathonStartQuery, - }); - - const startValue = getFragmentData( - SimpleConfigFragment, - startData?.activeConfiguration.data - ); - - const [{ data: endData, fetching: endFetch, error: endError }] = useQuery({ - query: useMarathonEndQuery, + const [{ data, fetching, error }] = useQuery({ + query: marathonTimeQuery, }); - const endValue = getFragmentData( - SimpleConfigFragment, - endData?.activeConfiguration.data - ); - useEffect(() => { - if (startError) { - logError(startError); - } - if (endError) { - logError(endError); + if (error) { + Logger.error("Failed to fetch marathon start time", { + error, + source: "useMarathonTime", + }); } }); - const determineMarathonIntervals = useMemo(() => { + const marathonInterval = useMemo(() => { let startTime: DateTime = DateTime.fromMillis(0); let endTime: DateTime = DateTime.fromMillis(0); try { - if (startValue) { - const parsed = startValue.value; - startTime = DateTime.fromISO(parsed); + if (data?.nextMarathon) { + startTime = dateTimeFromSomething(data.nextMarathon.startDate); if (!startTime.isValid) { - log(`Unrecognized marathon start time: ${startTime.toString()}`, "warn"); + Logger.warn( + `Unrecognized marathon start time: ${startTime.toString()}`, + { + source: "useMarathonTime", + } + ); } - } - - if (endValue) { - const parsed = endValue.value; - endTime = DateTime.fromISO(parsed); + endTime = dateTimeFromSomething(data.nextMarathon.endDate); if (!endTime.isValid) { - log(`Unrecognized marathon end time: ${endTime.toString()}`, "warn"); + Logger.warn(`Unrecognized marathon end time: ${endTime.toString()}`, { + source: "useMarathonTime", + }); } } } catch (error) { - if (error instanceof Error) { - logError(error); - } else { - log(String(error), "error"); - } + Logger.error("Failed to determine marathon intervals", { + error, + source: "useMarathonTime", + }); } - return({startTime, endTime} as MarathonTime); - - }, [startValue, endValue]); - + return { startTime, endTime } as MarathonTime; + }, [data?.nextMarathon]); return { - timesLoading: (startFetch || endFetch), - marathonTime: determineMarathonIntervals + timesLoading: fetching, + marathonTime: marathonInterval, }; } diff --git a/packages/mobile/src/common/hooks/useTabBarConfig.ts b/packages/mobile/src/common/hooks/useTabBarConfig.ts index 57cb952b..701ff541 100644 --- a/packages/mobile/src/common/hooks/useTabBarConfig.ts +++ b/packages/mobile/src/common/hooks/useTabBarConfig.ts @@ -1,5 +1,5 @@ import { SimpleConfigFragment } from "@common/fragments/Configuration"; -import { log, logError } from "@common/logging"; +import { Logger } from "@common/logger/Logger"; import { getFragmentData, graphql, @@ -32,7 +32,10 @@ export function useTabBarConfig(): { useEffect(() => { if (error) { - logError(error); + Logger.error("Failed to fetch tab bar configuration", { + error, + source: "useTabBarConfig", + }); } }); @@ -50,7 +53,7 @@ export function useTabBarConfig(): { ({ fancyTab } = parsed); } } else { - log( + Logger.warn( `Invalid fancyTab value in tab bar configuration: ${String( parsed.fancyTab )}` @@ -65,7 +68,7 @@ export function useTabBarConfig(): { shownTabs.push(tab); } } else { - log( + Logger.warn( `Invalid shownTabs value in tab bar configuration: ${String( tab )}` @@ -73,7 +76,7 @@ export function useTabBarConfig(): { } } } else { - log( + Logger.warn( `Invalid shownTabs value in tab bar configuration: ${String( parsed.shownTabs )}` @@ -81,15 +84,16 @@ export function useTabBarConfig(): { } } } else { - log(`Invalid allowed tab bar configuration: ${tabBarConfig.value}`); + Logger.warn( + `Invalid allowed tab bar configuration: ${tabBarConfig.value}` + ); } } } catch (error) { - if (error instanceof Error) { - logError(error); - } else { - log(String(error), "error"); - } + Logger.error("Failed to parse tab bar configuration", { + error, + source: "useTabBarConfig", + }); } return { fancyTab, shownTabs };