Skip to content

Commit

Permalink
feat: refresh token working, finishing egvs
Browse files Browse the repository at this point in the history
  • Loading branch information
trevorpfiz committed Aug 31, 2024
1 parent 194962e commit b6bf828
Show file tree
Hide file tree
Showing 21 changed files with 873 additions and 243 deletions.
7 changes: 1 addition & 6 deletions apps/expo/eas.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,7 @@
"development": {
"extends": "base",
"developmentClient": true,
"distribution": "internal",
"env": {
"EXPO_PUBLIC_SUPABASE_URL": "https://dnnqvouwzxzejzkvmqfk.supabase.co",
"EXPO_PUBLIC_SUPABASE_ANON_KEY": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImRubnF2b3V3enh6ZWp6a3ZtcWZrIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MjI0NTY0NjQsImV4cCI6MjAzODAzMjQ2NH0.I12jE7YC7h7B1qu1zYsBgzu3LQFj8CXNolevtxYpxU0",
"EXPO_PUBLIC_FASTAPI_URL": "http://localhost:8000"
}
"distribution": "internal"
},
"preview": {
"extends": "base",
Expand Down
1 change: 0 additions & 1 deletion apps/expo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@
"tailwind-merge": "^2.5.2",
"tailwindcss": "catalog:tailwind",
"tailwindcss-animate": "catalog:tailwind",
"trpc-token-refresh-link": "^0.5.0",
"victory-native": "^41.1.0",
"zeego": "^1.10.0",
"zustand": "^4.5.5"
Expand Down
9 changes: 6 additions & 3 deletions apps/expo/src/app/(app)/(tabs)/account.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { View } from "react-native";
import { ScrollView, View } from "react-native";
import {
useSessionContext,
useSupabaseClient,
useUser,
} from "@supabase/auth-helpers-react";

import DexcomCGMData from "~/components/dexcom/dexcom-data";
import DexcomDevicesList from "~/components/dexcom/dexcom-devices";
import { DexcomLogin } from "~/components/dexcom/dexcom-login";
import { ThemeToggle } from "~/components/theme-toggle";
Expand Down Expand Up @@ -36,13 +37,15 @@ export default function AccountScreen() {
};

return (
<View>
<ScrollView>
<Text>Account Screen</Text>
{user?.id && <SignOut />}

<DexcomLogin />

<DexcomCGMData />

<DexcomDevicesList />
</View>
</ScrollView>
);
}
133 changes: 80 additions & 53 deletions apps/expo/src/components/dexcom/dexcom-data.tsx
Original file line number Diff line number Diff line change
@@ -1,75 +1,102 @@
import React, { useState } from "react";
import { ActivityIndicator, Alert, ScrollView, View } from "react-native";
import { useEffect } from "react";
import { Alert, View } from "react-native";

import { Button } from "~/components/ui/button";
import { Text } from "~/components/ui/text";
import { useSyncStore } from "~/stores/sync-store";
import { api } from "~/utils/api";

