Initial commit: code as received (Create/Anything export)

Insole-production time tracker exported from the Create/Anything AI
platform. Baseline snapshot before any reverse-engineering or cleanup.

- apps/mobile: Expo Router app (iOS/Android/web), the only workspace
- publisher/: standalone OpenNext/AWS deploy tooling for the web side
- Backend (/api/tasks, /api/logs + DB) lives remotely, not in this repo
This commit is contained in:
Bas van Rossem
2026-06-17 10:19:33 +02:00
commit d94d0b188b
192 changed files with 50705 additions and 0 deletions

View File

@@ -0,0 +1,424 @@
import type React from "react";
import { Text, View, type ViewStyle } from "react-native";
// Stub for react-native-google-mobile-ads on web.
// Ads are native-only; these render visual placeholders so users can preview
// their layouts in Expo Go without the native module.
export const BannerAdSize = {
BANNER: "BANNER",
FULL_BANNER: "FULL_BANNER",
LARGE_BANNER: "LARGE_BANNER",
LEADERBOARD: "LEADERBOARD",
MEDIUM_RECTANGLE: "MEDIUM_RECTANGLE",
ADAPTIVE_BANNER: "ADAPTIVE_BANNER",
ANCHORED_ADAPTIVE_BANNER: "ANCHORED_ADAPTIVE_BANNER",
INLINE_ADAPTIVE_BANNER: "INLINE_ADAPTIVE_BANNER",
WIDE_SKYSCRAPER: "WIDE_SKYSCRAPER",
};
export const AdEventType = {
LOADED: "loaded",
ERROR: "error",
OPENED: "opened",
CLICKED: "clicked",
CLOSED: "closed",
};
export const RewardedAdEventType = {
LOADED: "loaded",
EARNED_REWARD: "earned_reward",
};
export const AdsConsentStatus = {
UNKNOWN: 0,
REQUIRED: 1,
NOT_REQUIRED: 2,
OBTAINED: 3,
};
export const AdsConsentDebugGeography = {
DISABLED: 0,
EEA: 1,
NOT_EEA: 2,
};
export const TestIds = {
BANNER: "ca-app-pub-3940256099942544/6300978111",
GAM_BANNER: "ca-app-pub-3940256099942544/6300978111",
INTERSTITIAL: "ca-app-pub-3940256099942544/1033173712",
GAM_INTERSTITIAL: "ca-app-pub-3940256099942544/1033173712",
REWARDED: "ca-app-pub-3940256099942544/5224354917",
REWARDED_INTERSTITIAL: "ca-app-pub-3940256099942544/5354046379",
APP_OPEN: "ca-app-pub-3940256099942544/3419835294",
NATIVE: "ca-app-pub-3940256099942544/2247696110",
NATIVE_VIDEO: "ca-app-pub-3940256099942544/1044960115",
};
const PLACEHOLDER_BG = "#f5f5f5";
const PLACEHOLDER_BORDER = "#e0e0e0";
const PLACEHOLDER_TEXT = "#999999";
const AD_LABEL_BG = "#fbbc04";
const AD_LABEL_TEXT = "#1a1a1a";
const AdLabel = () => (
<View
style={{
backgroundColor: AD_LABEL_BG,
paddingHorizontal: 4,
paddingVertical: 1,
borderRadius: 2,
}}
>
<Text
style={{
fontSize: 9,
fontWeight: "700",
color: AD_LABEL_TEXT,
lineHeight: 11,
}}
>
Ad
</Text>
</View>
);
const getBannerStyle = (size: string | undefined): ViewStyle => {
switch (size) {
case "FULL_BANNER":
return { width: 468, height: 60 };
case "LARGE_BANNER":
return { width: 320, height: 100 };
case "LEADERBOARD":
return { width: 728, height: 90 };
case "MEDIUM_RECTANGLE":
return { width: 300, height: 250 };
case "WIDE_SKYSCRAPER":
return { width: 160, height: 600 };
case "ADAPTIVE_BANNER":
case "ANCHORED_ADAPTIVE_BANNER":
return { width: "100%", height: 50 };
case "INLINE_ADAPTIVE_BANNER":
return { width: "100%", height: 100 };
default:
return { width: 320, height: 50 };
}
};
type BannerAdProps = {
size?: string;
unitId?: string;
onAdLoaded?: () => void;
onAdFailedToLoad?: (error: unknown) => void;
onAdOpened?: () => void;
onAdClosed?: () => void;
};
const BannerPlaceholder = ({
size,
label,
}: { size?: string; label: string }) => {
const dims = getBannerStyle(size);
return (
<View
style={{
...dims,
backgroundColor: PLACEHOLDER_BG,
borderWidth: 1,
borderColor: PLACEHOLDER_BORDER,
borderRadius: 4,
alignItems: "center",
justifyContent: "center",
flexDirection: "row",
gap: 6,
}}
>
<AdLabel />
<Text style={{ color: PLACEHOLDER_TEXT, fontSize: 12 }}>{label}</Text>
</View>
);
};
export const BannerAd = ({ size }: BannerAdProps) => (
<BannerPlaceholder size={size} label="Banner Ad" />
);
export const GAMBannerAd = ({ size }: BannerAdProps) => (
<BannerPlaceholder size={size} label="Ad Manager Banner" />
);
type NativeAdViewProps = {
children?: React.ReactNode;
nativeAd?: unknown;
style?: ViewStyle | ViewStyle[];
};
const DefaultNativeAdContent = () => (
<View>
<View
style={{ flexDirection: "row", alignItems: "center", marginBottom: 10 }}
>
<View
style={{
width: 40,
height: 40,
borderRadius: 8,
backgroundColor: PLACEHOLDER_BORDER,
marginRight: 10,
}}
/>
<View style={{ flex: 1 }}>
<View
style={{
height: 12,
backgroundColor: PLACEHOLDER_BORDER,
borderRadius: 4,
marginBottom: 6,
width: "70%",
}}
/>
<View
style={{
height: 10,
backgroundColor: "#ececec",
borderRadius: 4,
width: "40%",
}}
/>
</View>
</View>
<View
style={{
height: 140,
backgroundColor: PLACEHOLDER_BORDER,
borderRadius: 4,
marginBottom: 10,
alignItems: "center",
justifyContent: "center",
}}
>
<Text style={{ color: PLACEHOLDER_TEXT, fontSize: 12 }}>
Native Ad Media
</Text>
</View>
<View
style={{
height: 10,
backgroundColor: "#ececec",
borderRadius: 4,
marginBottom: 6,
}}
/>
<View
style={{
height: 10,
backgroundColor: "#ececec",
borderRadius: 4,
width: "80%",
marginBottom: 12,
}}
/>
<View
style={{
alignSelf: "flex-start",
backgroundColor: "#1a73e8",
paddingHorizontal: 14,
paddingVertical: 8,
borderRadius: 4,
}}
>
<Text style={{ color: "#fff", fontSize: 12, fontWeight: "600" }}>
Install
</Text>
</View>
</View>
);
export const NativeAdView = ({ children, style }: NativeAdViewProps) => (
<View
style={[
{
backgroundColor: PLACEHOLDER_BG,
borderWidth: 1,
borderColor: PLACEHOLDER_BORDER,
borderRadius: 8,
padding: 12,
position: "relative",
},
style as ViewStyle,
]}
>
<View style={{ position: "absolute", top: 8, right: 8, zIndex: 1 }}>
<AdLabel />
</View>
{children ?? <DefaultNativeAdContent />}
</View>
);
export const NativeAsset = ({
children,
style,
}: {
children?: React.ReactNode;
assetType?: string;
style?: ViewStyle | ViewStyle[];
}) => <View style={style as ViewStyle}>{children}</View>;
export const NativeMediaView = ({
style,
}: { style?: ViewStyle | ViewStyle[] }) => (
<View
style={[
{
height: 180,
backgroundColor: PLACEHOLDER_BORDER,
borderRadius: 4,
alignItems: "center",
justifyContent: "center",
},
style as ViewStyle,
]}
>
<Text style={{ color: PLACEHOLDER_TEXT, fontSize: 12 }}>
Ad Media (native only)
</Text>
</View>
);
export const NativeAd = {
createForAdRequest: async (_unitId?: string, _requestOptions?: unknown) => ({
headline: "Sample Ad Headline",
body: "Native ads only render on a real device.",
advertiser: "Sample Advertiser",
callToAction: "Install",
icon: null,
images: [],
starRating: null,
store: null,
price: null,
addAdEventListener: () => () => {},
removeAllListeners: () => {},
destroy: () => {},
}),
};
const createFullScreenAdStub = () => ({
loaded: false,
load: () => {},
show: () => Promise.resolve(),
addAdEventListener: () => () => {},
addAdEventsListener: () => () => {},
removeAllListeners: () => {},
});
export const InterstitialAd = {
createForAdRequest: () => createFullScreenAdStub(),
};
export const RewardedAd = {
createForAdRequest: () => createFullScreenAdStub(),
};
export const RewardedInterstitialAd = {
createForAdRequest: () => createFullScreenAdStub(),
};
export const AppOpenAd = {
createForAdRequest: () => createFullScreenAdStub(),
};
export const GAMInterstitialAd = {
createForAdRequest: () => createFullScreenAdStub(),
};
export const GAMRewardedAd = {
createForAdRequest: () => createFullScreenAdStub(),
};
export const GAMRewardedInterstitialAd = {
createForAdRequest: () => createFullScreenAdStub(),
};
const baseHookResult = {
isLoaded: false,
isOpened: false,
isClicked: false,
isClosed: false,
error: null as unknown,
load: () => {},
show: () => {},
};
export const useInterstitialAd = () => ({ ...baseHookResult });
export const useAppOpenAd = () => ({ ...baseHookResult });
export const useRewardedAd = () => ({
...baseHookResult,
isEarnedReward: false,
reward: null,
});
export const useRewardedInterstitialAd = () => ({
...baseHookResult,
isEarnedReward: false,
reward: null,
});
export const AdsConsent = {
requestInfoUpdate: async () => ({
status: AdsConsentStatus.NOT_REQUIRED,
isConsentFormAvailable: false,
}),
showForm: async () => ({ status: AdsConsentStatus.OBTAINED }),
loadAndShowConsentFormIfRequired: async () => ({
status: AdsConsentStatus.NOT_REQUIRED,
}),
gatherConsent: async () => ({ status: AdsConsentStatus.NOT_REQUIRED }),
reset: () => {},
getConsentInfo: async () => ({
status: AdsConsentStatus.NOT_REQUIRED,
canRequestAds: true,
isConsentFormAvailable: false,
privacyOptionsRequirementStatus: "NOT_REQUIRED",
}),
getUserChoices: async () => ({}),
getTCString: async () => "",
getGdprApplies: async () => false,
getPurposeConsents: async () => "",
getPurposeLegitimateInterests: async () => "",
};
const mobileAdsInstance = {
initialize: async () => [],
setRequestConfiguration: async () => {},
openAdInspector: async () => {},
openDebugMenu: () => {},
setAppMuted: () => {},
setAppVolume: () => {},
};
const mobileAds = () => mobileAdsInstance;
const defaultExport = Object.assign(mobileAds, {
BannerAd,
GAMBannerAd,
BannerAdSize,
InterstitialAd,
RewardedAd,
RewardedInterstitialAd,
AppOpenAd,
GAMInterstitialAd,
GAMRewardedAd,
GAMRewardedInterstitialAd,
NativeAd,
NativeAdView,
NativeAsset,
NativeMediaView,
AdEventType,
RewardedAdEventType,
AdsConsent,
AdsConsentStatus,
AdsConsentDebugGeography,
TestIds,
});
export { mobileAds };
export default defaultExport;

