Skip to content

Commit bd45934

Browse files
committed
merge
2 parents 07f5999 + f3eb3c1 commit bd45934

File tree

17 files changed

+330
-128
lines changed

17 files changed

+330
-128
lines changed

.github/workflows/publish.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Publish Updates to Production
2+
on:
3+
push:
4+
branches:
5+
- main
6+
jobs:
7+
update:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- name: 🏗 Setup repo
11+
uses: actions/checkout@v3
12+
13+
- name: 🏗 Setup Node
14+
uses: actions/setup-node@v3
15+
with:
16+
node-version: 18.x
17+
cache: npm
18+
19+
- name: 🏗 Setup EAS
20+
uses: expo/expo-github-action@v8
21+
with:
22+
eas-version: latest
23+
token: ${{ secrets.EXPO_TOKEN }}
24+
25+
- name: 📦 Install dependencies
26+
run: npm ci
27+
28+
- name: 🚀 Create update
29+
run: |
30+
MESSAGE=$(git log -1 --pretty=%B)
31+
eas update --channel production --environment production --non-interactive --message "$MESSAGE"

App.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ import "expo-dev-client";
33
import * as SentryReact from "@sentry/react";
44
import * as Sentry from "@sentry/react-native";
55
import { StripeProvider } from "@stripe/stripe-react-native";
6+
import * as BackgroundTask from "expo-background-task";
67
import { useFonts } from "expo-font";
78
import { ShareIntentProvider as ExpoShareIntentProvider } from "expo-share-intent";
9+
import * as TaskManager from "expo-task-manager";
10+
import * as Updates from "expo-updates";
811
import { useColorScheme } from "react-native";
912

1013
import { AuthProvider } from "./src/auth/AuthProvider";
@@ -15,6 +18,25 @@ import { LinkingProvider } from "./src/providers/LinkingContext";
1518
import { ShareIntentProvider } from "./src/providers/ShareIntentContext";
1619
import { ThemeProvider } from "./src/providers/ThemeContext";
1720

