mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-10-31 18:19:43 +00:00
chore(android): target API 36 with Expo 54
Upgrade the mobile native toolchain and Expo dependencies for Android 16 compatibility while preserving web and admin runtime isolation.
This commit is contained in:
1 parent
aacb4f8731
commit
61ec3692b7
27 files changed
+3867
-5535
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import { renderHook, act, waitFor } from '@testing-library/react-native';
|
||||
import { Alert } from 'react-native';
|
||||
import * as MediaLibrary from 'expo-media-library';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import * as FileSystem from 'expo-file-system/legacy';
|
||||
import { useMediaSaver } from '@/hooks/useMediaSaver';
|
||||
|
||||
// Mock expo-media-library
|
||||
@@ -11,7 +11,7 @@ jest.mock('expo-media-library', () => ({
|
||||
}));
|
||||
|
||||
// Mock expo-file-system with all required methods
|
||||
jest.mock('expo-file-system', () => ({
|
||||
jest.mock('expo-file-system/legacy', () => ({
|
||||
readAsStringAsync: jest.fn(async () => ''),
|
||||
downloadAsync: jest.fn(),
|
||||
deleteAsync: jest.fn(),
|
||||
|
||||
@@ -23,7 +23,7 @@ import { Ionicons } from "@expo/vector-icons";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import ViewShot from "react-native-view-shot";
|
||||
import * as Sharing from "expo-sharing";
|
||||
import * as FileSystem from "expo-file-system";
|
||||
import * as FileSystem from "expo-file-system/legacy";
|
||||
import { colors, gradients, radius, typography, spacing, blur } from "../../theme";
|
||||
|
||||
const logo = require("../../../assets/logo.png");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import Animated, { type AnimatedStyleProp } from 'react-native-reanimated';
|
||||
import Animated, { type AnimatedStyle } from 'react-native-reanimated';
|
||||
import type { ViewStyle } from 'react-native';
|
||||
import type { IntensityLevel } from '@/types';
|
||||
import { HEAT_COLORS } from './constants';
|
||||
@@ -9,7 +9,7 @@ import { styles } from './styles';
|
||||
|
||||
interface IntensitySliderTrackProps {
|
||||
value: IntensityLevel;
|
||||
progressStyle: AnimatedStyleProp<ViewStyle>;
|
||||
progressStyle: AnimatedStyle<ViewStyle>;
|
||||
}
|
||||
|
||||
export function IntensitySliderTrack({ value, progressStyle }: IntensitySliderTrackProps) {
|
||||
|
||||
@@ -23,7 +23,7 @@ import { Ionicons } from '@expo/vector-icons';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import ViewShot from 'react-native-view-shot';
|
||||
import * as Sharing from 'expo-sharing';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import * as FileSystem from 'expo-file-system/legacy';
|
||||
import { colors, gradients, radius, typography, spacing, blur } from '../../../theme';
|
||||
|
||||
const logo = require('../../../../assets/logo.png');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import type { SkImage } from '@shopify/react-native-skia';
|
||||
import * as MediaLibrary from 'expo-media-library';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import * as FileSystem from 'expo-file-system/legacy';
|
||||
|
||||
interface UseCanvasCaptureReturn {
|
||||
makeSnapshot: React.MutableRefObject<(() => SkImage | null) | null>;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useCallback, useRef } from 'react';
|
||||
import { Alert, Platform, ActionSheetIOS } from 'react-native';
|
||||
import * as ImagePicker from 'expo-image-picker';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import * as FileSystem from 'expo-file-system/legacy';
|
||||
import { uploadMedia } from '../lib/mediaApi';
|
||||
|
||||
export interface UseAvatarPickerOptions {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { Alert } from 'react-native';
|
||||
import * as MediaLibrary from 'expo-media-library';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import * as FileSystem from 'expo-file-system/legacy';
|
||||
|
||||
export function useMediaSaver() {
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getMediaUrl, mediaId } from './mediaApi';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import * as FileSystem from 'expo-file-system/legacy';
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
// Cache for signed URLs with expiration tracking
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Platform } from 'react-native';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import * as FileSystem from 'expo-file-system/legacy';
|
||||
import { decode } from 'base64-arraybuffer';
|
||||
import { ApiError, apiClient, authenticatedFetch } from './apiClient';
|
||||
|
||||
|
||||
@@ -12,20 +12,37 @@ import { useMatchStore } from "../store/matchStore";
|
||||
// Configure notification behavior for foreground
|
||||
// We don't show system alerts since the app handles notifications with in-app toasts
|
||||
Notifications.setNotificationHandler({
|
||||
handleNotification: async () => ({
|
||||
shouldShowAlert: false,
|
||||
shouldPlaySound: false,
|
||||
handleNotification: async () => ({
|
||||
shouldShowAlert: false,
|
||||
shouldShowBanner: false,
|
||||
shouldShowList: false,
|
||||
shouldPlaySound: false,
|
||||
shouldSetBadge: false,
|
||||
}),
|
||||
});
|
||||
|
||||
// Types
|
||||
export interface NotificationData {
|
||||
export interface NotificationData {
|
||||
type: "match" | "message" | "match_digest" | "partner_activity" | "nudge" | "weekly_summary";
|
||||
match_id?: string;
|
||||
message_id?: string;
|
||||
count?: number;
|
||||
}
|
||||
}
|
||||
|
||||
const notificationTypes = new Set<NotificationData["type"]>([
|
||||
"match",
|
||||
"message",
|
||||
"match_digest",
|
||||
"partner_activity",
|
||||
"nudge",
|
||||
"weekly_summary",
|
||||
]);
|
||||
|
||||
function isNotificationData(value: unknown): value is NotificationData {
|
||||
if (!value || typeof value !== "object") return false;
|
||||
const type = (value as { type?: unknown }).type;
|
||||
return typeof type === "string" && notificationTypes.has(type as NotificationData["type"]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -219,9 +236,9 @@ export async function checkAndRegisterPushToken(userId: string): Promise<boolean
|
||||
export function handleNotificationResponse(
|
||||
response: Notifications.NotificationResponse
|
||||
): void {
|
||||
const data = response.notification.request.content.data as NotificationData;
|
||||
|
||||
if (!data) {
|
||||
const data = response.notification.request.content.data;
|
||||
|
||||
if (!isNotificationData(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,13 +103,26 @@ jest.mock('expo-image-picker', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('expo-file-system', () => ({
|
||||
jest.mock('expo-file-system/legacy', () => ({
|
||||
readAsStringAsync: jest.fn(async () => ''),
|
||||
EncodingType: {
|
||||
Base64: 'base64',
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('expo-av', () => ({
|
||||
Audio: {
|
||||
requestPermissionsAsync: jest.fn(async () => ({ status: 'granted' })),
|
||||
setAudioModeAsync: jest.fn(async () => undefined),
|
||||
Recording: {
|
||||
createAsync: jest.fn(),
|
||||
},
|
||||
RecordingOptionsPresets: {
|
||||
HIGH_QUALITY: {},
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('react-native-compressor', () => ({
|
||||
Video: {
|
||||
compress: jest.fn(async (uri: string) => uri),
|
||||
|
||||
Reference in new issue
Block a user