const DexcomCGMData: React.FC = () => {
const [isFetching, setIsFetching] = useState(false);
const utils = api.useUtils();
const { lastSyncedTime, setLastSyncedTime } = useSyncStore();

const fetchAndStoreEGVsMutation = api.dexcom.fetchAndStoreEGVs.useMutation();
const fetchDataRangeQuery = api.dexcom.fetchDataRange.useQuery({});

const today = new Date();
const startDate = new Date(today.setHours(0, 0, 0, 0)).toISOString();
const endDate = new Date().toISOString();
const fetchAndStoreEGVsMutation = api.dexcom.fetchAndStoreEGVs.useMutation({
async onSuccess(data) {
if (data.recordsInserted > 0) {
Alert.alert(
"Success",
`${data.recordsInserted} CGM records fetched and stored successfully.`,
);
await utils.dexcom.getStoredEGVs.invalidate();
if (data.latestEGVTimestamp) {
setLastSyncedTime(new Date(data.latestEGVTimestamp));
}
} else {
Alert.alert("Info", "No new CGM data available.");
}
},
});

const queryInput = { startDate, endDate };
useEffect(() => {
if (fetchDataRangeQuery.data) {
console.log("Data range fetched:", fetchDataRangeQuery.data);
}
}, [fetchDataRangeQuery.data]);

const {
data: cgmData,
isLoading: isLoadingStoredData,
refetch: refetchStoredData,
} = api.dexcom.getStoredEGVs.useQuery(queryInput);
const handleFetchData = () => {
if (fetchDataRangeQuery.data?.egvs) {
const augustStart = new Date("2024-08-01T00:00:00Z");
const augustEnd = new Date("2024-08-31T23:59:59Z");

const handleFetchData = async () => {
setIsFetching(true);
try {
await fetchAndStoreEGVsMutation.mutateAsync(queryInput);
await refetchStoredData();
Alert.alert("Success", "CGM data fetched and stored successfully.");
} catch (error) {
console.error("Error fetching and storing CGM data:", error);
Alert.alert(
"Error",
"Failed to fetch and store CGM data. Please try again.",
const dataRangeStart = new Date(
fetchDataRangeQuery.data.egvs.start.systemTime,
);
const dataRangeEnd = new Date(
fetchDataRangeQuery.data.egvs.end.systemTime,
);
} finally {
setIsFetching(false);

const startDate =
dataRangeStart > augustStart
? dataRangeStart.toISOString()
: augustStart.toISOString();
const endDate =
dataRangeEnd < augustEnd
? dataRangeEnd.toISOString()
: augustEnd.toISOString();

const queryInput = {
startDate,
endDate,
};

fetchAndStoreEGVsMutation.mutate(queryInput);
} else {
Alert.alert("Error", "Unable to fetch data range. Please try again.");
}
};

if (isLoadingStoredData) {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<ActivityIndicator size="large" />
<Text>Loading CGM data...</Text>
</View>
);
if (fetchDataRangeQuery.isPending) {
return <Text>Loading...</Text>;
}

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

return (
<ScrollView style={{ padding: 16 }}>
<Text style={{ fontSize: 20, fontWeight: "bold", marginBottom: 16 }}>
Today's CGM Data
</Text>
<Button onPress={handleFetchData} disabled={isFetching}>
<Text>{isFetching ? "Fetching..." : "Fetch New CGM Data"}</Text>
<View>
<Button
onPress={handleFetchData}
disabled={fetchAndStoreEGVsMutation.isPending}
>
<Text>
{fetchAndStoreEGVsMutation.isPending
? "Fetching..."
: "Fetch August 2024 CGM Data"}
</Text>
</Button>
{!cgmData || cgmData.length === 0 ? (
<Text>No CGM data available for today.</Text>
) : (
cgmData.map((dataPoint) => (
<View key={dataPoint.id} style={{ marginBottom: 12 }}>
<Text>
Time: {new Date(dataPoint.displayTime).toLocaleTimeString()}
</Text>
<Text>Glucose Value: {dataPoint.glucoseValue} mg/dL</Text>
<Text>Trend: {dataPoint.trend}</Text>
</View>
))
)}
</ScrollView>
{fetchAndStoreEGVsMutation.isError ? (
<Text>
An error occurred: {fetchAndStoreEGVsMutation.error.message}
</Text>
) : null}
<Text>
Last Synced:{" "}
{lastSyncedTime ? lastSyncedTime.toLocaleString() : "Never"}
</Text>
</View>
);
};

Expand Down
22 changes: 22 additions & 0 deletions apps/expo/src/stores/sync-store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";

import { zustandStorage } from "~/lib/storage";

interface SyncState {
lastSyncedTime: Date | null;
setLastSyncedTime: (date: Date) => void;
}

export const useSyncStore = create<SyncState>()(
persist(
(set) => ({
lastSyncedTime: null,
setLastSyncedTime: (date: Date) => set({ lastSyncedTime: date }),
}),
{
name: "sync-storage", // unique name
storage: createJSONStorage(() => zustandStorage),
},
),
);
4 changes: 2 additions & 2 deletions apps/expo/src/utils/dexcom-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ export const isRefreshTokenExpired = (): boolean => {
export const updateDexcomTokens = (
accessToken: string,
refreshToken: string,
expiresIn: number,
expiresIn: number, // This is in seconds
): void => {
const now = Date.now();
const expiresAt = now + expiresIn * 1000;
const expiresAt = now + expiresIn * 1000; // Convert seconds to milliseconds
const refreshTokenCreated = now;

setDexcomTokens({
Expand Down
89 changes: 0 additions & 89 deletions apps/expo/src/utils/dexcom.ts

This file was deleted.

30 changes: 30 additions & 0 deletions apps/expo/src/utils/helper-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { createTRPCClient, httpBatchLink } from "@trpc/client";
import SuperJSON from "superjson";

import type { AppRouter } from "@hyper/api";

import { getBaseUrl } from "~/utils/base-url";
import { getDexcomTokens } from "~/utils/dexcom-store";

export const helperClient = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
transformer: SuperJSON,
url: `${getBaseUrl()}/api/trpc`,
headers() {
const headers = new Map<string, string>();
headers.set("x-trpc-source", "expo-react");

// Add Dexcom tokens to headers
const dexcomTokens = getDexcomTokens();
if (dexcomTokens) {
headers.set("X-Dexcom-Access-Token", dexcomTokens.accessToken);
headers.set("X-Dexcom-Refresh-Token", dexcomTokens.refreshToken);
headers.set("X-Dexcom-Expires-At", dexcomTokens.expiresAt.toString());
}

return Object.fromEntries(headers);
},
}),
],
});
Loading

0 comments on commit b6bf828

Please sign in to comment.