AI Wrapper Core
ProReact Native · ExpoWhat a store expects of an AI wrapper, in one folder: a versioned consent gate with withdraw, paywall rules without the screen, report, block and filter hooks, share card, delete account, published contact, photo deletion timer, self-photo attestation, an AI-generated label, privacy disclosure text and a client for the server proxy.
Installation
$ npx shadcn@latest add @goodui-native-pro/ai-coreThen run npx expo install --fix so @react-native-async-storage/async-storage, expo-image-picker, expo-notifications, expo-sharing, react-native-view-shot match your Expo SDK. Files land in components/goodui-native/ai-core.
Pro screens install from the license-gated @goodui-native-pro registry. Setup: Pro access.
Usage
import { useAiConsent } from "@/components/goodui-native/ai-core/consent";
import { ConsentRow } from "@/components/goodui-native/ai-core/consent-row";
import { deviceStorage } from "@/components/goodui-native/ai-core/device";
const consent = useAiConsent({ version: 2, provider: "OpenAI", storage: deviceStorage });
if (!consent.granted) router.push("/consent");
<ConsentRow consent={consent} provider="OpenAI" onReview={() => router.push("/consent")} />When to use
- Any app that sends a person's data to a third-party model.
- Any app that generates images or text a person can save or share.
- AI that runs fully on the device: there is no third party to disclose, though the label and report rules can still apply.
API reference
AiLabel
| Prop | Type | Default |
|---|---|---|
| kind | "generated" | "written" | "generated" |
| tone `scrim` sits on a picture; `plain` sits on the page. | "scrim" | "plain" | "plain" |
| labels | Partial<AiLabelLabels> | — |
| style Style for the outer container. | StyleProp<ViewStyle> | — |
| testID Identifier for end-to-end tests. | string | — |
PaywallLegal
| Prop | Type | Default |
|---|---|---|
| model* | PaywallModel | — |
| onRestore* | () => unknown | — |
| onTerms* | () => void | — |
| onPrivacy* | () => void | — |
| onNotNow Quiet way out. Leave the paywall from here; do not open a second offer. | () => void | — |
| style Style for the outer container. | StyleProp<ViewStyle> | — |
| testID Identifier for end-to-end tests. | string | — |
ConsentRow
| Prop | Type | Default |
|---|---|---|
| consent* | AiConsent | — |
| provider* | string | — |
| onReview* Opens the consent screen again. | () => void | — |
| labels | Partial<ConsentRowLabels> | — |
| style Style for the outer container. | StyleProp<ViewStyle> | — |
| testID Identifier for end-to-end tests. | string | — |
ContactRow
| Prop | Type | Default |
|---|---|---|
| email* Published on the row, where a reviewer can see it (App Store 1.2). | string | — |
| subject | string | — |
| labels | Partial<ContactRowLabels> | — |
| style Style for the outer container. | StyleProp<ViewStyle> | — |
| testID Identifier for end-to-end tests. | string | — |
DeleteAccount
| Prop | Type | Default |
|---|---|---|
| onDelete* Calls the server, clears local data, then send the person back to the start. Reject to show the error line. | () => Promise<unknown> | — |
| labels | Partial<DeleteAccountLabels> | — |
| style Style for the outer container. | StyleProp<ViewStyle> | — |
| testID Identifier for end-to-end tests. | string | — |
PhotoRetention
| Prop | Type | Default |
|---|---|---|
| deleteAt* When the server deletes the uploads, in ms. Null once they are gone. | number | null | — |
| onDeleteNow* Deletes the uploads right away. | () => void | — |
| labels | Partial<PhotoRetentionLabels> | — |
| style Style for the outer container. | StyleProp<ViewStyle> | — |
| testID Identifier for end-to-end tests. | string | — |
SelfAttestation
| Prop | Type | Default |
|---|---|---|
| minimumAge | number | 18 |
| onChange* True once every box is ticked. Hold the upload until then. | (attested: boolean) => void | — |
| labels | Partial<SelfAttestationLabels> | — |
| style Style for the outer container. | StyleProp<ViewStyle> | — |
| testID Identifier for end-to-end tests. | string | — |
ShareCard
| Prop | Type | Default |
|---|---|---|
| cardRef Pass the same ref to `shareView`. | RefObject<ComponentRef<typeof View> | null> | — |
| children* The art. Pass the template's own card: this frame owns size and the mark, not the look. | ReactNode | — |
| brand* Your app's name, drawn into the picture, so a shared card carries the source. | ReactNode | — |
| aspectRatio Width over height. 4:5 suits feeds and Stories crops. | number | 0.8 |
| width | number | 360 |
| background* | string | — |
| showAiLabel Leave the label on. Article 50 expects output to be marked. | boolean | true |
| style Style for the outer container. | StyleProp<ViewStyle> | — |
| testID Identifier for end-to-end tests. | string | — |
Motion and haptics
- None of its own. Sheets and holds inherit the kit's motion.
- Haptic: tap on rows, the hold-to-confirm haptics on delete.
- PhotoRetention ticks once a second and only while mounted.
Accessibility
- Every row is a named button or link. Status changes on ConsentRow and PhotoRetention use a live region where the state is read out.
Notes for AI
- Import device.ts directly. It is not in the kit index because it needs five native packages. Those packages are in each AI template.
- Check consent.granted before every send, not only at launch.
- The client filter is a convenience. Real moderation runs on the server.
- Bump the consent version when the provider or the data sent changes.
- A custom paywall must use usePaywallModel and PaywallLegal; check-kit reads template paywalls.
- Copy in PhotoRetention and ContactRow makes promises. Keep them true for your server and your support.
Related
The logic layer behind the AI components, with no UI: a typed provider adapter with an offline mock, a credit ledger that keeps subscription and bought credits apart, and a generation queue that charges, refunds and retries.
A consent screen shown before personal data reaches a third-party AI service: it names the provider, lists what is sent and what is not, has an optional age tick, and offers a visible way to decline.
A paywall for AI apps with weekly and yearly plans that each carry a credit allowance. The billed amount is always the largest price, weekly math stays in small text, the auto-renew line sits next to the buy button, and Terms, Privacy and Restore are always shown. check-kit fails if a price variant breaks this.
A bottom sheet for reporting generated content: six reasons, a switch to hide similar content, and a thank-you state. It gives AI apps an in-app way to report results, which app stores expect of generated content.
A balance pill for an app bar whose digits roll when the balance changes and which turns warning colored when low, plus CreditsCost, a live price line that says what a job costs and what is left after it.
Cards for long AI jobs: each one shows its status, a fill and a light sweep while running, a rolling percentage, and cancel, retry and open actions.