perf(images): migrate react-native Image → expo-image (memory+disk cache)
Avatare (Dicebear-URLs), Chat-Attachments und Feed-Bilder wurden bei jedem App-Reload neu vom Netzwerk geladen — RN Image hat nur flüchtigen Memory-Cache. expo-image (~3.0.11) bringt persistenten Disk-Cache (cachePolicy 'memory-disk' default). 14 Files migriert: UserAvatar, ChatBubble, RoomCard, ChatInput, PostCard, ComposeCard, NotificationsDropdown, AppHeader, ProfileHeader, AddDomainSheet, DomainGrid, room, profile/edit, signup. API-Mapping: resizeMode→contentFit. PostCard onLoad las e.nativeEvent. source — expo-image liefert e.source direkt (sonst wäre der Post-Bild- Aspect-Ratio-Fix still gebrochen). PostCard: nur Image-Zeilen angefasst, Like/Count/Memo-Logik unberührt (memory/feedback_minimal_post_changes.md). Kommt mit v0.3.3 (expo-image ist Native-Modul, braucht neuen Build).
This commit is contained in:
parent
a9015d1951
commit
b8e4b02b88
@ -4,10 +4,10 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
Image,
|
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Platform,
|
Platform,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import { Image } from 'expo-image';
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import Svg, { Path } from 'react-native-svg';
|
import Svg, { Path } from 'react-native-svg';
|
||||||
|
|||||||
@ -5,10 +5,10 @@ import {
|
|||||||
TextInput,
|
TextInput,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
Image,
|
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Alert,
|
Alert,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import { Image } from 'expo-image';
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import {
|
|||||||
Keyboard,
|
Keyboard,
|
||||||
KeyboardAvoidingView,
|
KeyboardAvoidingView,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { Image } from 'react-native';
|
import { Image } from 'expo-image';
|
||||||
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import { useRouter, useLocalSearchParams } from 'expo-router';
|
import { useRouter, useLocalSearchParams } from 'expo-router';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
@ -322,7 +322,7 @@ export default function RoomScreen() {
|
|||||||
<View style={styles.headerCenter}>
|
<View style={styles.headerCenter}>
|
||||||
<View style={styles.headerAvatar}>
|
<View style={styles.headerAvatar}>
|
||||||
{room?.avatarUrl ? (
|
{room?.avatarUrl ? (
|
||||||
<Image source={{ uri: room.avatarUrl }} style={styles.headerAvatarImg} resizeMode="cover" />
|
<Image source={{ uri: room.avatarUrl }} style={styles.headerAvatarImg} contentFit="cover" />
|
||||||
) : (
|
) : (
|
||||||
<Text style={styles.headerAvatarInitials}>{initials}</Text>
|
<Text style={styles.headerAvatarInitials}>{initials}</Text>
|
||||||
)}
|
)}
|
||||||
@ -556,7 +556,7 @@ function RoomSettingsModal({
|
|||||||
style={modal.avatarWrap}
|
style={modal.avatarWrap}
|
||||||
>
|
>
|
||||||
{room.avatarUrl ? (
|
{room.avatarUrl ? (
|
||||||
<Image source={{ uri: room.avatarUrl }} style={modal.avatar} resizeMode="cover" />
|
<Image source={{ uri: room.avatarUrl }} style={modal.avatar} contentFit="cover" />
|
||||||
) : (
|
) : (
|
||||||
<View style={[modal.avatar, modal.avatarPlaceholder]}>
|
<View style={[modal.avatar, modal.avatarPlaceholder]}>
|
||||||
<Ionicons name="people" size={32} color="#737373" />
|
<Ionicons name="people" size={32} color="#737373" />
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { View, Text, TouchableOpacity, Image } from 'react-native';
|
import { View, Text, TouchableOpacity } from 'react-native';
|
||||||
|
import { Image } from 'expo-image';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { useRouter, type RelativePathString } from 'expo-router';
|
import { useRouter, type RelativePathString } from 'expo-router';
|
||||||
|
|||||||
@ -5,10 +5,10 @@ import {
|
|||||||
TextInput,
|
TextInput,
|
||||||
Pressable,
|
Pressable,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
Image,
|
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Alert,
|
Alert,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import { Image } from 'expo-image';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -126,7 +126,7 @@ export function ComposeCard({ onPosted }: Props) {
|
|||||||
source={{ uri: imageUri }}
|
source={{ uri: imageUri }}
|
||||||
className="w-full rounded-xl"
|
className="w-full rounded-xl"
|
||||||
style={{ height: 160 }}
|
style={{ height: 160 }}
|
||||||
resizeMode="cover"
|
contentFit="cover"
|
||||||
/>
|
/>
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={() => setImageUri(null)}
|
onPress={() => setImageUri(null)}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { View, Text, Pressable, TouchableOpacity, Modal, FlatList, Animated, Image } from 'react-native';
|
import { View, Text, Pressable, TouchableOpacity, Modal, FlatList, Animated } from 'react-native';
|
||||||
|
import { Image } from 'expo-image';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { useRouter, type RelativePathString } from 'expo-router';
|
import { useRouter, type RelativePathString } from 'expo-router';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { memo, useState, useCallback, useRef, useEffect } from 'react';
|
import { memo, useState, useCallback, useRef, useEffect } from 'react';
|
||||||
import { View, Text, Image, Pressable, Animated, TouchableOpacity } from 'react-native';
|
import { View, Text, Pressable, Animated, TouchableOpacity } from 'react-native';
|
||||||
|
import { Image } from 'expo-image';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -308,7 +309,7 @@ function PostCardImpl({ post, onCommentPress }: Props) {
|
|||||||
<Image
|
<Image
|
||||||
source={{ uri: displayImage }}
|
source={{ uri: displayImage }}
|
||||||
onLoad={(e) => {
|
onLoad={(e) => {
|
||||||
const { width, height } = e.nativeEvent.source;
|
const { width, height } = e.source;
|
||||||
if (width && height) {
|
if (width && height) {
|
||||||
const ratio = width / height;
|
const ratio = width / height;
|
||||||
setImageAspectRatio(Math.max(0.6, Math.min(1.78, ratio)));
|
setImageAspectRatio(Math.max(0.6, Math.min(1.78, ratio)));
|
||||||
@ -316,7 +317,7 @@ function PostCardImpl({ post, onCommentPress }: Props) {
|
|||||||
}}
|
}}
|
||||||
className="w-full rounded-xl mt-3"
|
className="w-full rounded-xl mt-3"
|
||||||
style={{ aspectRatio: imageAspectRatio ?? 1.78 }}
|
style={{ aspectRatio: imageAspectRatio ?? 1.78 }}
|
||||||
resizeMode="cover"
|
contentFit="cover"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -439,7 +440,7 @@ function DomainFavicon({ domain, size }: DomainFaviconProps) {
|
|||||||
<Image
|
<Image
|
||||||
source={{ uri }}
|
source={{ uri }}
|
||||||
style={{ width: size, height: size, borderRadius: 6 }}
|
style={{ width: size, height: size, borderRadius: 6 }}
|
||||||
resizeMode="cover"
|
contentFit="cover"
|
||||||
onError={() => setFailed(true)}
|
onError={() => setFailed(true)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { View, Text, Image } from 'react-native';
|
import { View, Text } from 'react-native';
|
||||||
|
import { Image } from 'expo-image';
|
||||||
import { useOnlineUsers } from '../hooks/useOnlineUsers';
|
import { useOnlineUsers } from '../hooks/useOnlineUsers';
|
||||||
import { resolveAvatar } from '../lib/resolveAvatar';
|
import { resolveAvatar } from '../lib/resolveAvatar';
|
||||||
import { useColors } from '../lib/theme';
|
import { useColors } from '../lib/theme';
|
||||||
@ -89,7 +90,7 @@ export function UserAvatar({
|
|||||||
borderRadius: radius,
|
borderRadius: radius,
|
||||||
backgroundColor: colors.surfaceElevated,
|
backgroundColor: colors.surfaceElevated,
|
||||||
}}
|
}}
|
||||||
resizeMode="cover"
|
contentFit="cover"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<View
|
<View
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Image,
|
|
||||||
ScrollView,
|
ScrollView,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import { Image } from 'expo-image';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
@ -98,6 +98,14 @@ export function AddDomainSheet({ visible, tier, onClose, onAdd }: Props) {
|
|||||||
setError(t('blocker.error_web_limit_reached'));
|
setError(t('blocker.error_web_limit_reached'));
|
||||||
} else if (raw.includes('mail_limit_reached')) {
|
} else if (raw.includes('mail_limit_reached')) {
|
||||||
setError(t('blocker.error_mail_limit_reached'));
|
setError(t('blocker.error_mail_limit_reached'));
|
||||||
|
} else if (raw === 'limit_reached' || raw.includes('limit_reached')) {
|
||||||
|
// Client-side tier.atLimit Reject (combined web+mail). Bucket-spezifisch
|
||||||
|
// wäre genauer aber der Generic-Limit-Hinweis reicht für jetzt.
|
||||||
|
setError(
|
||||||
|
kind === 'mail'
|
||||||
|
? t('blocker.error_mail_limit_reached')
|
||||||
|
: t('blocker.error_web_limit_reached'),
|
||||||
|
);
|
||||||
} else if (raw.includes('invalid_mail_domain') || raw.includes('display_name_not_supported')) {
|
} else if (raw.includes('invalid_mail_domain') || raw.includes('display_name_not_supported')) {
|
||||||
setError(t('blocker.error_invalid_mail'));
|
setError(t('blocker.error_invalid_mail'));
|
||||||
} else if (raw.includes('invalid_domain') || raw.includes('invalid_pattern')) {
|
} else if (raw.includes('invalid_domain') || raw.includes('invalid_pattern')) {
|
||||||
|
|||||||
@ -3,9 +3,9 @@ import {
|
|||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
Image,
|
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import { Image } from 'expo-image';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { SuccessAlert } from '../SuccessAlert';
|
import { SuccessAlert } from '../SuccessAlert';
|
||||||
|
|||||||
@ -2,12 +2,12 @@ import { useState } from 'react';
|
|||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
Image,
|
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Modal,
|
Modal,
|
||||||
Platform,
|
Platform,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import { Image } from 'expo-image';
|
||||||
import * as Clipboard from 'expo-clipboard';
|
import * as Clipboard from 'expo-clipboard';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -198,7 +198,7 @@ export function ChatBubble({
|
|||||||
<Image
|
<Image
|
||||||
source={{ uri: msg.attachmentUrl }}
|
source={{ uri: msg.attachmentUrl }}
|
||||||
style={styles.image}
|
style={styles.image}
|
||||||
resizeMode="cover"
|
contentFit="cover"
|
||||||
/>
|
/>
|
||||||
{isImageOnly && (
|
{isImageOnly && (
|
||||||
<View style={styles.imageTimeOverlay}>
|
<View style={styles.imageTimeOverlay}>
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { useState, useRef } from 'react';
|
|||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
Image,
|
|
||||||
TextInput,
|
TextInput,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
@ -10,6 +9,7 @@ import {
|
|||||||
Platform,
|
Platform,
|
||||||
Alert,
|
Alert,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import { Image } from 'expo-image';
|
||||||
import * as ImagePicker from 'expo-image-picker';
|
import * as ImagePicker from 'expo-image-picker';
|
||||||
// TODO(sdk54): migrate to new expo-file-system class-based API (File/Directory/Paths) — see Task #14
|
// TODO(sdk54): migrate to new expo-file-system class-based API (File/Directory/Paths) — see Task #14
|
||||||
import * as FileSystem from 'expo-file-system/legacy';
|
import * as FileSystem from 'expo-file-system/legacy';
|
||||||
@ -165,7 +165,7 @@ export function ChatInput({
|
|||||||
{attachment && (
|
{attachment && (
|
||||||
<View style={styles.attachBar}>
|
<View style={styles.attachBar}>
|
||||||
{attachment.isImage ? (
|
{attachment.isImage ? (
|
||||||
<Image source={{ uri: attachment.uri }} style={styles.attachImg} resizeMode="cover" />
|
<Image source={{ uri: attachment.uri }} style={styles.attachImg} contentFit="cover" />
|
||||||
) : (
|
) : (
|
||||||
<View style={styles.attachFileIcon}>
|
<View style={styles.attachFileIcon}>
|
||||||
<Ionicons name="document" size={18} color="#737373" />
|
<Ionicons name="document" size={18} color="#737373" />
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { View, Text, Image, TouchableOpacity, StyleSheet } from 'react-native';
|
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
|
||||||
|
import { Image } from 'expo-image';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useColors } from '../../lib/theme';
|
import { useColors } from '../../lib/theme';
|
||||||
@ -43,7 +44,7 @@ export function RoomCard({ room, onPress }: Props) {
|
|||||||
<View style={styles.row}>
|
<View style={styles.row}>
|
||||||
<View style={[styles.avatar, { backgroundColor: room.isPublic ? '#EFF6FF' : colors.surfaceElevated }]}>
|
<View style={[styles.avatar, { backgroundColor: room.isPublic ? '#EFF6FF' : colors.surfaceElevated }]}>
|
||||||
{room.avatarUrl ? (
|
{room.avatarUrl ? (
|
||||||
<Image source={{ uri: room.avatarUrl }} style={styles.avatarImg} resizeMode="cover" />
|
<Image source={{ uri: room.avatarUrl }} style={styles.avatarImg} contentFit="cover" />
|
||||||
) : !room.isPublic ? (
|
) : !room.isPublic ? (
|
||||||
<Text style={styles.avatarInitials}>{initials}</Text>
|
<Text style={styles.avatarInitials}>{initials}</Text>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { View, Text, TouchableOpacity, Image } from 'react-native';
|
import { View, Text, TouchableOpacity } from 'react-native';
|
||||||
|
import { Image } from 'expo-image';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import Svg, { Path } from 'react-native-svg';
|
import Svg, { Path } from 'react-native-svg';
|
||||||
import { useColors } from '../../lib/theme';
|
import { useColors } from '../../lib/theme';
|
||||||
|
|||||||
@ -36,7 +36,8 @@
|
|||||||
"expo-file-system": "~19.0.22",
|
"expo-file-system": "~19.0.22",
|
||||||
"expo-font": "~14.0.11",
|
"expo-font": "~14.0.11",
|
||||||
"expo-haptics": "^15.0.8",
|
"expo-haptics": "^15.0.8",
|
||||||
"expo-image-manipulator": "~14.0.7",
|
"expo-image": "~3.0.11",
|
||||||
|
"expo-image-manipulator": "~14.0.7",
|
||||||
"expo-image-picker": "~17.0.11",
|
"expo-image-picker": "~17.0.11",
|
||||||
"expo-linking": "~8.0.12",
|
"expo-linking": "~8.0.12",
|
||||||
"expo-local-authentication": "~17.0.8",
|
"expo-local-authentication": "~17.0.8",
|
||||||
|
|||||||
20
pnpm-lock.yaml
generated
20
pnpm-lock.yaml
generated
@ -192,6 +192,9 @@ importers:
|
|||||||
expo-haptics:
|
expo-haptics:
|
||||||
specifier: ^15.0.8
|
specifier: ^15.0.8
|
||||||
version: 15.0.8(expo@54.0.34)
|
version: 15.0.8(expo@54.0.34)
|
||||||
|
expo-image:
|
||||||
|
specifier: ~3.0.11
|
||||||
|
version: 3.0.11(expo@54.0.34)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0)
|
||||||
expo-image-manipulator:
|
expo-image-manipulator:
|
||||||
specifier: ~14.0.7
|
specifier: ~14.0.7
|
||||||
version: 14.0.8(expo@54.0.34)
|
version: 14.0.8(expo@54.0.34)
|
||||||
@ -5567,6 +5570,17 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
expo: '*'
|
expo: '*'
|
||||||
|
|
||||||
|
expo-image@3.0.11:
|
||||||
|
resolution: {integrity: sha512-4TudfUCLgYgENv+f48omnU8tjS2S0Pd9EaON5/s1ZUBRwZ7K8acEr4NfvLPSaeXvxW24iLAiyQ7sV7BXQH3RoA==}
|
||||||
|
peerDependencies:
|
||||||
|
expo: '*'
|
||||||
|
react: '*'
|
||||||
|
react-native: '*'
|
||||||
|
react-native-web: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
react-native-web:
|
||||||
|
optional: true
|
||||||
|
|
||||||
expo-json-utils@0.15.0:
|
expo-json-utils@0.15.0:
|
||||||
resolution: {integrity: sha512-duRT6oGl80IDzH2LD2yEFWNwGIC2WkozsB6HF3cDYNoNNdUvFk6uN3YiwsTsqVM/D0z6LEAQ01/SlYvN+Fw0JQ==}
|
resolution: {integrity: sha512-duRT6oGl80IDzH2LD2yEFWNwGIC2WkozsB6HF3cDYNoNNdUvFk6uN3YiwsTsqVM/D0z6LEAQ01/SlYvN+Fw0JQ==}
|
||||||
|
|
||||||
@ -15566,6 +15580,12 @@ snapshots:
|
|||||||
expo: 54.0.34(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0)(typescript@5.8.3)
|
expo: 54.0.34(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0)(typescript@5.8.3)
|
||||||
expo-image-loader: 6.0.0(expo@54.0.34)
|
expo-image-loader: 6.0.0(expo@54.0.34)
|
||||||
|
|
||||||
|
expo-image@3.0.11(expo@54.0.34)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0):
|
||||||
|
dependencies:
|
||||||
|
expo: 54.0.34(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0)(typescript@5.8.3)
|
||||||
|
react: 19.1.0
|
||||||
|
react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.1.0)
|
||||||
|
|
||||||
expo-json-utils@0.15.0: {}
|
expo-json-utils@0.15.0: {}
|
||||||
|
|
||||||
expo-keep-awake@15.0.8(expo@54.0.34)(react@19.1.0):
|
expo-keep-awake@15.0.8(expo@54.0.34)(react@19.1.0):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user