Migrate from .NET MAUI to Expo + Convex

Complete rewrite of Scry using TypeScript stack:

- Expo/React Native with Expo Router (file-based routing)
- Convex backend (serverless functions + real-time database)
- Adaptive camera system (expo-camera in Expo Go, Vision Camera in production)
- React Native Skia + fast-opencv for image processing
- GDPR-compliant auth setup with Zitadel OIDC (pending configuration)

Key features:
- Card recognition pipeline ported to TypeScript
- Perceptual hashing (192-bit color pHash)
- CLAHE preprocessing for lighting normalization
- Local SQLite cache with Convex sync
- Collection management with offline support

Removes all .NET/MAUI code (src/, test/, tools/).

💘 Generated with Crush

Assisted-by: Claude Opus 4.5 via Crush <crush@charm.land>
This commit is contained in:
Chris Kruining 2026-02-09 16:16:34 +01:00
parent 56499d5af9
commit 83ab4df537
No known key found for this signature in database
GPG key ID: EB894A3560CCCAD2
138 changed files with 19136 additions and 7681 deletions

View file

@ -0,0 +1,64 @@
/**
* React Context for card hash cache.
* Provides offline-first hash data for card recognition.
*/
import React, { createContext, useContext, useState, useCallback, type ReactNode } from "react";
import type { CardHashEntry } from "../recognition";
interface HashCacheState {
cardHashes: CardHashEntry[];
hashesLoaded: boolean;
lastHashSync: number;
}
interface HashCacheContextValue extends HashCacheState {
setCardHashes: (hashes: CardHashEntry[]) => void;
clearHashes: () => void;
}
const HashCacheContext = createContext<HashCacheContextValue | null>(null);
export function HashCacheProvider({ children }: { children: ReactNode }) {
const [state, setState] = useState<HashCacheState>({
cardHashes: [],
hashesLoaded: false,
lastHashSync: 0,
});
const setCardHashes = useCallback((hashes: CardHashEntry[]) => {
setState({
cardHashes: hashes,
hashesLoaded: true,
lastHashSync: Date.now(),
});
}, []);
const clearHashes = useCallback(() => {
setState({
cardHashes: [],
hashesLoaded: false,
lastHashSync: 0,
});
}, []);
return (
<HashCacheContext.Provider
value={{
...state,
setCardHashes,
clearHashes,
}}
>
{children}
</HashCacheContext.Provider>
);
}
export function useHashCache() {
const context = useContext(HashCacheContext);
if (!context) {
throw new Error("useHashCache must be used within HashCacheProvider");
}
return context;
}