View File

@@ -0,0 +1,179 @@
// Expo Go-safe stub for react-native-purchases.
//
// The real package's index pulls in @revenuecat/purchases-js-hybrid-mappings
// (a ~15k-line Svelte UMD bundle of browser-only code) which throws on
// module evaluation under Hermes in Expo Go preview. Even importing it from
// a hook that's never called crashes _layout.tsx, which makes expo-router
// silently swallow the throw and warn "Route \"./_layout.tsx\" is missing
// the required default export" — leaving the app stuck on a black/splash
// screen forever.
//
// This polyfill is wired up in metro.config.js for native platforms only
// when EXPO_PUBLIC_CREATE_ENV !== 'PRODUCTION'. Production EAS builds keep
// the real SDK, so paid users hit RevenueCat as normal.
const noopAsync = async () => undefined;
const LOG_LEVEL = {
VERBOSE: "VERBOSE",
DEBUG: "DEBUG",
INFO: "INFO",
WARN: "WARN",
ERROR: "ERROR",
SILENT: "SILENT",
};
const PRODUCT_CATEGORY = {
SUBSCRIPTION: "SUBSCRIPTION",
NON_SUBSCRIPTION: "NON_SUBSCRIPTION",
UNKNOWN: "UNKNOWN",
};
const PURCHASE_TYPE = {
INAPP: "inapp",
SUBS: "subs",
};
const PURCHASES_ARE_COMPLETED_BY_TYPE = {
REVENUECAT: "REVENUECAT",
MY_APP: "MY_APP",
};
const REFUND_REQUEST_STATUS = {
SUCCESS: "SUCCESS",
USER_CANCELLED: "USER_CANCELLED",
ERROR: "ERROR",
};
const BILLING_FEATURE = {
SUBSCRIPTIONS: "SUBSCRIPTIONS",
SUBSCRIPTIONS_UPDATE: "SUBSCRIPTIONS_UPDATE",
IN_APP_MESSAGING: "IN_APP_MESSAGING",
PRICE_CHANGE_CONFIRMATION: "PRICE_CHANGE_CONFIRMATION",
};
const STOREKIT_VERSION = {
DEFAULT: "DEFAULT",
STOREKIT_1: "STOREKIT_1",
STOREKIT_2: "STOREKIT_2",
};
const Purchases = {
configure: noopAsync,
setLogLevel: () => {},
setLogHandler: () => {},
addCustomerInfoUpdateListener: () => () => {},
removeCustomerInfoUpdateListener: () => {},
getOfferings: async () => ({ current: null, all: {} }),
getProducts: async () => [],
getCustomerInfo: async () => ({
entitlements: { active: {}, all: {} },
activeSubscriptions: [],
allPurchasedProductIdentifiers: [],
latestExpirationDate: null,
firstSeen: new Date().toISOString(),
originalAppUserId: "expo-go-preview",
requestDate: new Date().toISOString(),
allExpirationDates: {},
allPurchaseDates: {},
originalApplicationVersion: null,
originalPurchaseDate: null,
managementURL: null,
nonSubscriptionTransactions: [],
}),
purchasePackage: async () => {
const error: Error & { userCancelled?: boolean } = new Error(
"Purchases not available in Expo Go preview. Build a development build or run in TestFlight to test purchases.",
);
error.userCancelled = true;
throw error;
},
purchaseProduct: async () => {
const error: Error & { userCancelled?: boolean } = new Error(
"Purchases not available in Expo Go preview.",
);
error.userCancelled = true;
throw error;
},
restorePurchases: async () => ({
entitlements: { active: {}, all: {} },
activeSubscriptions: [],
allPurchasedProductIdentifiers: [],
latestExpirationDate: null,
firstSeen: new Date().toISOString(),
originalAppUserId: "expo-go-preview",
requestDate: new Date().toISOString(),
allExpirationDates: {},
allPurchaseDates: {},
originalApplicationVersion: null,
originalPurchaseDate: null,
managementURL: null,
nonSubscriptionTransactions: [],
}),
logIn: async (appUserID: string) => ({
customerInfo: {
entitlements: { active: {}, all: {} },
activeSubscriptions: [],
originalAppUserId: appUserID,
},
created: false,
}),
logOut: async () => ({
entitlements: { active: {}, all: {} },
activeSubscriptions: [],
originalAppUserId: "expo-go-preview",
}),
setAttributes: noopAsync,
setEmail: noopAsync,
setDisplayName: noopAsync,
setPhoneNumber: noopAsync,
setPushToken: noopAsync,
setAdjustID: noopAsync,
setAppsflyerID: noopAsync,
setFBAnonymousID: noopAsync,
setMparticleID: noopAsync,
setOnesignalID: noopAsync,
setAirshipChannelID: noopAsync,
setMediaSource: noopAsync,
setCampaign: noopAsync,
setAdGroup: noopAsync,
setAd: noopAsync,
setKeyword: noopAsync,
setCreative: noopAsync,
collectDeviceIdentifiers: () => {},
syncPurchases: noopAsync,
syncAttributesAndOfferingsIfNeeded: async () => ({ current: null, all: {} }),
enableAdServicesAttributionTokenCollection: () => {},
isAnonymous: async () => true,
checkTrialOrIntroductoryPriceEligibility: async () => ({}),
invalidateCustomerInfoCache: () => {},
presentCodeRedemptionSheet: () => {},
beginRefundRequestForActiveEntitlement: async () => REFUND_REQUEST_STATUS.ERROR,
beginRefundRequestForEntitlement: async () => REFUND_REQUEST_STATUS.ERROR,
beginRefundRequestForProduct: async () => REFUND_REQUEST_STATUS.ERROR,
showInAppMessages: noopAsync,
getPromotionalOffer: async () => null,
purchasePromotionalOffer: async () => {
const error: Error & { userCancelled?: boolean } = new Error(
"Purchases not available in Expo Go preview.",
);
error.userCancelled = true;
throw error;
},
canMakePayments: async () => false,
getAppUserID: async () => "expo-go-preview",
close: () => {},
configureInUITestMode: () => {},
};
export {
LOG_LEVEL,
PRODUCT_CATEGORY,
PURCHASE_TYPE,
PURCHASES_ARE_COMPLETED_BY_TYPE,
REFUND_REQUEST_STATUS,
BILLING_FEATURE,
STOREKIT_VERSION,
};
export default Purchases;

View File

@@ -0,0 +1,16 @@
import React from 'react';
import { TextInput as RNTextInput, type TextInputProps } from 'react-native';
const TextInput = React.forwardRef<RNTextInput, TextInputProps>((props, ref) => {
return (
<RNTextInput
ref={ref}
placeholderTextColor={props.placeholderTextColor || 'black'}
{...props}
/>
);
});
TextInput.displayName = 'TextInput';
export default TextInput;