Compare commits
21 Commits
8388f49fd4
...
v0.1.13
| Author | SHA1 | Date | |
|---|---|---|---|
| dca76ea394 | |||
| af8ec98bb4 | |||
| c43b913dde | |||
| 256b338ae3 | |||
| ec95406305 | |||
| 6bd1dc8358 | |||
| d3d73c255e | |||
| 72cfbf41f3 | |||
| 86303e5c84 | |||
| c414ddc4fa | |||
| f0fda1947d | |||
| 8a75cffbd1 | |||
| 0b0226368b | |||
| 8f42971187 | |||
| 9e820dbee6 | |||
| f185e5faec | |||
| c6a71ceeba | |||
| 3e762f70e4 | |||
| f311f9f7c5 | |||
| 7b4b7c7a36 | |||
| 418eb1daa1 |
@@ -0,0 +1,185 @@
|
|||||||
|
name: Release APK
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*.*.*'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: build-android-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
if: github.server_url == 'https://github.com'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 60
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Free disk space
|
||||||
|
run: |
|
||||||
|
sudo rm -rf /usr/share/dotnet /usr/local/share/boost /opt/ghc
|
||||||
|
sudo rm -rf /usr/local/share/chromium /usr/local/share/powershell
|
||||||
|
sudo rm -rf /usr/share/swift /usr/local/julia*
|
||||||
|
sudo apt-get autoremove -y > /dev/null 2>&1
|
||||||
|
sudo apt-get clean
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: '17'
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Set up Android SDK
|
||||||
|
uses: android-actions/setup-android@v3
|
||||||
|
|
||||||
|
- name: Accept Android SDK licenses
|
||||||
|
run: yes | sdkmanager --licenses || true
|
||||||
|
|
||||||
|
- name: Install SDK components
|
||||||
|
run: |
|
||||||
|
sdkmanager "platform-tools" "platforms;android-35" "build-tools;35.0.0"
|
||||||
|
|
||||||
|
- name: Cache Gradle
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('package.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-
|
||||||
|
|
||||||
|
- name: Decode keystore
|
||||||
|
env:
|
||||||
|
KEYSTORE_B64: ${{ secrets.KEYSTORE_B64 }}
|
||||||
|
KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}
|
||||||
|
KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }}
|
||||||
|
KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.config/sheethappens
|
||||||
|
echo "$KEYSTORE_B64" | base64 -d > ~/.config/sheethappens/sheethappens.jks
|
||||||
|
{
|
||||||
|
printf 'KEYSTORE_PATH=%s\n' "$HOME/.config/sheethappens/sheethappens.jks"
|
||||||
|
printf 'KEYSTORE_ALIAS=%q\n' "$KEYSTORE_ALIAS"
|
||||||
|
printf 'KEYSTORE_STORE_PASSWORD=%q\n' "$KEYSTORE_STORE_PASSWORD"
|
||||||
|
printf 'KEYSTORE_KEY_PASSWORD=%q\n' "$KEYSTORE_KEY_PASSWORD"
|
||||||
|
} > ~/.config/sheethappens/signing.env
|
||||||
|
chmod 600 ~/.config/sheethappens/signing.env ~/.config/sheethappens/sheethappens.jks
|
||||||
|
|
||||||
|
- name: Verify signing.env
|
||||||
|
run: |
|
||||||
|
echo "Keys present in signing.env:"
|
||||||
|
grep -o '^[^=]*' ~/.config/sheethappens/signing.env
|
||||||
|
source ~/.config/sheethappens/signing.env
|
||||||
|
[ -n "$KEYSTORE_PATH" ] && echo "KEYSTORE_PATH: set" || echo "KEYSTORE_PATH: EMPTY"
|
||||||
|
[ -n "$KEYSTORE_ALIAS" ] && echo "KEYSTORE_ALIAS: set" || echo "KEYSTORE_ALIAS: EMPTY"
|
||||||
|
[ -n "$KEYSTORE_STORE_PASSWORD" ] && echo "KEYSTORE_STORE_PASSWORD: set" || echo "KEYSTORE_STORE_PASSWORD: EMPTY"
|
||||||
|
[ -n "$KEYSTORE_KEY_PASSWORD" ] && echo "KEYSTORE_KEY_PASSWORD: set" || echo "KEYSTORE_KEY_PASSWORD: EMPTY"
|
||||||
|
|
||||||
|
- name: Patch native modules for Android 15 16KB page size
|
||||||
|
run: |
|
||||||
|
WORKLETS="node_modules/react-native-worklets/android/CMakeLists.txt"
|
||||||
|
REANIMATED="node_modules/react-native-reanimated/android/CMakeLists.txt"
|
||||||
|
if [ -f "$WORKLETS" ] && ! grep -q "max-page-size" "$WORKLETS"; then
|
||||||
|
printf '\ntarget_link_options(worklets PRIVATE "-Wl,-z,max-page-size=16384")\n' >> "$WORKLETS"
|
||||||
|
fi
|
||||||
|
if [ -f "$REANIMATED" ] && ! grep -q "max-page-size" "$REANIMATED"; then
|
||||||
|
printf '\ntarget_link_options(reanimated PRIVATE "-Wl,-z,max-page-size=16384")\n' >> "$REANIMATED"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build signed APK
|
||||||
|
run: ./build-apk.sh
|
||||||
|
|
||||||
|
- name: Find built APK
|
||||||
|
id: apk
|
||||||
|
run: |
|
||||||
|
APK=$(ls dist/*.apk | sort | tail -1)
|
||||||
|
echo "path=$APK" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Generate changelog
|
||||||
|
id: changelog
|
||||||
|
run: |
|
||||||
|
PREV_TAG=$(git tag --sort=-version:refname | grep -v "^${{ github.ref_name }}$" | head -1)
|
||||||
|
echo "Previous tag: $PREV_TAG"
|
||||||
|
echo "prev_tag=$PREV_TAG" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
FEATS=$(git log "${PREV_TAG}..HEAD" --pretty=format:"%s" --no-merges \
|
||||||
|
| grep -E "^feat(\([^)]+\))?: " \
|
||||||
|
| sed -E 's/^feat(\([^)]+\))?: //' \
|
||||||
|
| sed 's/^/- /')
|
||||||
|
|
||||||
|
FIXES=$(git log "${PREV_TAG}..HEAD" --pretty=format:"%s" --no-merges \
|
||||||
|
| grep -E "^fix(\([^)]+\))?: " \
|
||||||
|
| sed -E 's/^fix(\([^)]+\))?: //' \
|
||||||
|
| sed 's/^/- /')
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "changelog<<CEOF"
|
||||||
|
[ -n "$FEATS" ] && printf "### What's New\n%s\n\n" "$FEATS"
|
||||||
|
[ -n "$FIXES" ] && printf "### Bug Fixes\n%s\n\n" "$FIXES"
|
||||||
|
echo "CEOF"
|
||||||
|
} >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
name: "SheetHappens ${{ github.ref_name }}"
|
||||||
|
body: |
|
||||||
|
## SheetHappens ${{ github.ref_name }}
|
||||||
|
|
||||||
|
${{ steps.changelog.outputs.changelog }}
|
||||||
|
[Full changelog](https://github.com/${{ github.repository }}/compare/${{ steps.changelog.outputs.prev_tag }}...${{ github.ref_name }}) · [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md)
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
1. Enable "Unknown sources" on the device
|
||||||
|
2. Transfer the APK to the device and open it to install
|
||||||
|
files: ${{ steps.apk.outputs.path }}
|
||||||
|
draft: false
|
||||||
|
prerelease: ${{ contains(github.ref_name, '-') }}
|
||||||
|
|
||||||
|
- name: Create Gitea Release
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
TAG: ${{ github.ref_name }}
|
||||||
|
PREV_TAG: ${{ steps.changelog.outputs.prev_tag }}
|
||||||
|
CHANGELOG: ${{ steps.changelog.outputs.changelog }}
|
||||||
|
GH_REPO: ${{ github.repository }}
|
||||||
|
run: |
|
||||||
|
if [ -z "$GITEA_TOKEN" ]; then
|
||||||
|
echo "GITEA_TOKEN not set — skipping Gitea release"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
BODY="## SheetHappens ${TAG}
|
||||||
|
|
||||||
|
${CHANGELOG}
|
||||||
|
|
||||||
|
[Full changelog](https://homegit.gyozamancave.fr/billisdead/SheetHappens/compare/${PREV_TAG}...${TAG}) · [CHANGELOG.md](https://homegit.gyozamancave.fr/billisdead/SheetHappens/raw/branch/main/CHANGELOG.md)
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
Download the APK from the [GitHub release](https://github.com/${GH_REPO}/releases/tag/${TAG}), enable \"Unknown sources\" on the device and install."
|
||||||
|
|
||||||
|
curl -sf -X POST \
|
||||||
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"https://homegit.gyozamancave.fr/api/v1/repos/billisdead/SheetHappens/releases" \
|
||||||
|
-d "{
|
||||||
|
\"tag_name\": \"${TAG}\",
|
||||||
|
\"name\": \"SheetHappens ${TAG}\",
|
||||||
|
\"body\": $(printf '%s' "$BODY" | jq -Rs .),
|
||||||
|
\"draft\": false,
|
||||||
|
\"prerelease\": $([ "${TAG#*-}" != "${TAG}" ] && echo true || echo false)
|
||||||
|
}"
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to SheetHappens are documented here.
|
||||||
|
Each release links to the full diff for detailed context.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [v0.1.7] — 2026-06-22
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
- Revert splash screen overrides that caused Android crash-to-desktop — expo-router already manages the splash lifecycle internally
|
||||||
|
- Remove redundant `SafeAreaProvider` wrapper (expo-router's `ExpoRoot` already injects it)
|
||||||
|
|
||||||
|
[View changes](https://github.com/pirona/SheetHappens/compare/v0.1.6...v0.1.7)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [v0.1.6] — 2026-06-22
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
- Prevent black screen on launch by holding splash until SecureStore settings load
|
||||||
|
- Add `splash.backgroundColor` to `app.json` to match app theme on Android
|
||||||
|
|
||||||
|
[View changes](https://github.com/pirona/SheetHappens/compare/v0.1.5...v0.1.6)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [v0.1.5] — 2026-06-22
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
- Fix signed APK release: inject signing secrets via env vars, write `signing.env` with `printf %q` to handle special characters
|
||||||
|
- Add `signing.env` verification step in CI
|
||||||
|
|
||||||
|
[View changes](https://github.com/pirona/SheetHappens/compare/v0.1.0...v0.1.5)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [v0.1.0] — Initial release
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- Incident log with SQLite persistence
|
||||||
|
- Voice input via `expo-speech-recognition`
|
||||||
|
- Edit mode, service autocomplete
|
||||||
|
- Dark theme (NativeWind + expo-router)
|
||||||
|
- Onboarding flow
|
||||||
|
- Signed APK via GitHub Actions CI
|
||||||
+89
-19
@@ -1,28 +1,98 @@
|
|||||||
import { Stack } from "expo-router";
|
import { useEffect, useState, Component } from "react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { Stack, router } from "expo-router";
|
||||||
import { StatusBar } from "expo-status-bar";
|
import { StatusBar } from "expo-status-bar";
|
||||||
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
||||||
|
import { View, Text, ScrollView } from "react-native";
|
||||||
import "../global.css";
|
import "../global.css";
|
||||||
import { Colors } from "@/constants/theme";
|
import { Colors } from "@/constants/theme";
|
||||||
|
import { useSettings } from "@/hooks/useSettings";
|
||||||
|
|
||||||
|
// Catch JS errors in release mode and show them on-screen instead of
|
||||||
|
// silently crashing to desktop. Remove once the crash is identified.
|
||||||
|
class CrashBoundary extends Component<
|
||||||
|
{ children: ReactNode },
|
||||||
|
{ error: Error | null }
|
||||||
|
> {
|
||||||
|
state = { error: null };
|
||||||
|
static getDerivedStateFromError(e: Error) {
|
||||||
|
return { error: e };
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const { error } = this.state;
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<View style={{ flex: 1, backgroundColor: "#0f1117", padding: 20, paddingTop: 60 }}>
|
||||||
|
<Text style={{ color: "#ef4444", fontSize: 16, fontWeight: "700", marginBottom: 12 }}>
|
||||||
|
CRASH — copie ce texte
|
||||||
|
</Text>
|
||||||
|
<ScrollView>
|
||||||
|
<Text style={{ color: "#fff", fontSize: 11, fontFamily: "monospace" }}>
|
||||||
|
{String(error)}{"\n\n"}{(error as any).stack}
|
||||||
|
</Text>
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return this.props.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default function RootLayout() {
|
export default function RootLayout() {
|
||||||
|
const { settings, ready } = useSettings();
|
||||||
|
const [jsError, setJsError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const prev = (ErrorUtils as any).getGlobalHandler();
|
||||||
|
(ErrorUtils as any).setGlobalHandler((e: Error, isFatal: boolean) => {
|
||||||
|
if (isFatal) setJsError(`${String(e)}\n\n${(e as any).stack ?? ""}`);
|
||||||
|
prev?.(e, isFatal);
|
||||||
|
});
|
||||||
|
return () => (ErrorUtils as any).setGlobalHandler(prev);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ready) return;
|
||||||
|
if (!settings.onboardingDone) {
|
||||||
|
router.replace("/onboarding");
|
||||||
|
}
|
||||||
|
}, [ready, settings.onboardingDone]);
|
||||||
|
|
||||||
|
if (jsError) {
|
||||||
|
return (
|
||||||
|
<View style={{ flex: 1, backgroundColor: "#0f1117", padding: 20, paddingTop: 60 }}>
|
||||||
|
<Text style={{ color: "#ef4444", fontSize: 16, fontWeight: "700", marginBottom: 12 }}>
|
||||||
|
FATAL JS ERROR — copie ce texte
|
||||||
|
</Text>
|
||||||
|
<ScrollView>
|
||||||
|
<Text style={{ color: "#fff", fontSize: 11, fontFamily: "monospace" }}>
|
||||||
|
{jsError}
|
||||||
|
</Text>
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GestureHandlerRootView style={{ flex: 1, backgroundColor: Colors.bg }}>
|
<CrashBoundary>
|
||||||
<StatusBar style="light" />
|
<GestureHandlerRootView style={{ flex: 1, backgroundColor: Colors.bg }}>
|
||||||
<Stack
|
<StatusBar style="light" />
|
||||||
screenOptions={{
|
<Stack
|
||||||
headerStyle: { backgroundColor: Colors.surface },
|
screenOptions={{
|
||||||
headerTintColor: Colors.text1,
|
headerStyle: { backgroundColor: Colors.surface },
|
||||||
headerTitleStyle: { color: Colors.text1 },
|
headerTintColor: Colors.text1,
|
||||||
contentStyle: { backgroundColor: Colors.bg },
|
headerTitleStyle: { color: Colors.text1 },
|
||||||
animation: "slide_from_right",
|
contentStyle: { backgroundColor: Colors.bg },
|
||||||
}}
|
animation: "slide_from_right",
|
||||||
>
|
}}
|
||||||
<Stack.Screen name="index" options={{ title: "SheetHappens" }} />
|
>
|
||||||
<Stack.Screen name="new" options={{ title: "New Incident", presentation: "modal" }} />
|
<Stack.Screen name="index" options={{ title: "SheetHappens" }} />
|
||||||
<Stack.Screen name="incident/[id]" options={{ title: "Incident" }} />
|
<Stack.Screen name="new" options={{ title: "New Incident", presentation: "modal" }} />
|
||||||
<Stack.Screen name="settings" options={{ title: "Settings" }} />
|
<Stack.Screen name="incident/[id]" options={{ title: "Incident" }} />
|
||||||
<Stack.Screen name="onboarding" options={{ headerShown: false }} />
|
<Stack.Screen name="settings" options={{ title: "Settings" }} />
|
||||||
</Stack>
|
<Stack.Screen name="onboarding" options={{ headerShown: false }} />
|
||||||
</GestureHandlerRootView>
|
</Stack>
|
||||||
|
</GestureHandlerRootView>
|
||||||
|
</CrashBoundary>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ import {
|
|||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { useLocalSearchParams, router, useNavigation } from "expo-router";
|
import { useLocalSearchParams, router, useNavigation } from "expo-router";
|
||||||
import * as Clipboard from "expo-clipboard";
|
import * as Clipboard from "expo-clipboard";
|
||||||
|
import * as SecureStore from "expo-secure-store";
|
||||||
import { getIncidentById, updateIncident } from "@/lib/db";
|
import { getIncidentById, updateIncident } from "@/lib/db";
|
||||||
import { renderMarkdown } from "@/lib/markdown";
|
import { renderMarkdown } from "@/lib/markdown";
|
||||||
|
import { pushIncidentToGitea } from "@/lib/gitea";
|
||||||
import type { Incident } from "@/types/incident";
|
import type { Incident } from "@/types/incident";
|
||||||
import { Colors } from "@/constants/theme";
|
import { Colors } from "@/constants/theme";
|
||||||
|
|
||||||
@@ -38,6 +40,7 @@ export default function IncidentDetailScreen() {
|
|||||||
const { id } = useLocalSearchParams<{ id: string }>();
|
const { id } = useLocalSearchParams<{ id: string }>();
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
const [incident, setIncident] = useState<Incident | null>(null);
|
const [incident, setIncident] = useState<Incident | null>(null);
|
||||||
|
const [pushing, setPushing] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id) {
|
if (id) {
|
||||||
@@ -56,6 +59,33 @@ export default function IncidentDetailScreen() {
|
|||||||
setIncident({ ...incident, status: "resolved" });
|
setIncident({ ...incident, status: "resolved" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleGiteaPush() {
|
||||||
|
if (!incident) return;
|
||||||
|
const [url, token, owner, repo, template] = await Promise.all([
|
||||||
|
SecureStore.getItemAsync("gitea_url"),
|
||||||
|
SecureStore.getItemAsync("gitea_token"),
|
||||||
|
SecureStore.getItemAsync("gitea_owner"),
|
||||||
|
SecureStore.getItemAsync("gitea_repo"),
|
||||||
|
SecureStore.getItemAsync("md_template"),
|
||||||
|
]);
|
||||||
|
if (!url || !token || !owner || !repo) {
|
||||||
|
Alert.alert("Gitea not configured", "Set up Gitea in Settings first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setPushing(true);
|
||||||
|
const result = await pushIncidentToGitea(
|
||||||
|
incident,
|
||||||
|
{ url, token, owner, repo },
|
||||||
|
template ?? undefined
|
||||||
|
);
|
||||||
|
setPushing(false);
|
||||||
|
if (result.success) {
|
||||||
|
Alert.alert("Pushed", result.url ? `${result.url}` : "Push successful.");
|
||||||
|
} else {
|
||||||
|
Alert.alert("Push failed", result.error ?? "Unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleExport() {
|
async function handleExport() {
|
||||||
if (!incident) return;
|
if (!incident) return;
|
||||||
const md = renderMarkdown(incident);
|
const md = renderMarkdown(incident);
|
||||||
@@ -188,6 +218,29 @@ export default function IncidentDetailScreen() {
|
|||||||
</Text>
|
</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
|
|
||||||
|
<Pressable
|
||||||
|
onPress={handleGiteaPush}
|
||||||
|
disabled={pushing}
|
||||||
|
style={{
|
||||||
|
backgroundColor: pushing ? Colors.surface2 : Colors.surface,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: pushing ? Colors.border : Colors.primary,
|
||||||
|
borderRadius: 10,
|
||||||
|
padding: 14,
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: pushing ? Colors.textDim : Colors.primary,
|
||||||
|
fontWeight: "600",
|
||||||
|
fontSize: 15,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{pushing ? "Pushing…" : "Push to Gitea"}
|
||||||
|
</Text>
|
||||||
|
</Pressable>
|
||||||
|
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={() => router.push({ pathname: "/new", params: { editId: incident.id } })}
|
onPress={() => router.push({ pathname: "/new", params: { editId: incident.id } })}
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
+12
-1
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
@@ -8,12 +8,23 @@ import {
|
|||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import { getIncidents } from "@/lib/db";
|
import { getIncidents } from "@/lib/db";
|
||||||
|
import { useSettings } from "@/hooks/useSettings";
|
||||||
import type { Incident } from "@/types/incident";
|
import type { Incident } from "@/types/incident";
|
||||||
import { Colors } from "@/constants/theme";
|
import { Colors } from "@/constants/theme";
|
||||||
|
|
||||||
export default function HomeScreen() {
|
export default function HomeScreen() {
|
||||||
const [incidents, setIncidents] = useState<Incident[]>([]);
|
const [incidents, setIncidents] = useState<Incident[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const { settings, ready } = useSettings();
|
||||||
|
const didRedirect = useRef(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ready || didRedirect.current) return;
|
||||||
|
if (settings.homeView === "capture") {
|
||||||
|
didRedirect.current = true;
|
||||||
|
router.push("/new");
|
||||||
|
}
|
||||||
|
}, [ready, settings.homeView]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadIncidents();
|
loadIncidents();
|
||||||
|
|||||||
+166
-17
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
@@ -9,9 +9,15 @@ import {
|
|||||||
Platform,
|
Platform,
|
||||||
Alert,
|
Alert,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { router } from "expo-router";
|
import { router, useLocalSearchParams, useNavigation } from "expo-router";
|
||||||
import { createIncident } from "@/lib/db";
|
import {
|
||||||
|
createIncident,
|
||||||
|
updateIncident,
|
||||||
|
getIncidentById,
|
||||||
|
getDistinctServices,
|
||||||
|
} from "@/lib/db";
|
||||||
import { Colors } from "@/constants/theme";
|
import { Colors } from "@/constants/theme";
|
||||||
|
import { useVoice } from "@/hooks/useVoice";
|
||||||
import type { IncidentDraft } from "@/types/incident";
|
import type { IncidentDraft } from "@/types/incident";
|
||||||
|
|
||||||
const FIELD_STYLE = {
|
const FIELD_STYLE = {
|
||||||
@@ -38,10 +44,14 @@ const LABEL_STYLE = {
|
|||||||
fontWeight: "600" as const,
|
fontWeight: "600" as const,
|
||||||
textTransform: "uppercase" as const,
|
textTransform: "uppercase" as const,
|
||||||
letterSpacing: 0.5,
|
letterSpacing: 0.5,
|
||||||
marginBottom: 6,
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
type VoiceField = "title" | "symptom" | "rootCause" | "fix";
|
||||||
|
|
||||||
export default function NewIncidentScreen() {
|
export default function NewIncidentScreen() {
|
||||||
|
const { editId } = useLocalSearchParams<{ editId?: string }>();
|
||||||
|
const navigation = useNavigation();
|
||||||
|
|
||||||
const [form, setForm] = useState<IncidentDraft>({
|
const [form, setForm] = useState<IncidentDraft>({
|
||||||
title: "",
|
title: "",
|
||||||
service: "",
|
service: "",
|
||||||
@@ -53,6 +63,59 @@ export default function NewIncidentScreen() {
|
|||||||
});
|
});
|
||||||
const [tagInput, setTagInput] = useState("");
|
const [tagInput, setTagInput] = useState("");
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
const [knownServices, setKnownServices] = useState<string[]>([]);
|
||||||
|
const [serviceFocused, setServiceFocused] = useState(false);
|
||||||
|
|
||||||
|
const { state: voiceState, transcript, start: startVoice, stop: stopVoice } = useVoice();
|
||||||
|
const voiceFieldRef = useRef<VoiceField | null>(null);
|
||||||
|
const [listeningField, setListeningField] = useState<VoiceField | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getDistinctServices().then(setKnownServices);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editId) return;
|
||||||
|
navigation.setOptions({ title: "Edit Incident" });
|
||||||
|
getIncidentById(editId).then((inc) => {
|
||||||
|
if (!inc) return;
|
||||||
|
setForm({
|
||||||
|
title: inc.title,
|
||||||
|
service: inc.service,
|
||||||
|
symptom: inc.symptom,
|
||||||
|
rootCause: inc.rootCause,
|
||||||
|
fix: inc.fix,
|
||||||
|
status: inc.status,
|
||||||
|
tags: inc.tags,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [editId]);
|
||||||
|
|
||||||
|
// Live-fill the active voice field as transcript updates
|
||||||
|
useEffect(() => {
|
||||||
|
const field = voiceFieldRef.current;
|
||||||
|
if (transcript && field) {
|
||||||
|
setForm((f) => ({ ...f, [field]: transcript }));
|
||||||
|
}
|
||||||
|
}, [transcript]);
|
||||||
|
|
||||||
|
// Clear listening state when recognition ends
|
||||||
|
useEffect(() => {
|
||||||
|
if ((voiceState === "idle" || voiceState === "error") && voiceFieldRef.current !== null) {
|
||||||
|
voiceFieldRef.current = null;
|
||||||
|
setListeningField(null);
|
||||||
|
}
|
||||||
|
}, [voiceState]);
|
||||||
|
|
||||||
|
function handleMic(field: VoiceField) {
|
||||||
|
if (listeningField !== null) {
|
||||||
|
stopVoice();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
voiceFieldRef.current = field;
|
||||||
|
setListeningField(field);
|
||||||
|
startVoice("fr-FR");
|
||||||
|
}
|
||||||
|
|
||||||
function set(key: keyof IncidentDraft) {
|
function set(key: keyof IncidentDraft) {
|
||||||
return (value: string) => setForm((f) => ({ ...f, [key]: value }));
|
return (value: string) => setForm((f) => ({ ...f, [key]: value }));
|
||||||
@@ -77,15 +140,71 @@ export default function NewIncidentScreen() {
|
|||||||
}
|
}
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
try {
|
try {
|
||||||
await createIncident(form);
|
if (editId) {
|
||||||
|
await updateIncident(editId, form);
|
||||||
|
} else {
|
||||||
|
await createIncident(form);
|
||||||
|
}
|
||||||
router.back();
|
router.back();
|
||||||
} catch (e) {
|
} catch {
|
||||||
Alert.alert("Error", "Failed to save incident.");
|
Alert.alert("Error", "Failed to save incident.");
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const filteredServices =
|
||||||
|
serviceFocused && form.service.length > 0
|
||||||
|
? knownServices.filter(
|
||||||
|
(s) =>
|
||||||
|
s.toLowerCase().includes(form.service.toLowerCase()) &&
|
||||||
|
s !== form.service
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
function micButton(field: VoiceField) {
|
||||||
|
const active = listeningField === field;
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
onPress={() => handleMic(field)}
|
||||||
|
style={{
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
paddingVertical: 3,
|
||||||
|
borderRadius: 4,
|
||||||
|
backgroundColor: active ? "#ef444420" : Colors.surface2,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: active ? Colors.danger : Colors.border,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: active ? Colors.danger : Colors.text2,
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: "700",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{active ? "STOP" : "MIC"}
|
||||||
|
</Text>
|
||||||
|
</Pressable>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function labelRow(label: string, field: VoiceField) {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: 6,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text style={LABEL_STYLE}>{label}</Text>
|
||||||
|
{micButton(field)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
style={{ flex: 1, backgroundColor: Colors.bg }}
|
style={{ flex: 1, backgroundColor: Colors.bg }}
|
||||||
@@ -95,7 +214,7 @@ export default function NewIncidentScreen() {
|
|||||||
contentContainerStyle={{ padding: 16, paddingBottom: 32 }}
|
contentContainerStyle={{ padding: 16, paddingBottom: 32 }}
|
||||||
keyboardShouldPersistTaps="handled"
|
keyboardShouldPersistTaps="handled"
|
||||||
>
|
>
|
||||||
<Text style={LABEL_STYLE}>Title *</Text>
|
{labelRow("Title *", "title")}
|
||||||
<TextInput
|
<TextInput
|
||||||
style={FIELD_STYLE}
|
style={FIELD_STYLE}
|
||||||
value={form.title}
|
value={form.title}
|
||||||
@@ -105,18 +224,50 @@ export default function NewIncidentScreen() {
|
|||||||
returnKeyType="next"
|
returnKeyType="next"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Text style={LABEL_STYLE}>Service</Text>
|
<Text style={[LABEL_STYLE, { marginBottom: 6 }]}>Service</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={FIELD_STYLE}
|
style={FIELD_STYLE}
|
||||||
value={form.service}
|
value={form.service}
|
||||||
onChangeText={set("service")}
|
onChangeText={set("service")}
|
||||||
|
onFocus={() => setServiceFocused(true)}
|
||||||
|
onBlur={() => setTimeout(() => setServiceFocused(false), 150)}
|
||||||
placeholder="e.g. ghost-blog, k3s-cluster, haproxy"
|
placeholder="e.g. ghost-blog, k3s-cluster, haproxy"
|
||||||
placeholderTextColor={Colors.textDim}
|
placeholderTextColor={Colors.textDim}
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
returnKeyType="next"
|
returnKeyType="next"
|
||||||
/>
|
/>
|
||||||
|
{filteredServices.length > 0 && (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
backgroundColor: Colors.surface2,
|
||||||
|
borderRadius: 8,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: Colors.border,
|
||||||
|
marginTop: -12,
|
||||||
|
marginBottom: 16,
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{filteredServices.slice(0, 5).map((s, i) => (
|
||||||
|
<Pressable
|
||||||
|
key={s}
|
||||||
|
onPress={() => {
|
||||||
|
setForm((f) => ({ ...f, service: s }));
|
||||||
|
setServiceFocused(false);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
padding: 12,
|
||||||
|
borderTopWidth: i === 0 ? 0 : 1,
|
||||||
|
borderTopColor: Colors.border,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text style={{ color: Colors.text1, fontSize: 14 }}>{s}</Text>
|
||||||
|
</Pressable>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
<Text style={LABEL_STYLE}>Symptom</Text>
|
{labelRow("Symptom", "symptom")}
|
||||||
<TextInput
|
<TextInput
|
||||||
style={MONO_FIELD_STYLE}
|
style={MONO_FIELD_STYLE}
|
||||||
value={form.symptom}
|
value={form.symptom}
|
||||||
@@ -127,7 +278,7 @@ export default function NewIncidentScreen() {
|
|||||||
returnKeyType="next"
|
returnKeyType="next"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Text style={LABEL_STYLE}>Root Cause</Text>
|
{labelRow("Root Cause", "rootCause")}
|
||||||
<TextInput
|
<TextInput
|
||||||
style={MONO_FIELD_STYLE}
|
style={MONO_FIELD_STYLE}
|
||||||
value={form.rootCause}
|
value={form.rootCause}
|
||||||
@@ -138,7 +289,7 @@ export default function NewIncidentScreen() {
|
|||||||
returnKeyType="next"
|
returnKeyType="next"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Text style={LABEL_STYLE}>Fix Applied</Text>
|
{labelRow("Fix Applied", "fix")}
|
||||||
<TextInput
|
<TextInput
|
||||||
style={MONO_FIELD_STYLE}
|
style={MONO_FIELD_STYLE}
|
||||||
value={form.fix}
|
value={form.fix}
|
||||||
@@ -149,7 +300,7 @@ export default function NewIncidentScreen() {
|
|||||||
returnKeyType="done"
|
returnKeyType="done"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Text style={LABEL_STYLE}>Tags</Text>
|
<Text style={[LABEL_STYLE, { marginBottom: 6 }]}>Tags</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={FIELD_STYLE}
|
style={FIELD_STYLE}
|
||||||
value={tagInput}
|
value={tagInput}
|
||||||
@@ -181,7 +332,7 @@ export default function NewIncidentScreen() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text style={{ color: Colors.text2, fontSize: 13 }}>{tag}</Text>
|
<Text style={{ color: Colors.text2, fontSize: 13 }}>{tag}</Text>
|
||||||
<Text style={{ color: Colors.textDim, fontSize: 12 }}>✕</Text>
|
<Text style={{ color: Colors.textDim, fontSize: 12 }}>x</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
@@ -198,10 +349,8 @@ export default function NewIncidentScreen() {
|
|||||||
marginTop: 8,
|
marginTop: 8,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text
|
<Text style={{ color: "#fff", fontSize: 16, fontWeight: "700" }}>
|
||||||
style={{ color: "#fff", fontSize: 16, fontWeight: "700" }}
|
{saving ? "Saving…" : editId ? "Save Changes" : "Save Incident"}
|
||||||
>
|
|
||||||
{saving ? "Saving…" : "Save Incident"}
|
|
||||||
</Text>
|
</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
Executable
+189
@@ -0,0 +1,189 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# build-apk.sh — Build a signed release APK locally without expo.dev / EAS.
|
||||||
|
#
|
||||||
|
# Prerequisites (first time only):
|
||||||
|
# 1. Generate a keystore:
|
||||||
|
# keytool -genkey -v -keystore ~/.config/sheethappens/sheethappens.jks \
|
||||||
|
# -alias sheethappens -keyalg RSA -keysize 2048 -validity 10000
|
||||||
|
# 2. Fill in signing credentials:
|
||||||
|
# mkdir -p ~/.config/sheethappens
|
||||||
|
# cat > ~/.config/sheethappens/signing.env <<EOF
|
||||||
|
# KEYSTORE_PATH=~/.config/sheethappens/sheethappens.jks
|
||||||
|
# KEYSTORE_ALIAS=sheethappens
|
||||||
|
# KEYSTORE_STORE_PASSWORD=your_store_password
|
||||||
|
# KEYSTORE_KEY_PASSWORD=your_key_password
|
||||||
|
# EOF
|
||||||
|
# chmod 600 ~/.config/sheethappens/signing.env
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./build-apk.sh # → dist/sheethappens-YYYYMMDD-HHMM.apk
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
SIGNING_ENV="$HOME/.config/sheethappens/signing.env"
|
||||||
|
DIST_DIR="$SCRIPT_DIR/dist"
|
||||||
|
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||||
|
info() { echo -e "${GREEN}[build]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[warn]${NC} $*"; }
|
||||||
|
abort() { echo -e "${RED}[error]${NC} $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# ─── 1. Signing credentials ────────────────────────────────────────────────
|
||||||
|
if [ ! -f "$SIGNING_ENV" ]; then
|
||||||
|
abort "Missing signing.env — see comments at top of this script."
|
||||||
|
fi
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$SIGNING_ENV"
|
||||||
|
|
||||||
|
[ -z "${KEYSTORE_PATH:-}" ] && abort "KEYSTORE_PATH not set in signing.env"
|
||||||
|
[ -z "${KEYSTORE_ALIAS:-}" ] && abort "KEYSTORE_ALIAS not set in signing.env"
|
||||||
|
[ -z "${KEYSTORE_STORE_PASSWORD:-}" ] && abort "KEYSTORE_STORE_PASSWORD not set in signing.env"
|
||||||
|
[ -z "${KEYSTORE_KEY_PASSWORD:-}" ] && abort "KEYSTORE_KEY_PASSWORD not set in signing.env"
|
||||||
|
|
||||||
|
KEYSTORE_PATH_EXPANDED="${KEYSTORE_PATH/#\$HOME/$HOME}"
|
||||||
|
KEYSTORE_PATH_EXPANDED="${KEYSTORE_PATH_EXPANDED/#~/$HOME}"
|
||||||
|
|
||||||
|
[ ! -f "$KEYSTORE_PATH_EXPANDED" ] && \
|
||||||
|
abort "Keystore not found: $KEYSTORE_PATH_EXPANDED"
|
||||||
|
|
||||||
|
# ─── 2. Java check ─────────────────────────────────────────────────────────
|
||||||
|
if [ -z "${JAVA_HOME:-}" ] || "$JAVA_HOME/bin/java" -version 2>&1 | grep -qE '"(2[5-9]|[3-9][0-9])\.'; then
|
||||||
|
if [ -d "$HOME/jdk21" ]; then
|
||||||
|
export JAVA_HOME="$HOME/jdk21"
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
info "Using local JDK 21: $JAVA_HOME"
|
||||||
|
else
|
||||||
|
abort "Java 25+ detected but Gradle only supports ≤ Java 24.\nInstall JDK 21 and set JAVA_HOME."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
info "Java: $("$JAVA_HOME/bin/java" -version 2>&1 | head -1)"
|
||||||
|
|
||||||
|
# ─── 3. Android SDK ────────────────────────────────────────────────────────
|
||||||
|
if [ -z "${ANDROID_HOME:-}" ]; then
|
||||||
|
for candidate in "$HOME/android-sdk" "$HOME/Android/Sdk" "/opt/android-sdk"; do
|
||||||
|
if [ -d "$candidate/platform-tools" ]; then
|
||||||
|
export ANDROID_HOME="$candidate"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
[ -z "${ANDROID_HOME:-}" ] && abort "Android SDK not found. Set ANDROID_HOME."
|
||||||
|
export PATH="$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/cmdline-tools/latest/bin"
|
||||||
|
info "Android SDK: $ANDROID_HOME"
|
||||||
|
|
||||||
|
# ─── 4. expo prebuild ──────────────────────────────────────────────────────
|
||||||
|
info "Running expo prebuild (Android)…"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
npx expo prebuild --platform android --clean --no-install
|
||||||
|
|
||||||
|
# ─── 4.5. Patch native modules — 16KB page size (Android 15) ──────────────
|
||||||
|
info "Patching native modules for 16KB page size support (Android 15)…"
|
||||||
|
|
||||||
|
WORKLETS_CMAKE="$SCRIPT_DIR/node_modules/react-native-worklets/android/CMakeLists.txt"
|
||||||
|
REANIMATED_CMAKE="$SCRIPT_DIR/node_modules/react-native-reanimated/android/CMakeLists.txt"
|
||||||
|
|
||||||
|
if [ -f "$WORKLETS_CMAKE" ] && ! grep -q "max-page-size" "$WORKLETS_CMAKE"; then
|
||||||
|
printf '\ntarget_link_options(worklets PRIVATE "-Wl,-z,max-page-size=16384")\n' >> "$WORKLETS_CMAKE"
|
||||||
|
info "Patched react-native-worklets for 16KB pages"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$REANIMATED_CMAKE" ] && ! grep -q "max-page-size" "$REANIMATED_CMAKE"; then
|
||||||
|
printf '\ntarget_link_options(reanimated PRIVATE "-Wl,-z,max-page-size=16384")\n' >> "$REANIMATED_CMAKE"
|
||||||
|
info "Patched react-native-reanimated for 16KB pages"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── 4.6. Disable New Architecture (Android 15 compat) ────────────────────
|
||||||
|
GRADLE_PROPS_NEWARCH="$SCRIPT_DIR/android/gradle.properties"
|
||||||
|
if [ -f "$GRADLE_PROPS_NEWARCH" ]; then
|
||||||
|
sed -i 's/^newArchEnabled=true/newArchEnabled=false/' "$GRADLE_PROPS_NEWARCH"
|
||||||
|
if ! grep -q "^newArchEnabled=" "$GRADLE_PROPS_NEWARCH"; then
|
||||||
|
echo "newArchEnabled=false" >> "$GRADLE_PROPS_NEWARCH"
|
||||||
|
fi
|
||||||
|
info "New Architecture disabled in gradle.properties"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── 5. Patch build.gradle — inject release signingConfig ──────────────────
|
||||||
|
info "Patching android/app/build.gradle for release signing…"
|
||||||
|
|
||||||
|
python3 - "$SCRIPT_DIR/android/app/build.gradle" << 'PYEOF'
|
||||||
|
import sys, re
|
||||||
|
|
||||||
|
path = sys.argv[1]
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
if 'MYAPP_UPLOAD_STORE_FILE' in content:
|
||||||
|
print('[patch] build.gradle already patched, skipping.')
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
release_signing = """\n release {
|
||||||
|
if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
|
||||||
|
storeFile file(MYAPP_UPLOAD_STORE_FILE)
|
||||||
|
storePassword MYAPP_UPLOAD_STORE_PASSWORD
|
||||||
|
keyAlias MYAPP_UPLOAD_KEY_ALIAS
|
||||||
|
keyPassword MYAPP_UPLOAD_KEY_PASSWORD
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
content = re.sub(
|
||||||
|
r'(signingConfigs\s*\{[\s\S]*?debug\s*\{[\s\S]*?\})',
|
||||||
|
r'\1' + release_signing,
|
||||||
|
content,
|
||||||
|
count=1
|
||||||
|
)
|
||||||
|
|
||||||
|
def replace_release_signing(m):
|
||||||
|
return m.group(0).replace('signingConfig signingConfigs.debug',
|
||||||
|
'signingConfig signingConfigs.release', 1)
|
||||||
|
|
||||||
|
content = re.sub(
|
||||||
|
r'release\s*\{[^}]*signingConfig\s+signingConfigs\.debug',
|
||||||
|
replace_release_signing,
|
||||||
|
content,
|
||||||
|
count=1,
|
||||||
|
flags=re.DOTALL
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
print('[patch] build.gradle patched for release signing.')
|
||||||
|
PYEOF
|
||||||
|
|
||||||
|
# ─── 6. Inject signing props into gradle.properties ────────────────────────
|
||||||
|
info "Injecting signing credentials into gradle.properties…"
|
||||||
|
GRADLE_PROPS="$SCRIPT_DIR/android/gradle.properties"
|
||||||
|
|
||||||
|
sed -i '/^# --- sheethappens release signing/,/^# --- end signing/d' "$GRADLE_PROPS" 2>/dev/null || true
|
||||||
|
|
||||||
|
cat >> "$GRADLE_PROPS" << EOF
|
||||||
|
|
||||||
|
# --- sheethappens release signing (injected by build-apk.sh — wiped after build)
|
||||||
|
MYAPP_UPLOAD_STORE_FILE=$KEYSTORE_PATH_EXPANDED
|
||||||
|
MYAPP_UPLOAD_STORE_PASSWORD=$KEYSTORE_STORE_PASSWORD
|
||||||
|
MYAPP_UPLOAD_KEY_ALIAS=$KEYSTORE_ALIAS
|
||||||
|
MYAPP_UPLOAD_KEY_PASSWORD=$KEYSTORE_KEY_PASSWORD
|
||||||
|
# --- end signing
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# ─── 7. Gradle build ───────────────────────────────────────────────────────
|
||||||
|
cd "$SCRIPT_DIR/android"
|
||||||
|
chmod +x gradlew
|
||||||
|
info "Building APK (release)…"
|
||||||
|
./gradlew assembleRelease --no-daemon
|
||||||
|
|
||||||
|
# ─── 8. Wipe credentials from gradle.properties ───────────────────────────
|
||||||
|
sed -i '/^# --- sheethappens release signing/,/^# --- end signing/d' "$GRADLE_PROPS"
|
||||||
|
info "Signing credentials wiped from gradle.properties."
|
||||||
|
|
||||||
|
# ─── 9. Copy to dist/ ─────────────────────────────────────────────────────
|
||||||
|
mkdir -p "$DIST_DIR"
|
||||||
|
TIMESTAMP="$(date +%Y%m%d-%H%M)"
|
||||||
|
OUTPUT="$DIST_DIR/sheethappens-$TIMESTAMP.apk"
|
||||||
|
cp "app/build/outputs/apk/release/app-release.apk" "$OUTPUT"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
info "Build complete!"
|
||||||
|
echo -e " ${GREEN}→ $OUTPUT${NC}"
|
||||||
|
echo ""
|
||||||
Generated
+520
-212
@@ -19,8 +19,8 @@
|
|||||||
"expo-sqlite": "~56.0.5",
|
"expo-sqlite": "~56.0.5",
|
||||||
"expo-status-bar": "~56.0.4",
|
"expo-status-bar": "~56.0.4",
|
||||||
"nativewind": "~4.2.5",
|
"nativewind": "~4.2.5",
|
||||||
"react": "19.2.7",
|
"react": "19.2.3",
|
||||||
"react-native": "^0.85.3",
|
"react-native": "0.85.3",
|
||||||
"react-native-gesture-handler": "~3.0.2",
|
"react-native-gesture-handler": "~3.0.2",
|
||||||
"react-native-reanimated": "~4.4.1",
|
"react-native-reanimated": "~4.4.1",
|
||||||
"react-native-safe-area-context": "~5.8.0",
|
"react-native-safe-area-context": "~5.8.0",
|
||||||
@@ -535,6 +535,22 @@
|
|||||||
"@babel/core": "^7.0.0-0"
|
"@babel/core": "^7.0.0-0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/plugin-transform-arrow-functions": {
|
||||||
|
"version": "7.29.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz",
|
||||||
|
"integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-plugin-utils": "^7.29.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "^7.0.0-0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/plugin-transform-async-generator-functions": {
|
"node_modules/@babel/plugin-transform-async-generator-functions": {
|
||||||
"version": "7.29.7",
|
"version": "7.29.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz",
|
||||||
@@ -908,6 +924,38 @@
|
|||||||
"@babel/core": "^7.0.0-0"
|
"@babel/core": "^7.0.0-0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/plugin-transform-react-jsx-self": {
|
||||||
|
"version": "7.29.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz",
|
||||||
|
"integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-plugin-utils": "^7.29.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "^7.0.0-0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/plugin-transform-react-jsx-source": {
|
||||||
|
"version": "7.29.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz",
|
||||||
|
"integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-plugin-utils": "^7.29.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "^7.0.0-0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/plugin-transform-react-pure-annotations": {
|
"node_modules/@babel/plugin-transform-react-pure-annotations": {
|
||||||
"version": "7.29.7",
|
"version": "7.29.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.29.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.29.7.tgz",
|
||||||
@@ -924,6 +972,22 @@
|
|||||||
"@babel/core": "^7.0.0-0"
|
"@babel/core": "^7.0.0-0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/plugin-transform-regenerator": {
|
||||||
|
"version": "7.29.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.7.tgz",
|
||||||
|
"integrity": "sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-plugin-utils": "^7.29.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "^7.0.0-0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/plugin-transform-runtime": {
|
"node_modules/@babel/plugin-transform-runtime": {
|
||||||
"version": "7.29.7",
|
"version": "7.29.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.7.tgz",
|
||||||
@@ -944,6 +1008,38 @@
|
|||||||
"@babel/core": "^7.0.0-0"
|
"@babel/core": "^7.0.0-0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/plugin-transform-shorthand-properties": {
|
||||||
|
"version": "7.29.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz",
|
||||||
|
"integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-plugin-utils": "^7.29.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "^7.0.0-0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/plugin-transform-template-literals": {
|
||||||
|
"version": "7.29.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz",
|
||||||
|
"integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-plugin-utils": "^7.29.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "^7.0.0-0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/plugin-transform-typescript": {
|
"node_modules/@babel/plugin-transform-typescript": {
|
||||||
"version": "7.29.7",
|
"version": "7.29.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz",
|
||||||
@@ -1058,99 +1154,6 @@
|
|||||||
"integrity": "sha512-IJkBtN1o8u9BW5fvSii1MyHPQ7Q0HxbWcVBvOrOzgMLpVtZw7R2w94wBTVR7kZwv3w1JNTESMmLA5Sqn1+Z36A==",
|
"integrity": "sha512-IJkBtN1o8u9BW5fvSii1MyHPQ7Q0HxbWcVBvOrOzgMLpVtZw7R2w94wBTVR7kZwv3w1JNTESMmLA5Sqn1+Z36A==",
|
||||||
"license": "MIT AND Apache-2.0"
|
"license": "MIT AND Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/@expo/cli": {
|
|
||||||
"version": "56.1.16",
|
|
||||||
"resolved": "https://registry.npmjs.org/@expo/cli/-/cli-56.1.16.tgz",
|
|
||||||
"integrity": "sha512-VBQn0mqAwc67b9Cn0RVXyeodghomAx5xGRhA/bXaQzuxDjMQk0zIOb6pXMZX7yiIwJW66UZt/zQiJNSv6aWJYw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@expo/code-signing-certificates": "^0.0.6",
|
|
||||||
"@expo/config": "~56.0.9",
|
|
||||||
"@expo/config-plugins": "~56.0.9",
|
|
||||||
"@expo/devcert": "^1.2.1",
|
|
||||||
"@expo/env": "~2.3.0",
|
|
||||||
"@expo/image-utils": "^0.10.1",
|
|
||||||
"@expo/inline-modules": "^0.0.12",
|
|
||||||
"@expo/json-file": "^10.2.0",
|
|
||||||
"@expo/log-box": "^56.0.13",
|
|
||||||
"@expo/metro": "~56.0.0",
|
|
||||||
"@expo/metro-config": "~56.0.14",
|
|
||||||
"@expo/metro-file-map": "^56.0.3",
|
|
||||||
"@expo/osascript": "^2.6.0",
|
|
||||||
"@expo/package-manager": "^1.12.1",
|
|
||||||
"@expo/plist": "^0.7.0",
|
|
||||||
"@expo/prebuild-config": "^56.0.16",
|
|
||||||
"@expo/require-utils": "^56.1.3",
|
|
||||||
"@expo/router-server": "^56.0.14",
|
|
||||||
"@expo/schema-utils": "^56.0.0",
|
|
||||||
"@expo/spawn-async": "^1.8.0",
|
|
||||||
"@expo/ws-tunnel": "^2.0.0",
|
|
||||||
"@expo/xcpretty": "^4.4.4",
|
|
||||||
"@react-native/dev-middleware": "0.85.3",
|
|
||||||
"accepts": "^1.3.8",
|
|
||||||
"arg": "^5.0.2",
|
|
||||||
"bplist-creator": "0.1.0",
|
|
||||||
"bplist-parser": "^0.3.1",
|
|
||||||
"chalk": "^4.0.0",
|
|
||||||
"ci-info": "^3.3.0",
|
|
||||||
"compression": "^1.7.4",
|
|
||||||
"connect": "^3.7.0",
|
|
||||||
"debug": "^4.3.4",
|
|
||||||
"dnssd-advertise": "^1.1.4",
|
|
||||||
"expo-server": "^56.0.5",
|
|
||||||
"fetch-nodeshim": "^0.4.10",
|
|
||||||
"getenv": "^2.0.0",
|
|
||||||
"glob": "^13.0.0",
|
|
||||||
"lan-network": "^0.2.1",
|
|
||||||
"multitars": "^1.0.0",
|
|
||||||
"node-forge": "^1.3.3",
|
|
||||||
"npm-package-arg": "^11.0.0",
|
|
||||||
"ora": "^3.4.0",
|
|
||||||
"picomatch": "^4.0.4",
|
|
||||||
"pretty-format": "^29.7.0",
|
|
||||||
"progress": "^2.0.3",
|
|
||||||
"prompts": "^2.3.2",
|
|
||||||
"resolve-from": "^5.0.0",
|
|
||||||
"semver": "^7.6.0",
|
|
||||||
"send": "^0.19.0",
|
|
||||||
"slugify": "^1.3.4",
|
|
||||||
"stacktrace-parser": "^0.1.10",
|
|
||||||
"structured-headers": "^0.4.1",
|
|
||||||
"terminal-link": "^2.1.1",
|
|
||||||
"toqr": "^0.1.1",
|
|
||||||
"wrap-ansi": "^7.0.0",
|
|
||||||
"ws": "^8.12.1",
|
|
||||||
"zod": "^3.25.76"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"expo-internal": "main.js"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"expo": "*",
|
|
||||||
"expo-router": "*",
|
|
||||||
"react-native": "*"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"expo-router": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"react-native": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@expo/cli/node_modules/semver": {
|
|
||||||
"version": "7.8.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
|
|
||||||
"integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
|
|
||||||
"license": "ISC",
|
|
||||||
"bin": {
|
|
||||||
"semver": "bin/semver.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@expo/code-signing-certificates": {
|
"node_modules/@expo/code-signing-certificates": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.6.tgz",
|
||||||
@@ -1563,9 +1566,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@expo/prebuild-config/node_modules/semver": {
|
"node_modules/@expo/prebuild-config/node_modules/semver": {
|
||||||
"version": "7.8.4",
|
"version": "7.8.5",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
|
||||||
"integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
|
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
@@ -1593,40 +1596,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@expo/router-server": {
|
|
||||||
"version": "56.0.14",
|
|
||||||
"resolved": "https://registry.npmjs.org/@expo/router-server/-/router-server-56.0.14.tgz",
|
|
||||||
"integrity": "sha512-2UCTtZfcq1ZPgp3wk8/+sq9DvFI9UxrPr1jcEKMAF2DGAJLosnpc8GWNNg2hkjt6SHUOdFHIPxujWPYyho2y3A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"debug": "^4.3.4"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@expo/metro-runtime": "^56.0.15",
|
|
||||||
"expo": "*",
|
|
||||||
"expo-constants": "^56.0.18",
|
|
||||||
"expo-font": "^56.0.6",
|
|
||||||
"expo-router": "*",
|
|
||||||
"expo-server": "^56.0.5",
|
|
||||||
"react": "*",
|
|
||||||
"react-dom": "*",
|
|
||||||
"react-server-dom-webpack": "~19.0.1 || ~19.1.2 || ~19.2.1"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@expo/metro-runtime": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"expo-router": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"react-dom": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"react-server-dom-webpack": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@expo/schema-utils": {
|
"node_modules/@expo/schema-utils": {
|
||||||
"version": "56.0.1",
|
"version": "56.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@expo/schema-utils/-/schema-utils-56.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@expo/schema-utils/-/schema-utils-56.0.1.tgz",
|
||||||
@@ -2292,6 +2261,88 @@
|
|||||||
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-native/babel-plugin-codegen": {
|
||||||
|
"version": "0.85.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.85.3.tgz",
|
||||||
|
"integrity": "sha512-Wc94zGfeFG8Njf9SHMPfYZP04kjigkOps6F1TYTvd7ZVXuGxqseCDgxc50LWcOhOCLypI9n3oVVqz81C3p44ZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/traverse": "^7.29.0",
|
||||||
|
"@react-native/codegen": "0.85.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-native/babel-preset": {
|
||||||
|
"version": "0.85.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.85.3.tgz",
|
||||||
|
"integrity": "sha512-fD7fxEhkJB/aF57tWoXjaAWpklfrExYZS3k6aXPP3BQ77DZY7gvf/b7dbirwjID6NVnP1JDRJyTuPBGr0K/vlw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/core": "^7.25.2",
|
||||||
|
"@babel/plugin-proposal-export-default-from": "^7.24.7",
|
||||||
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||||
|
"@babel/plugin-syntax-export-default-from": "^7.24.7",
|
||||||
|
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
|
||||||
|
"@babel/plugin-syntax-optional-chaining": "^7.8.3",
|
||||||
|
"@babel/plugin-transform-async-generator-functions": "^7.25.4",
|
||||||
|
"@babel/plugin-transform-async-to-generator": "^7.24.7",
|
||||||
|
"@babel/plugin-transform-block-scoping": "^7.25.0",
|
||||||
|
"@babel/plugin-transform-class-properties": "^7.25.4",
|
||||||
|
"@babel/plugin-transform-classes": "^7.25.4",
|
||||||
|
"@babel/plugin-transform-destructuring": "^7.24.8",
|
||||||
|
"@babel/plugin-transform-flow-strip-types": "^7.25.2",
|
||||||
|
"@babel/plugin-transform-for-of": "^7.24.7",
|
||||||
|
"@babel/plugin-transform-modules-commonjs": "^7.24.8",
|
||||||
|
"@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7",
|
||||||
|
"@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7",
|
||||||
|
"@babel/plugin-transform-optional-catch-binding": "^7.24.7",
|
||||||
|
"@babel/plugin-transform-optional-chaining": "^7.24.8",
|
||||||
|
"@babel/plugin-transform-private-methods": "^7.24.7",
|
||||||
|
"@babel/plugin-transform-private-property-in-object": "^7.24.7",
|
||||||
|
"@babel/plugin-transform-react-display-name": "^7.24.7",
|
||||||
|
"@babel/plugin-transform-react-jsx": "^7.25.2",
|
||||||
|
"@babel/plugin-transform-react-jsx-self": "^7.24.7",
|
||||||
|
"@babel/plugin-transform-react-jsx-source": "^7.24.7",
|
||||||
|
"@babel/plugin-transform-regenerator": "^7.24.7",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.24.7",
|
||||||
|
"@babel/plugin-transform-typescript": "^7.25.2",
|
||||||
|
"@babel/plugin-transform-unicode-regex": "^7.24.7",
|
||||||
|
"@react-native/babel-plugin-codegen": "0.85.3",
|
||||||
|
"babel-plugin-syntax-hermes-parser": "0.33.3",
|
||||||
|
"babel-plugin-transform-flow-enums": "^0.0.2",
|
||||||
|
"react-refresh": "^0.14.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-native/codegen": {
|
||||||
|
"version": "0.85.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.85.3.tgz",
|
||||||
|
"integrity": "sha512-/JkS1lGLyzBWP1FbgDwaqEf7qShIC6pUC1M0a/YMAd/v4iqR24MRkQWe7jkYvcBQ2LpEhs5NGE9InhxSv21zCA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/core": "^7.25.2",
|
||||||
|
"@babel/parser": "^7.29.0",
|
||||||
|
"hermes-parser": "0.33.3",
|
||||||
|
"invariant": "^2.2.4",
|
||||||
|
"nullthrows": "^1.1.1",
|
||||||
|
"tinyglobby": "^0.2.15",
|
||||||
|
"yargs": "^17.6.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@react-native/community-cli-plugin": {
|
"node_modules/@react-native/community-cli-plugin": {
|
||||||
"version": "0.85.3",
|
"version": "0.85.3",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.85.3.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.85.3.tgz",
|
||||||
@@ -2410,6 +2461,50 @@
|
|||||||
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-native/js-polyfills": {
|
||||||
|
"version": "0.85.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.85.3.tgz",
|
||||||
|
"integrity": "sha512-U2+aMshIXf1uFn77tpBb/xhHWB9vkVrMpt7kkucAugF8hJKYTDGB587X7WwelHduK2KBfhl4giSv0rzZGoef9A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-native/metro-babel-transformer": {
|
||||||
|
"version": "0.85.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.85.3.tgz",
|
||||||
|
"integrity": "sha512-omuKq+r7jM4XvCMIlNMPP7Up3SyB8o5EAdZtF7YXniKyq7UOMBqhYHFqgsdOXr0lT+3ADf7VCJG3sb82jlBrrQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/core": "^7.25.2",
|
||||||
|
"@react-native/babel-preset": "0.85.3",
|
||||||
|
"hermes-parser": "0.33.3",
|
||||||
|
"nullthrows": "^1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-native/metro-config": {
|
||||||
|
"version": "0.85.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.85.3.tgz",
|
||||||
|
"integrity": "sha512-sVo6HepUmCcpdfozEf91lA0FjpLNNZYu/Zi9FiYiAQTK8pzATXDVTqhvdxpFrQn435p5eUTSbllvbH/KN+bnyA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@react-native/js-polyfills": "0.85.3",
|
||||||
|
"@react-native/metro-babel-transformer": "0.85.3",
|
||||||
|
"metro-config": "^0.84.3",
|
||||||
|
"metro-runtime": "^0.84.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@react-native/normalize-colors": {
|
"node_modules/@react-native/normalize-colors": {
|
||||||
"version": "0.85.3",
|
"version": "0.85.3",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.85.3.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.85.3.tgz",
|
||||||
@@ -2445,6 +2540,61 @@
|
|||||||
"integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==",
|
"integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@testing-library/dom": {
|
||||||
|
"version": "10.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
|
||||||
|
"integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/code-frame": "^7.10.4",
|
||||||
|
"@babel/runtime": "^7.12.5",
|
||||||
|
"@types/aria-query": "^5.0.1",
|
||||||
|
"aria-query": "5.3.0",
|
||||||
|
"dom-accessibility-api": "^0.5.9",
|
||||||
|
"lz-string": "^1.5.0",
|
||||||
|
"picocolors": "1.1.1",
|
||||||
|
"pretty-format": "^27.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@testing-library/dom/node_modules/ansi-styles": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@testing-library/dom/node_modules/pretty-format": {
|
||||||
|
"version": "27.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
|
||||||
|
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1",
|
||||||
|
"ansi-styles": "^5.0.0",
|
||||||
|
"react-is": "^17.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@testing-library/dom/node_modules/react-is": {
|
||||||
|
"version": "17.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
|
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/@testing-library/jest-dom": {
|
"node_modules/@testing-library/jest-dom": {
|
||||||
"version": "6.9.1",
|
"version": "6.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz",
|
||||||
@@ -2483,6 +2633,13 @@
|
|||||||
"@testing-library/dom": ">=7.21.4"
|
"@testing-library/dom": ">=7.21.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/aria-query": {
|
||||||
|
"version": "5.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
||||||
|
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/@types/istanbul-lib-coverage": {
|
"node_modules/@types/istanbul-lib-coverage": {
|
||||||
"version": "2.0.6",
|
"version": "2.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
|
||||||
@@ -2803,6 +2960,15 @@
|
|||||||
"integrity": "sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==",
|
"integrity": "sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/babel-plugin-syntax-hermes-parser": {
|
||||||
|
"version": "0.33.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.33.3.tgz",
|
||||||
|
"integrity": "sha512-/Z9xYdaJ1lC0pT9do6TqCqhOSLfZ5Ot8D5za1p+feEfWYupCOfGbhhEXN9r2ZgJtDNUNRw/Z+T2CvAGKBqtqWA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"hermes-parser": "0.33.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/babel-plugin-transform-flow-enums": {
|
"node_modules/babel-plugin-transform-flow-enums": {
|
||||||
"version": "0.0.2",
|
"version": "0.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz",
|
||||||
@@ -2879,49 +3045,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/babel-preset-expo/node_modules/@react-native/babel-plugin-codegen": {
|
|
||||||
"version": "0.85.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.85.3.tgz",
|
|
||||||
"integrity": "sha512-Wc94zGfeFG8Njf9SHMPfYZP04kjigkOps6F1TYTvd7ZVXuGxqseCDgxc50LWcOhOCLypI9n3oVVqz81C3p44ZA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/traverse": "^7.29.0",
|
|
||||||
"@react-native/codegen": "0.85.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/babel-preset-expo/node_modules/@react-native/codegen": {
|
|
||||||
"version": "0.85.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.85.3.tgz",
|
|
||||||
"integrity": "sha512-/JkS1lGLyzBWP1FbgDwaqEf7qShIC6pUC1M0a/YMAd/v4iqR24MRkQWe7jkYvcBQ2LpEhs5NGE9InhxSv21zCA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/core": "^7.25.2",
|
|
||||||
"@babel/parser": "^7.29.0",
|
|
||||||
"hermes-parser": "0.33.3",
|
|
||||||
"invariant": "^2.2.4",
|
|
||||||
"nullthrows": "^1.1.1",
|
|
||||||
"tinyglobby": "^0.2.15",
|
|
||||||
"yargs": "^17.6.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@babel/core": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/babel-preset-expo/node_modules/babel-plugin-syntax-hermes-parser": {
|
|
||||||
"version": "0.33.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.33.3.tgz",
|
|
||||||
"integrity": "sha512-/Z9xYdaJ1lC0pT9do6TqCqhOSLfZ5Ot8D5za1p+feEfWYupCOfGbhhEXN9r2ZgJtDNUNRw/Z+T2CvAGKBqtqWA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"hermes-parser": "0.33.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||||
@@ -3602,6 +3725,13 @@
|
|||||||
"integrity": "sha512-Ndrrf6BMPalkQPd/zubL+4YghH2J9NspapQ09uDXwYbvOPkP0oaqf5CkcwJ0b50kS2O3ul6yVu+jz+RY62Cejg==",
|
"integrity": "sha512-Ndrrf6BMPalkQPd/zubL+4YghH2J9NspapQ09uDXwYbvOPkP0oaqf5CkcwJ0b50kS2O3ul6yVu+jz+RY62Cejg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/dom-accessibility-api": {
|
||||||
|
"version": "0.5.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
||||||
|
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
@@ -4049,6 +4179,121 @@
|
|||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expo/node_modules/@expo/cli": {
|
||||||
|
"version": "56.1.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/@expo/cli/-/cli-56.1.16.tgz",
|
||||||
|
"integrity": "sha512-VBQn0mqAwc67b9Cn0RVXyeodghomAx5xGRhA/bXaQzuxDjMQk0zIOb6pXMZX7yiIwJW66UZt/zQiJNSv6aWJYw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@expo/code-signing-certificates": "^0.0.6",
|
||||||
|
"@expo/config": "~56.0.9",
|
||||||
|
"@expo/config-plugins": "~56.0.9",
|
||||||
|
"@expo/devcert": "^1.2.1",
|
||||||
|
"@expo/env": "~2.3.0",
|
||||||
|
"@expo/image-utils": "^0.10.1",
|
||||||
|
"@expo/inline-modules": "^0.0.12",
|
||||||
|
"@expo/json-file": "^10.2.0",
|
||||||
|
"@expo/log-box": "^56.0.13",
|
||||||
|
"@expo/metro": "~56.0.0",
|
||||||
|
"@expo/metro-config": "~56.0.14",
|
||||||
|
"@expo/metro-file-map": "^56.0.3",
|
||||||
|
"@expo/osascript": "^2.6.0",
|
||||||
|
"@expo/package-manager": "^1.12.1",
|
||||||
|
"@expo/plist": "^0.7.0",
|
||||||
|
"@expo/prebuild-config": "^56.0.16",
|
||||||
|
"@expo/require-utils": "^56.1.3",
|
||||||
|
"@expo/router-server": "^56.0.14",
|
||||||
|
"@expo/schema-utils": "^56.0.0",
|
||||||
|
"@expo/spawn-async": "^1.8.0",
|
||||||
|
"@expo/ws-tunnel": "^2.0.0",
|
||||||
|
"@expo/xcpretty": "^4.4.4",
|
||||||
|
"@react-native/dev-middleware": "0.85.3",
|
||||||
|
"accepts": "^1.3.8",
|
||||||
|
"arg": "^5.0.2",
|
||||||
|
"bplist-creator": "0.1.0",
|
||||||
|
"bplist-parser": "^0.3.1",
|
||||||
|
"chalk": "^4.0.0",
|
||||||
|
"ci-info": "^3.3.0",
|
||||||
|
"compression": "^1.7.4",
|
||||||
|
"connect": "^3.7.0",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"dnssd-advertise": "^1.1.4",
|
||||||
|
"expo-server": "^56.0.5",
|
||||||
|
"fetch-nodeshim": "^0.4.10",
|
||||||
|
"getenv": "^2.0.0",
|
||||||
|
"glob": "^13.0.0",
|
||||||
|
"lan-network": "^0.2.1",
|
||||||
|
"multitars": "^1.0.0",
|
||||||
|
"node-forge": "^1.3.3",
|
||||||
|
"npm-package-arg": "^11.0.0",
|
||||||
|
"ora": "^3.4.0",
|
||||||
|
"picomatch": "^4.0.4",
|
||||||
|
"pretty-format": "^29.7.0",
|
||||||
|
"progress": "^2.0.3",
|
||||||
|
"prompts": "^2.3.2",
|
||||||
|
"resolve-from": "^5.0.0",
|
||||||
|
"semver": "^7.6.0",
|
||||||
|
"send": "^0.19.0",
|
||||||
|
"slugify": "^1.3.4",
|
||||||
|
"stacktrace-parser": "^0.1.10",
|
||||||
|
"structured-headers": "^0.4.1",
|
||||||
|
"terminal-link": "^2.1.1",
|
||||||
|
"toqr": "^0.1.1",
|
||||||
|
"wrap-ansi": "^7.0.0",
|
||||||
|
"ws": "^8.12.1",
|
||||||
|
"zod": "^3.25.76"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"expo-internal": "main.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "*",
|
||||||
|
"expo-router": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"expo-router": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-native": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/expo/node_modules/@expo/cli/node_modules/@expo/router-server": {
|
||||||
|
"version": "56.0.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@expo/router-server/-/router-server-56.0.14.tgz",
|
||||||
|
"integrity": "sha512-2UCTtZfcq1ZPgp3wk8/+sq9DvFI9UxrPr1jcEKMAF2DGAJLosnpc8GWNNg2hkjt6SHUOdFHIPxujWPYyho2y3A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^4.3.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@expo/metro-runtime": "^56.0.15",
|
||||||
|
"expo": "*",
|
||||||
|
"expo-constants": "^56.0.18",
|
||||||
|
"expo-font": "^56.0.6",
|
||||||
|
"expo-router": "*",
|
||||||
|
"expo-server": "^56.0.5",
|
||||||
|
"react": "*",
|
||||||
|
"react-dom": "*",
|
||||||
|
"react-server-dom-webpack": "~19.0.1 || ~19.1.2 || ~19.2.1"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@expo/metro-runtime": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"expo-router": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-server-dom-webpack": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/expo/node_modules/expo-modules-core": {
|
"node_modules/expo/node_modules/expo-modules-core": {
|
||||||
"version": "56.0.17",
|
"version": "56.0.17",
|
||||||
"resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-56.0.17.tgz",
|
"resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-56.0.17.tgz",
|
||||||
@@ -4070,6 +4315,45 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expo/node_modules/react-native-worklets": {
|
||||||
|
"version": "0.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.8.3.tgz",
|
||||||
|
"integrity": "sha512-oCBJROyLU7yG/1R8s0INMflygTH71bx+5XcYkH0CM938TlhSoVbiunE1WVW5FZa51vwYqfLie/IXMX2s1Kh3eg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/plugin-transform-arrow-functions": "^7.27.1",
|
||||||
|
"@babel/plugin-transform-class-properties": "^7.27.1",
|
||||||
|
"@babel/plugin-transform-classes": "^7.28.4",
|
||||||
|
"@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1",
|
||||||
|
"@babel/plugin-transform-optional-chaining": "^7.27.1",
|
||||||
|
"@babel/plugin-transform-shorthand-properties": "^7.27.1",
|
||||||
|
"@babel/plugin-transform-template-literals": "^7.27.1",
|
||||||
|
"@babel/plugin-transform-unicode-regex": "^7.27.1",
|
||||||
|
"@babel/preset-typescript": "^7.27.1",
|
||||||
|
"convert-source-map": "^2.0.0",
|
||||||
|
"semver": "^7.7.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "*",
|
||||||
|
"@react-native/metro-config": "*",
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "0.81 - 0.85"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/expo/node_modules/semver": {
|
||||||
|
"version": "7.8.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
|
||||||
|
"integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/exponential-backoff": {
|
"node_modules/exponential-backoff": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz",
|
||||||
@@ -5180,6 +5464,16 @@
|
|||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lz-string": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"bin": {
|
||||||
|
"lz-string": "bin/bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/makeerror": {
|
"node_modules/makeerror": {
|
||||||
"version": "1.0.12",
|
"version": "1.0.12",
|
||||||
"resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
|
"resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
|
||||||
@@ -5816,9 +6110,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm-package-arg/node_modules/semver": {
|
"node_modules/npm-package-arg/node_modules/semver": {
|
||||||
"version": "7.8.4",
|
"version": "7.8.5",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
|
||||||
"integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
|
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
@@ -6414,9 +6708,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "19.2.7",
|
"version": "19.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
||||||
"integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
|
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -6453,6 +6747,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-dom": {
|
||||||
|
"version": "19.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
|
||||||
|
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"scheduler": "^0.27.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^19.2.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-fast-compare": {
|
"node_modules/react-fast-compare": {
|
||||||
"version": "3.2.2",
|
"version": "3.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
|
||||||
@@ -6910,43 +7217,44 @@
|
|||||||
"react-native": ">=0.82.0"
|
"react-native": ">=0.82.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-native/node_modules/@react-native/codegen": {
|
"node_modules/react-native-worklets": {
|
||||||
"version": "0.85.3",
|
"version": "0.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.85.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.9.2.tgz",
|
||||||
"integrity": "sha512-/JkS1lGLyzBWP1FbgDwaqEf7qShIC6pUC1M0a/YMAd/v4iqR24MRkQWe7jkYvcBQ2LpEhs5NGE9InhxSv21zCA==",
|
"integrity": "sha512-bCBV4dwcoLnqpk0bbL92nNuv6km37DRpamCX/nqNxsRs6LmLeFoFe/a7OKL1pVDi3PY97C0BEhmq8Qq/iNvpVg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
"@babel/plugin-transform-arrow-functions": "^7.27.1",
|
||||||
"@babel/parser": "^7.29.0",
|
"@babel/plugin-transform-class-properties": "^7.28.6",
|
||||||
"hermes-parser": "0.33.3",
|
"@babel/plugin-transform-classes": "^7.28.6",
|
||||||
"invariant": "^2.2.4",
|
"@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6",
|
||||||
"nullthrows": "^1.1.1",
|
"@babel/plugin-transform-optional-chaining": "^7.28.6",
|
||||||
"tinyglobby": "^0.2.15",
|
"@babel/plugin-transform-shorthand-properties": "^7.27.1",
|
||||||
"yargs": "^17.6.2"
|
"@babel/plugin-transform-template-literals": "^7.27.1",
|
||||||
},
|
"@babel/plugin-transform-unicode-regex": "^7.27.1",
|
||||||
"engines": {
|
"@babel/preset-typescript": "^7.28.5",
|
||||||
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
"@babel/types": "^7.27.1",
|
||||||
|
"convert-source-map": "^2.0.0",
|
||||||
|
"semver": "^7.7.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@babel/core": "*"
|
"@babel/core": "*",
|
||||||
|
"@react-native/metro-config": "*",
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "0.83 - 0.86"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-native/node_modules/@react-native/js-polyfills": {
|
"node_modules/react-native-worklets/node_modules/semver": {
|
||||||
"version": "0.85.3",
|
"version": "7.8.4",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.85.3.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
|
||||||
"integrity": "sha512-U2+aMshIXf1uFn77tpBb/xhHWB9vkVrMpt7kkucAugF8hJKYTDGB587X7WwelHduK2KBfhl4giSv0rzZGoef9A==",
|
"integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
|
||||||
"license": "MIT",
|
"license": "ISC",
|
||||||
|
"peer": true,
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
"node": ">=10"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-native/node_modules/babel-plugin-syntax-hermes-parser": {
|
|
||||||
"version": "0.33.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.33.3.tgz",
|
|
||||||
"integrity": "sha512-/Z9xYdaJ1lC0pT9do6TqCqhOSLfZ5Ot8D5za1p+feEfWYupCOfGbhhEXN9r2ZgJtDNUNRw/Z+T2CvAGKBqtqWA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"hermes-parser": "0.33.3"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-native/node_modules/commander": {
|
"node_modules/react-native/node_modules/commander": {
|
||||||
@@ -7932,7 +8240,7 @@
|
|||||||
"version": "5.8.3",
|
"version": "5.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
|
|||||||
+5
-1
@@ -19,7 +19,7 @@
|
|||||||
"expo-sqlite": "~56.0.5",
|
"expo-sqlite": "~56.0.5",
|
||||||
"expo-status-bar": "~56.0.4",
|
"expo-status-bar": "~56.0.4",
|
||||||
"nativewind": "~4.2.5",
|
"nativewind": "~4.2.5",
|
||||||
"react": "19.2.7",
|
"react": "19.2.3",
|
||||||
"react-native": "0.85.3",
|
"react-native": "0.85.3",
|
||||||
"react-native-gesture-handler": "~3.0.2",
|
"react-native-gesture-handler": "~3.0.2",
|
||||||
"react-native-reanimated": "~4.4.1",
|
"react-native-reanimated": "~4.4.1",
|
||||||
@@ -27,6 +27,10 @@
|
|||||||
"react-native-screens": "~4.25.2",
|
"react-native-screens": "~4.25.2",
|
||||||
"tailwindcss": "~3.4.19"
|
"tailwindcss": "~3.4.19"
|
||||||
},
|
},
|
||||||
|
"overrides": {
|
||||||
|
"react": "19.2.3",
|
||||||
|
"react-dom": "19.2.3"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.0",
|
"@babel/core": "^7.25.0",
|
||||||
"@expo/metro-config": "~56.0.14",
|
"@expo/metro-config": "~56.0.14",
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// Exclude native build for packages not used in this project.
|
||||||
|
// react-native-gesture-handler detects their absence and falls back to its
|
||||||
|
// noreanimated codepath, so GestureHandlerRootView continues to work.
|
||||||
|
module.exports = {
|
||||||
|
dependencies: {
|
||||||
|
'react-native-reanimated': {
|
||||||
|
platforms: { android: null },
|
||||||
|
},
|
||||||
|
'react-native-worklets': {
|
||||||
|
platforms: { android: null },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user