21+
const BACKGROUND_TASK_NAME = "task-run-expo-update";
22+
23+
export const setupBackgroundUpdates = async () => {
24+
TaskManager.defineTask(BACKGROUND_TASK_NAME, async () => {
25+
const update = await Updates.checkForUpdateAsync();
26+
if (update.isAvailable) {
27+
await Updates.fetchUpdateAsync();
28+
await Updates.reloadAsync();
29+
}
30+
return Promise.resolve();
31+
});
32+
33+
await BackgroundTask.registerTaskAsync(BACKGROUND_TASK_NAME, {
34+
minimumInterval: 60 * 24,
35+
});
36+
};
37+
38+
setupBackgroundUpdates();
39+
1840
function App() {
1941
const [fontsLoaded] = useFonts({
2042
"JetBrainsMono-Regular": require("./assets/fonts/JetBrainsMono-Regular.ttf"),
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
diff --git a/node_modules/@thedev132/yellowpages/dist/index.js b/node_modules/@thedev132/yellowpages/dist/index.js
2+
index 80558a8..887aa5c 100644
3+
--- a/node_modules/@thedev132/yellowpages/dist/index.js
4+
+++ b/node_modules/@thedev132/yellowpages/dist/index.js
5+
@@ -13,7 +13,7 @@ var _Category = class _Category {
6+
"https://raw.githubusercontent.com/hackclub/yellow_pages/main/lib/yellow_pages/categories.yaml"
7+
);
8+
const text = await response.text();
9+
- const yaml = await import("js-yaml");
10+
+ const yaml = require("js-yaml");
11+
const data = yaml.load(text);
12+
this.categoriesByCode = Object.entries(data).reduce(
13+
(acc, [code, obj]) => {
14+
@@ -79,7 +79,7 @@ var _Merchant = class _Merchant {
15+
"https://raw.githubusercontent.com/hackclub/yellow_pages/main/lib/yellow_pages/merchants.yaml"
16+
);
17+
const text = await response.text();
18+
- const yaml = await import("js-yaml");
19+
+ const yaml = require("js-yaml");
20+
const data = yaml.load(text);
21+
this.merchants = data.reduce((acc, merchant) => {
22+
if (!merchant.name) return acc;

src/auth/AuthProvider.tsx

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ const TOKEN_CREATED_AT_KEY = "auth_token_created_at";
1313

1414
const redirectUri = makeRedirectUri({ scheme: "hcb" });
1515

16-
let lastSuccessfulRefreshTime = 0;
17-
const MIN_REFRESH_INTERVAL_MS = 1000;
18-
1916
let refreshPromise: Promise<{
2017
success: boolean;
2118
newTokens?: AuthTokens;
@@ -120,7 +117,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
120117
await SecureStore.deleteItemAsync(TOKEN_CREATED_AT_KEY);
121118
await SecureStore.deleteItemAsync(CODE_VERIFIER_KEY);
122119
setTokensState(null);
123-
Sentry.setUser(null);
124120
}
125121
} catch (error) {
126122
console.error("Failed to save auth tokens", error, {
@@ -140,9 +136,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
140136
await SecureStore.deleteItemAsync(CODE_VERIFIER_KEY);
141137

142138
setTokensState(null);
143-
Sentry.setUser(null);
144139

145-
lastSuccessfulRefreshTime = 0;
146140
refreshPromise = null;
147141
} catch (error) {
148142
console.error("Error during forced logout", error, {
@@ -181,13 +175,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
181175
}
182176

183177
const now = Date.now();
184-
const timeSinceLastRefresh = now - lastSuccessfulRefreshTime;
185-
if (timeSinceLastRefresh < MIN_REFRESH_INTERVAL_MS) {
186-
console.log(
187-
`Skipping token refresh - last refresh was ${timeSinceLastRefresh}ms ago (minimum interval: ${MIN_REFRESH_INTERVAL_MS}ms)`,
188-
);
189-
return { success: true, newTokens: tokens };
190-
}
191178

192179
console.log("Client ID:", process.env.EXPO_PUBLIC_CLIENT_ID);
193180
console.log("Redirect URI:", redirectUri);
@@ -284,8 +271,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
284271

285272
console.log("Token refreshed successfully");
286273

287-
lastSuccessfulRefreshTime = Date.now();
288-
289274
return { success: true, newTokens };
290275
} catch (error) {
291276
console.error("Token refresh failed", error, {

src/components/cards/CardDetails.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,13 @@ export default function CardDetails({
349349
</Text>
350350
</View>
351351
{grantCard?.purpose && (
352-
<>
352+
<View
353+
style={{
354+
flexDirection: "row",
355+
justifyContent: "space-between",
356+
marginBottom: 12,
357+
}}
358+
>
353359
<Text
354360
style={{
355361
fontSize: 16,
@@ -365,11 +371,12 @@ export default function CardDetails({
365371
fontSize: 16,
366372
fontWeight: "500",
367373
fontFamily: "JetBrainsMono-Regular",
374+
flexShrink: 1,
368375
}}
369376
>
370377
{grantCard?.purpose}
371378
</Text>
372-
</>
379+
</View>
373380
)}
374381
</View>
375382
<View

src/components/core/SentryUserBridge.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,14 @@ import User from "../../lib/types/User";
77
function SentryUserBridge() {
88
const { data: user } = useSWR<User>("user");
99
useEffect(() => {
10-
console.log("user", user);
1110
if (user?.id) {
1211
Sentry.setUser({
1312
id: String(user.id),
1413
email: user.email ?? undefined,
15-
username: user.name ?? undefined,
14+
name: user.name ?? undefined,
1615
});
1716
} else {
1817
Sentry.setUser(null);
19-
Sentry.setContext("user", null as unknown as Record<string, unknown>);
2018
}
2119
}, [user]);
2220
return null;

src/core/AppContent.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export default function AppContent({
8787
const [appIsReady, setAppIsReady] = useState(false);
8888
const isDark = useIsDark();
8989
const navigationRef = useRef<NavigationContainerRef<TabParamList>>(null);
90+
const isBiometricAuthInProgress = useRef(false);
9091
const hcb = useClient();
9192

9293
useEffect(() => {
@@ -244,6 +245,16 @@ export default function AppContent({
244245
return;
245246
}
246247

248+
if (isBiometricAuthInProgress.current) {
249+
console.log(
250+
"Biometric authentication already in progress, skipping...",
251+
);
252+
return;
253+
}
254+
255+
isBiometricAuthInProgress.current = true;
256+
257+
// Keep splash screen visible during biometric authentication
247258
const result = await LocalAuthentication.authenticateAsync({
248259
promptMessage: "Authenticate to access HCB",
249260
cancelLabel: "Cancel",
@@ -263,17 +274,21 @@ export default function AppContent({
263274
);
264275
setIsAuthenticated(false);
265276
}
277+
setAppIsReady(true);
278+
isBiometricAuthInProgress.current = false;
266279
} catch (error) {
267280
console.error("Biometric authentication error", error, {
268281
context: { action: "biometric_auth" },
269282
});
270283
setIsAuthenticated(false);
284+
setAppIsReady(true);
285+
isBiometricAuthInProgress.current = false;
271286
}
272287
} else {
273288
console.log("No access token, skipping biometric authentication");
274289
setIsAuthenticated(true);
290+
setAppIsReady(true);
275291
}
276-
setAppIsReady(true);
277292
};
278293

279294
checkAuth();

src/lib/NavigatorParamList.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export type StackParamList = {
3737

3838
export type CardsStackParamList = {
3939
CardList: undefined;
40-
Card: { card?: Card; cardId?: string };
40+
Card: { card?: Card; cardId?: string; grantId?: string };
4141
GrantCard: { grantId: string };
4242
OrderCard: undefined;
4343
Transaction: {

src/lib/useDigitalWallet.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,15 @@ export default function useDigitalWallet(cardId: string, isPhysical: boolean) {
116116

117117
useEffect(() => {
118118
if (Platform.OS === "ios") {
119-
import("react-native-watch-connectivity").then(({ getIsPaired }) => {
120-
getIsPaired().then((isPaired) => {
119+
try {
120+
// eslint-disable-next-line @typescript-eslint/no-var-requires
121+
const { getIsPaired } = require("react-native-watch-connectivity");
122+
getIsPaired().then((isPaired: boolean) => {
121123
setIsPaired(isPaired);
122124
});
123-
});
125+
} catch (e) {
126+
setIsPaired(false);
127+
}
124128
} else {
125129
setIsPaired(false);
126130
}

src/pages/Invitation.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export default function InvitationPage({
6969
organization: invitation!.organization,
7070
});
7171
mutate(`user/organizations`);
72+
mutate("user/invitations");
7273
},
7374
onError: () => {
7475
showAlert(
@@ -96,7 +97,10 @@ export default function InvitationPage({
9697
{
9798
populateCache: (_, invitations) =>
9899
invitations?.filter((i) => i.id != inviteId) || [],
99-
onSuccess: () => navigation.goBack(),
100+
onSuccess: () => {
101+
mutate("user/invitations");
102+
navigation.goBack();
103+
},
100104
},
101105
);
102106

0 commit comments

Comments
 (0)