codingpixel-expo-app 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +112 -0
- package/bin/cli.js +9 -0
- package/dist/babel.js +198 -0
- package/dist/bootstrap.js +54 -0
- package/dist/fonts.js +98 -0
- package/dist/index.js +120 -0
- package/dist/install.js +243 -0
- package/dist/overlay.js +141 -0
- package/dist/patch.js +212 -0
- package/dist/prompts.js +115 -0
- package/dist/scaffold.js +43 -0
- package/dist/sdkNotes.js +19 -0
- package/dist/util.js +48 -0
- package/package.json +56 -0
- package/templates/base/assets/fonts/.gitkeep +0 -0
- package/templates/base/assets/images/.gitkeep +0 -0
- package/templates/base/assets/index.ts +8 -0
- package/templates/base/src/app/_layout.tsx +39 -0
- package/templates/base/src/app/index.tsx +13 -0
- package/templates/base/src/app/routes.tsx +9 -0
- package/templates/base/src/core/hooks/.gitkeep +0 -0
- package/templates/base/src/core/redux/hooks.ts +6 -0
- package/templates/base/src/core/redux/mmkvStorage.ts +18 -0
- package/templates/base/src/core/redux/reducers.ts +20 -0
- package/templates/base/src/core/redux/slices/userSlice.ts +31 -0
- package/templates/base/src/core/redux/store.ts +28 -0
- package/templates/base/src/core/services/.gitkeep +0 -0
- package/templates/base/src/core/tanstack/index.tsx +93 -0
- package/templates/base/src/core/tanstack/tanstack-keys.ts +42 -0
- package/templates/base/src/core/utils/config.ts +50 -0
- package/templates/base/src/core/utils/constants.ts +11 -0
- package/templates/base/src/core/utils/endpoints.ts +29 -0
- package/templates/base/src/core/utils/types.ts +47 -0
- package/templates/base/src/core/utils/validation.ts +10 -0
- package/templates/base/src/features/.gitkeep +0 -0
- package/templates/base/src/ui/appComponents/AppRefreshControl/index.tsx +39 -0
- package/templates/base/src/ui/appComponents/appButton/index.tsx +122 -0
- package/templates/base/src/ui/appComponents/appColumnView/index.tsx +320 -0
- package/templates/base/src/ui/appComponents/appFlashList/index.tsx +191 -0
- package/templates/base/src/ui/appComponents/appFlatList/index.tsx +172 -0
- package/templates/base/src/ui/appComponents/appIcon/index.tsx +105 -0
- package/templates/base/src/ui/appComponents/appInput/index.tsx +226 -0
- package/templates/base/src/ui/appComponents/appKeyboardAvoidingView/index.tsx +168 -0
- package/templates/base/src/ui/appComponents/appKeyboardAwareScrollView/index.tsx +188 -0
- package/templates/base/src/ui/appComponents/appLogger/index.tsx +22 -0
- package/templates/base/src/ui/appComponents/appPressable/index.tsx +220 -0
- package/templates/base/src/ui/appComponents/appRowView/index.tsx +320 -0
- package/templates/base/src/ui/appComponents/appSafeAreaInsets/index.tsx +24 -0
- package/templates/base/src/ui/appComponents/appScrollView/index.tsx +166 -0
- package/templates/base/src/ui/appComponents/appSkeleton/index.tsx +22 -0
- package/templates/base/src/ui/appComponents/appStatusBar/index.tsx +8 -0
- package/templates/base/src/ui/appComponents/appTabHeader/index.tsx +111 -0
- package/templates/base/src/ui/appComponents/appText/index.tsx +258 -0
- package/templates/base/src/ui/appComponents/appTextWrapper/index.tsx +84 -0
- package/templates/base/src/ui/appComponents/appWrapper/index.tsx +56 -0
- package/templates/base/src/ui/appComponents/customActivityIndicator/index.tsx +34 -0
- package/templates/base/src/ui/appComponents/customGetPermissionModal/index.tsx +62 -0
- package/templates/base/src/ui/appComponents/customModal/index.tsx +26 -0
- package/templates/base/src/ui/appComponents/customTextInput/index.tsx +143 -0
- package/templates/base/src/ui/components/.gitkeep +0 -0
- package/templates/base/src/ui/components/avatarBlock/index.tsx +42 -0
- package/templates/base/src/ui/components/backgroundGradient/index.tsx +24 -0
- package/templates/base/src/ui/components/errorFallback/index.tsx +40 -0
- package/templates/base/src/ui/iconComponents/IconFontAwesome6/index.tsx +22 -0
- package/templates/base/src/ui/iconComponents/IoniconsIcons/index.tsx +33 -0
- package/templates/base/src/ui/iconComponents/antDesignicons/index.tsx +22 -0
- package/templates/base/src/ui/iconComponents/featherIcons/index.tsx +22 -0
- package/templates/base/src/ui/iconComponents/materialCommunityIcons/index.tsx +24 -0
- package/templates/base/src/ui/iconComponents/materialIcons/index.tsx +23 -0
- package/templates/base/src/ui/iconComponents/octiconsIcons/index.tsx +23 -0
- package/templates/base/src/ui/theme/allFileStyles.ts +39 -0
- package/templates/base/src/ui/theme/colors.ts +54 -0
- package/templates/base/src/ui/theme/fonts.ts +3 -0
- package/templates/base/src/ui/theme/responsive.ts +27 -0
- package/templates/bottom-sheet/src/ui/appComponents/BottomSheetKeyboardAwareScrollView/index.tsx +111 -0
- package/templates/bottom-sheet/src/ui/appComponents/appBottomSheetBackdrop/index.tsx +27 -0
- package/templates/bottom-sheet/src/ui/appComponents/appBottomSheetScrollView/index.tsx +99 -0
- package/templates/bottom-sheet/src/ui/appComponents/appBottomSheetView/index.tsx +173 -0
- package/templates/bottom-sheet/src/ui/appComponents/customBottomSheetModal/index.tsx +17 -0
- package/templates/claude-command/init-app.md +41 -0
- package/templates/image-picker/media-constants.snippet.ts +18 -0
- package/templates/image-picker/src/core/services/PermissionService.ts +48 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { RF } from "@theme/responsive";
|
|
2
|
+
import { BottomSheetView } from "@gorhom/bottom-sheet";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { DimensionValue, ViewProps, ViewStyle } from "react-native";
|
|
5
|
+
|
|
6
|
+
interface AppBottomSheetViewProps extends ViewProps {
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
focusHook?: () => void;
|
|
9
|
+
|
|
10
|
+
flex?: number;
|
|
11
|
+
flexGrow?: number;
|
|
12
|
+
flexShrink?: number;
|
|
13
|
+
flexBasis?: number | DimensionValue;
|
|
14
|
+
flexDirection?: ViewStyle["flexDirection"];
|
|
15
|
+
alignItems?: ViewStyle["alignItems"];
|
|
16
|
+
justifyContent?: ViewStyle["justifyContent"];
|
|
17
|
+
alignSelf?: ViewStyle["alignSelf"];
|
|
18
|
+
|
|
19
|
+
gap?: number;
|
|
20
|
+
|
|
21
|
+
width?: number | DimensionValue;
|
|
22
|
+
height?: number | DimensionValue;
|
|
23
|
+
minWidth?: number | DimensionValue;
|
|
24
|
+
maxWidth?: number | DimensionValue;
|
|
25
|
+
minHeight?: number | DimensionValue;
|
|
26
|
+
maxHeight?: number | DimensionValue;
|
|
27
|
+
|
|
28
|
+
padding?: number;
|
|
29
|
+
paddingHorizontal?: number;
|
|
30
|
+
paddingVertical?: number;
|
|
31
|
+
paddingTop?: number;
|
|
32
|
+
paddingBottom?: number;
|
|
33
|
+
paddingLeft?: number;
|
|
34
|
+
paddingRight?: number;
|
|
35
|
+
|
|
36
|
+
margin?: number;
|
|
37
|
+
marginHorizontal?: number;
|
|
38
|
+
marginVertical?: number;
|
|
39
|
+
marginTop?: number;
|
|
40
|
+
marginBottom?: number;
|
|
41
|
+
marginLeft?: number;
|
|
42
|
+
marginRight?: number;
|
|
43
|
+
|
|
44
|
+
backgroundColor?: string;
|
|
45
|
+
borderRadius?: number;
|
|
46
|
+
borderTopLeftRadius?: number;
|
|
47
|
+
borderTopRightRadius?: number;
|
|
48
|
+
borderBottomLeftRadius?: number;
|
|
49
|
+
borderBottomRightRadius?: number;
|
|
50
|
+
opacity?: number;
|
|
51
|
+
overflow?: ViewStyle["overflow"];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const AppBottomSheetView: React.FC<AppBottomSheetViewProps> = ({
|
|
55
|
+
children,
|
|
56
|
+
focusHook,
|
|
57
|
+
style,
|
|
58
|
+
flex,
|
|
59
|
+
flexGrow,
|
|
60
|
+
flexShrink,
|
|
61
|
+
flexBasis,
|
|
62
|
+
flexDirection,
|
|
63
|
+
alignItems,
|
|
64
|
+
justifyContent,
|
|
65
|
+
alignSelf,
|
|
66
|
+
gap,
|
|
67
|
+
width,
|
|
68
|
+
height,
|
|
69
|
+
minWidth,
|
|
70
|
+
maxWidth,
|
|
71
|
+
minHeight,
|
|
72
|
+
maxHeight,
|
|
73
|
+
padding,
|
|
74
|
+
paddingHorizontal,
|
|
75
|
+
paddingVertical,
|
|
76
|
+
paddingTop,
|
|
77
|
+
paddingBottom,
|
|
78
|
+
paddingLeft,
|
|
79
|
+
paddingRight,
|
|
80
|
+
margin,
|
|
81
|
+
marginHorizontal,
|
|
82
|
+
marginVertical,
|
|
83
|
+
marginTop,
|
|
84
|
+
marginBottom,
|
|
85
|
+
marginLeft,
|
|
86
|
+
marginRight,
|
|
87
|
+
backgroundColor,
|
|
88
|
+
borderRadius,
|
|
89
|
+
borderTopLeftRadius,
|
|
90
|
+
borderTopRightRadius,
|
|
91
|
+
borderBottomLeftRadius,
|
|
92
|
+
borderBottomRightRadius,
|
|
93
|
+
opacity,
|
|
94
|
+
overflow,
|
|
95
|
+
...rest
|
|
96
|
+
}) => {
|
|
97
|
+
const toDim = (v?: number | DimensionValue) =>
|
|
98
|
+
v === undefined
|
|
99
|
+
? undefined
|
|
100
|
+
: typeof v === "number"
|
|
101
|
+
? (RF(v) as unknown as DimensionValue)
|
|
102
|
+
: v;
|
|
103
|
+
|
|
104
|
+
const customStyle: ViewStyle = {
|
|
105
|
+
flex,
|
|
106
|
+
flexGrow,
|
|
107
|
+
flexShrink,
|
|
108
|
+
flexBasis,
|
|
109
|
+
flexDirection,
|
|
110
|
+
alignItems,
|
|
111
|
+
justifyContent,
|
|
112
|
+
alignSelf,
|
|
113
|
+
|
|
114
|
+
width: toDim(width),
|
|
115
|
+
height: toDim(height),
|
|
116
|
+
minWidth: toDim(minWidth),
|
|
117
|
+
maxWidth: toDim(maxWidth),
|
|
118
|
+
minHeight: toDim(minHeight),
|
|
119
|
+
maxHeight: toDim(maxHeight),
|
|
120
|
+
|
|
121
|
+
gap: gap !== undefined ? RF(gap) : undefined,
|
|
122
|
+
|
|
123
|
+
padding: padding !== undefined ? RF(padding) : undefined,
|
|
124
|
+
paddingHorizontal:
|
|
125
|
+
paddingHorizontal !== undefined ? RF(paddingHorizontal) : undefined,
|
|
126
|
+
paddingVertical:
|
|
127
|
+
paddingVertical !== undefined ? RF(paddingVertical) : undefined,
|
|
128
|
+
paddingTop: paddingTop !== undefined ? RF(paddingTop) : undefined,
|
|
129
|
+
paddingBottom: paddingBottom !== undefined ? RF(paddingBottom) : undefined,
|
|
130
|
+
paddingLeft: paddingLeft !== undefined ? RF(paddingLeft) : undefined,
|
|
131
|
+
paddingRight: paddingRight !== undefined ? RF(paddingRight) : undefined,
|
|
132
|
+
|
|
133
|
+
margin: margin !== undefined ? RF(margin) : undefined,
|
|
134
|
+
marginHorizontal:
|
|
135
|
+
marginHorizontal !== undefined ? RF(marginHorizontal) : undefined,
|
|
136
|
+
marginVertical:
|
|
137
|
+
marginVertical !== undefined ? RF(marginVertical) : undefined,
|
|
138
|
+
marginTop: marginTop !== undefined ? RF(marginTop) : undefined,
|
|
139
|
+
marginBottom: marginBottom !== undefined ? RF(marginBottom) : undefined,
|
|
140
|
+
marginLeft: marginLeft !== undefined ? RF(marginLeft) : undefined,
|
|
141
|
+
marginRight: marginRight !== undefined ? RF(marginRight) : undefined,
|
|
142
|
+
|
|
143
|
+
backgroundColor,
|
|
144
|
+
borderRadius: borderRadius !== undefined ? RF(borderRadius) : undefined,
|
|
145
|
+
borderTopLeftRadius:
|
|
146
|
+
borderTopLeftRadius !== undefined ? RF(borderTopLeftRadius) : undefined,
|
|
147
|
+
borderTopRightRadius:
|
|
148
|
+
borderTopRightRadius !== undefined ? RF(borderTopRightRadius) : undefined,
|
|
149
|
+
borderBottomLeftRadius:
|
|
150
|
+
borderBottomLeftRadius !== undefined
|
|
151
|
+
? RF(borderBottomLeftRadius)
|
|
152
|
+
: undefined,
|
|
153
|
+
borderBottomRightRadius:
|
|
154
|
+
borderBottomRightRadius !== undefined
|
|
155
|
+
? RF(borderBottomRightRadius)
|
|
156
|
+
: undefined,
|
|
157
|
+
|
|
158
|
+
opacity,
|
|
159
|
+
overflow,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<BottomSheetView
|
|
164
|
+
focusHook={focusHook}
|
|
165
|
+
style={[customStyle, style]}
|
|
166
|
+
{...rest}
|
|
167
|
+
>
|
|
168
|
+
{children}
|
|
169
|
+
</BottomSheetView>
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export default AppBottomSheetView;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BottomSheetModal, BottomSheetModalProps } from "@gorhom/bottom-sheet";
|
|
2
|
+
import React, { forwardRef } from "react";
|
|
3
|
+
|
|
4
|
+
const CustomBottomSheetModal = forwardRef<
|
|
5
|
+
BottomSheetModal,
|
|
6
|
+
BottomSheetModalProps
|
|
7
|
+
>(({ children, ...rest }, ref) => {
|
|
8
|
+
return (
|
|
9
|
+
<BottomSheetModal ref={ref} {...rest}>
|
|
10
|
+
{children}
|
|
11
|
+
</BottomSheetModal>
|
|
12
|
+
);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
CustomBottomSheetModal.displayName = "CustomBottomSheetModal";
|
|
16
|
+
|
|
17
|
+
export default CustomBottomSheetModal;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: init-app
|
|
3
|
+
description: Scaffold a new Expo project using codingpixel-expo-app with full prompt control + PM choice. Slash-command flow is non-TTY — all answers are resolved BEFORE invoking the CLI.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /init-app — Scaffold Expo project
|
|
7
|
+
|
|
8
|
+
Use **AskUserQuestion** to resolve every answer up front. The CLI itself runs without a TTY (slash-command stdin is closed); any missing answer would throw with `not a TTY`.
|
|
9
|
+
|
|
10
|
+
## Step 1 — Gather answers (use AskUserQuestion)
|
|
11
|
+
|
|
12
|
+
Ask the user, in this order:
|
|
13
|
+
|
|
14
|
+
1. **App name / target directory** (required, non-empty string). This becomes the positional CLI arg AND substitutes into the env-var command below. Do NOT pass the literal string `<dir>` — substitute the real value.
|
|
15
|
+
2. **Include bottom-sheet support?** (yes/no → "1" or "0").
|
|
16
|
+
3. **Include image-picker support?** (yes/no → "1" or "0").
|
|
17
|
+
4. **Package manager** (yarn or npm).
|
|
18
|
+
|
|
19
|
+
Fonts are intentionally disabled in this CLI version (Deviation #9 in `docs/MIRROR_NOTES.md`) — `Fonts = {}` ships in the generated app and `EXPO_PRIMARY_FONT` / `EXPO_SECONDARY_FONT` env vars are silently ignored.
|
|
20
|
+
|
|
21
|
+
## Step 2 — Run the CLI
|
|
22
|
+
|
|
23
|
+
Substitute every answer into the command. **Replace `my-app` with the resolved app name from step 1** before invoking:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
EXPO_INCLUDE_BOTTOM_SHEET="0" \
|
|
27
|
+
EXPO_INCLUDE_IMAGE_PICKER="0" \
|
|
28
|
+
EXPO_PACKAGE_MANAGER="yarn" \
|
|
29
|
+
npx --yes codingpixel-expo-app my-app
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Step 3 — After the CLI completes
|
|
33
|
+
|
|
34
|
+
Show the user the printed next-steps block (`cd <dir> / npx expo prebuild / yarn ios`). The CLI's success message lists the actual produced PM (which may differ from requested if Expo CLI ignored the flag — see PM-mismatch warning if shown).
|
|
35
|
+
|
|
36
|
+
## Failure handling
|
|
37
|
+
|
|
38
|
+
If the CLI exits non-zero:
|
|
39
|
+
- Read its stderr — it includes a recovery hint.
|
|
40
|
+
- Patches are idempotent: if the user fixes the root cause (e.g. network, missing PATH entry), they can re-run the same command and the CLI converges.
|
|
41
|
+
- Never auto-`rm -rf` the target dir; the user owns that decision.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const MEDIA_TYPES = {
|
|
2
|
+
IMAGE: "image" as const,
|
|
3
|
+
VIDEO: "video" as const,
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export const IMAGE_PICKER_OPTIONS = {
|
|
7
|
+
mediaTypes: ["images"] as const,
|
|
8
|
+
allowsEditing: false,
|
|
9
|
+
quality: 0.85,
|
|
10
|
+
base64: false,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const CAMERA_OPTIONS = {
|
|
14
|
+
mediaTypes: ["images"] as const,
|
|
15
|
+
allowsEditing: false,
|
|
16
|
+
quality: 0.85,
|
|
17
|
+
base64: false,
|
|
18
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import log from "@appComponents/appLogger";
|
|
2
|
+
import * as ImagePicker from "expo-image-picker";
|
|
3
|
+
|
|
4
|
+
const hasCameraPermission = async () => {
|
|
5
|
+
try {
|
|
6
|
+
const { status, canAskAgain } =
|
|
7
|
+
await ImagePicker.getCameraPermissionsAsync();
|
|
8
|
+
|
|
9
|
+
if (status === "granted") {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// 🔥 FIX: request when undetermined OR denied
|
|
14
|
+
if ((status === "undetermined" || status === "denied") && canAskAgain) {
|
|
15
|
+
const result = await ImagePicker.requestCameraPermissionsAsync();
|
|
16
|
+
return result.status === "granted";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return false;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
log.error("Camera permission error:", error);
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const hasLibraryPermission = async () => {
|
|
27
|
+
try {
|
|
28
|
+
const { status, canAskAgain } =
|
|
29
|
+
await ImagePicker.getMediaLibraryPermissionsAsync();
|
|
30
|
+
|
|
31
|
+
if (status === "granted") {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 🔥 FIX: request when undetermined OR denied
|
|
36
|
+
if ((status === "undetermined" || status === "denied") && canAskAgain) {
|
|
37
|
+
const result = await ImagePicker.requestMediaLibraryPermissionsAsync();
|
|
38
|
+
return result.status === "granted";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return false;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
log.error("Media library permission error:", error);
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export { hasCameraPermission, hasLibraryPermission };
|