feat(deployment): prepare app for iOS TestFlight submission

- Update outdated Expo packages to latest compatible versions
- Remove unmaintained expo-random package
- Remove unnecessary @types/react-native package
- Configure eas.json with preview and production profiles for iOS
- Fix updates URL in app.json with correct project ID
- Add /android and /ios to .gitignore to resolve workflow conflict
- Create comprehensive iOS TestFlight submission guide
- Add production flag in theme constants
- Hide development-only Programs tab in production builds
- Remove debug UI and console logs from social feed in production
- Update CHANGELOG.md with TestFlight preparation changes
All checks from expo-doctor now pass (15/15).
This commit is contained in:
DocNR 2025-03-28 21:22:20 -07:00
parent 3f2ababe4f
commit 89504f48e8
12 changed files with 382 additions and 691 deletions

5
.gitignore vendored
View File

@ -16,6 +16,11 @@ web-build/
*.key
*.mobileprovision
# Prebuild folders - addressing workflow conflict
# For CNG/Prebuild with EAS Build
/android
/ios
# Metro
.metro-health-check*

View File

@ -1,5 +1,11 @@
# Changelog
## [Unreleased]
### Added
- TestFlight preparation: Added production flag in theme constants
- TestFlight preparation: Hid development-only Programs tab in production builds
- TestFlight preparation: Removed debug UI and console logs from social feed in production builds
All notable changes to the POWR project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
@ -14,6 +20,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Implemented useProfileStats hook with auto-refresh capabilities
- Added proper loading states and error handling
- Created documentation in the new documentation structure
- iOS TestFlight build configuration
- Created comprehensive TestFlight submission documentation
- Added production and preview build profiles to eas.json
- Added TestFlight submission configuration
- Created deployment documentation in docs/deployment/ios_testflight_guide.md
## Improved
- Enhanced Profile UI
@ -22,6 +33,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added inline copy and QR buttons for better usability
- Enhanced visual consistency across profile elements
- Replaced hardcoded follower counts with real-time data
- Updated project configuration for TestFlight
- Updated outdated packages (expo, expo-dev-client, expo-file-system, expo-router, expo-sqlite, jest-expo)
- Removed unmaintained packages (expo-random)
- Removed unnecessary packages (@types/react-native)
- Fixed updates URL in app.json to use the correct project ID
- Documented workflow conflict between managed and bare configurations
## Fixed
- Prebuild/managed workflow conflict documentation
- Added detailed explanation of the configuration issue
- Documented future decision points for project architecture
- Provided options for resolving the configuration conflict
# Changelog - March 26, 2025

View File

@ -67,7 +67,7 @@
"policy": "sdkVersion"
},
"updates": {
"url": "https://u.expo.dev/your-project-id"
"url": "https://u.expo.dev/f3895f49-d9c9-4653-b73b-356f727debe2"
},
"extra": {
"router": {

View File

@ -8,6 +8,7 @@ import Header from '@/components/Header';
import { useTheme } from '@react-navigation/native';
import type { CustomTheme } from '@/lib/theme';
import { TabScreen } from '@/components/layout/TabScreen';
import { IS_PRODUCTION } from '@/lib/theme/constants';
const Tab = createMaterialTopTabNavigator();
@ -52,11 +53,14 @@ export default function LibraryLayout() {
component={TemplatesScreen}
options={{ title: 'Templates' }}
/>
<Tab.Screen
name="programs"
component={ProgramsScreen}
options={{ title: 'Programs' }}
/>
{/* Only show Programs tab in development builds */}
{!IS_PRODUCTION && (
<Tab.Screen
name="programs"
component={ProgramsScreen}
options={{ title: 'Programs' }}
/>
)}
</Tab.Navigator>
</TabScreen>
);

View File

@ -10,6 +10,7 @@ import { useContactList } from '@/lib/hooks/useContactList';
import { ChevronUp, Bug } from 'lucide-react-native';
import { withOfflineState } from '@/components/social/SocialOfflineState';
import { useSocialFeed } from '@/lib/hooks/useSocialFeed';
import { IS_PRODUCTION } from '@/lib/theme/constants';
function FollowingScreen() {
const { isAuthenticated, currentUser } = useNDKCurrentUser();
@ -18,11 +19,13 @@ function FollowingScreen() {
// Get the user's contact list
const { contacts, isLoading: isLoadingContacts } = useContactList(currentUser?.pubkey);
// Add debug logging for contact list
// Add debug logging for contact list (only in development)
React.useEffect(() => {
console.log(`[FollowingScreen] Contact list has ${contacts.length} contacts`);
if (contacts.length > 0) {
console.log(`[FollowingScreen] First few contacts: ${contacts.slice(0, 3).join(', ')}`);
if (!IS_PRODUCTION) {
console.log(`[FollowingScreen] Contact list has ${contacts.length} contacts`);
if (contacts.length > 0) {
console.log(`[FollowingScreen] First few contacts: ${contacts.slice(0, 3).join(', ')}`);
}
}
}, [contacts.length]);
@ -77,7 +80,9 @@ function FollowingScreen() {
// Update loadedContactsCount when contacts change
React.useEffect(() => {
if (contacts.length > 0 && contacts.length !== loadedContactsCount) {
console.log(`[FollowingScreen] Contact list changed from ${loadedContactsCount} to ${contacts.length} contacts`);
if (!IS_PRODUCTION) {
console.log(`[FollowingScreen] Contact list changed from ${loadedContactsCount} to ${contacts.length} contacts`);
}
setLoadedContactsCount(contacts.length);
// Reset hasLoadedWithContacts flag when contacts change
setHasLoadedWithContacts(false);
@ -99,7 +104,9 @@ function FollowingScreen() {
contactRefreshAttempts < maxContactRefreshAttempts;
if (shouldRefresh) {
console.log(`[FollowingScreen] Refreshing feed with ${contacts.length} contacts (attempt ${contactRefreshAttempts + 1}/${maxContactRefreshAttempts})`);
if (!IS_PRODUCTION) {
console.log(`[FollowingScreen] Refreshing feed with ${contacts.length} contacts (attempt ${contactRefreshAttempts + 1}/${maxContactRefreshAttempts})`);
}
setIsRefreshingWithContacts(true);
setContactRefreshAttempts(prev => prev + 1);
@ -111,7 +118,9 @@ function FollowingScreen() {
setIsRefreshingWithContacts(false);
})
.catch(error => {
console.error('[FollowingScreen] Error refreshing feed:', error);
if (!IS_PRODUCTION) {
console.error('[FollowingScreen] Error refreshing feed:', error);
}
setIsRefreshingWithContacts(false);
// Prevent infinite retries by marking as loaded after max attempts
@ -183,7 +192,9 @@ function FollowingScreen() {
}
}
} catch (error) {
console.error('[FollowingScreen] Error refreshing feed:', error);
if (!IS_PRODUCTION) {
console.error('[FollowingScreen] Error refreshing feed:', error);
}
} finally {
setIsRefreshing(false);
}
@ -193,25 +204,33 @@ function FollowingScreen() {
const checkRelayConnections = useCallback(() => {
if (!ndk) return;
console.log("=== RELAY CONNECTION STATUS ===");
if (ndk.pool && ndk.pool.relays) {
console.log(`Connected to ${ndk.pool.relays.size} relays:`);
ndk.pool.relays.forEach((relay) => {
console.log(`- ${relay.url}: ${relay.status}`);
});
} else {
console.log("No relay pool or connections available");
// Only log in development mode
if (!IS_PRODUCTION) {
console.log("=== RELAY CONNECTION STATUS ===");
if (ndk.pool && ndk.pool.relays) {
console.log(`Connected to ${ndk.pool.relays.size} relays:`);
ndk.pool.relays.forEach((relay) => {
console.log(`- ${relay.url}: ${relay.status}`);
});
} else {
console.log("No relay pool or connections available");
}
console.log("===============================");
}
console.log("===============================");
}, [ndk]);
// Handle post selection - simplified for testing
const handlePostPress = useCallback((entry: any) => {
// Just show an alert with the entry info for testing
alert(`Selected ${entry.type} with ID: ${entry.id || entry.eventId}`);
if (!IS_PRODUCTION) {
alert(`Selected ${entry.type} with ID: ${entry.id || entry.eventId}`);
// Alternatively, log to console for debugging
console.log(`Selected ${entry.type}:`, entry);
// Alternatively, log to console for debugging
console.log(`Selected ${entry.type}:`, entry);
}
// In production, this would navigate to the post detail screen
// TODO: Implement proper post detail navigation for production
}, []);
// Memoize render item function
@ -267,15 +286,17 @@ function FollowingScreen() {
: "No content from followed users found. Try following more users or check your relay connections."}
</Text>
{/* Debug toggle */}
<TouchableOpacity
className="mt-4 bg-gray-200 py-2 px-4 rounded"
onPress={() => setShowDebug(!showDebug)}
>
<Text>{showDebug ? "Hide" : "Show"} Debug Info</Text>
</TouchableOpacity>
{/* Debug toggle - only shown in development */}
{!IS_PRODUCTION && (
<TouchableOpacity
className="mt-4 bg-gray-200 py-2 px-4 rounded"
onPress={() => setShowDebug(!showDebug)}
>
<Text>{showDebug ? "Hide" : "Show"} Debug Info</Text>
</TouchableOpacity>
)}
{showDebug && (
{!IS_PRODUCTION && showDebug && (
<View className="mt-4 p-4 bg-gray-100 rounded w-full">
<Text className="text-xs">User pubkey: {currentUser?.pubkey?.substring(0, 12)}...</Text>
<Text className="text-xs">Authenticated: {isAuthenticated ? "Yes" : "No"}</Text>
@ -305,16 +326,18 @@ function FollowingScreen() {
return (
<View className="flex-1">
{/* Debug toggle button */}
<TouchableOpacity
className="absolute top-2 right-2 z-10 bg-gray-200 p-2 rounded-full"
onPress={() => setShowDebug(!showDebug)}
>
<Bug size={16} color="#666" />
</TouchableOpacity>
{/* Debug toggle button - only shown in development */}
{!IS_PRODUCTION && (
<TouchableOpacity
className="absolute top-2 right-2 z-10 bg-gray-200 p-2 rounded-full"
onPress={() => setShowDebug(!showDebug)}
>
<Bug size={16} color="#666" />
</TouchableOpacity>
)}
{/* Debug panel */}
{showDebug && <DebugControls />}
{/* Debug panel - only shown in development */}
{!IS_PRODUCTION && showDebug && <DebugControls />}
{showNewButton && (
<TouchableOpacity

BIN
assets/splash.mp4 Normal file

Binary file not shown.

BIN
assets/v1-splash.mp4 Normal file

Binary file not shown.

View File

@ -0,0 +1,96 @@
# iOS TestFlight Submission Guide
This guide documents the process for creating and submitting POWR to TestFlight for iOS testing.
## Project Configuration Issues
### Prebuild/Managed Workflow Conflict
The project currently has a "mixed" state that needs to be addressed:
- Native iOS and Android folders exist (bare workflow)
- Configuration exists in app.json that would normally be used in a managed workflow
When both native folders and app.json configs exist, EAS Build will use the native project settings and ignore certain app.json configurations including:
- orientation
- icon
- scheme
- userInterfaceStyle
- splash
- ios/android configuration
- plugins
- androidStatusBar
**TODO: After TestFlight validation, decide on one of these approaches:**
1. Commit to bare workflow: Keep native folders and move all configuration to them
2. Commit to managed workflow: Remove native folders and let Expo handle native code generation
## Fixed Issues
The following issues were addressed to prepare for TestFlight:
1. Updated outdated packages:
- expo: ~52.0.41 (was 52.0.35)
- expo-dev-client: ~5.0.15 (was 5.0.12)
- expo-file-system: ~18.0.12 (was 18.0.10)
- expo-router: ~4.0.19 (was 4.0.17)
- expo-sqlite: ~15.1.3 (was 15.1.2)
- jest-expo: ~52.0.6 (was 52.0.4)
2. Removed unmaintained and unnecessary packages:
- expo-random: Removed as it's flagged as unmaintained
- @types/react-native: Removed as types are included with react-native
3. Added proper iOS build configurations in eas.json:
- Added preview build profile for internal testing
- Added production build profile for App Store submission
4. Fixed updates URL in app.json to use the correct project ID
5. Fixed prebuild/managed workflow conflict:
- Added /android and /ios folders to .gitignore as recommended by expo-doctor
- This approach tells Git to ignore native folders while still allowing EAS Build to use them
- Addresses the warning about app.json configuration fields not being synced in non-CNG projects
## TestFlight Build Process
To create and submit a build to TestFlight:
1. Update Apple credentials in eas.json:
```json
"submit": {
"production": {
"ios": {
"appleId": "YOUR_APPLE_ID_EMAIL",
"ascAppId": "YOUR_APP_STORE_CONNECT_APP_ID",
"appleTeamId": "YOUR_APPLE_TEAM_ID"
}
}
}
```
2. Create a build for internal testing (preview):
```
eas build --platform ios --profile preview
```
3. Create a production build for TestFlight:
```
eas build --platform ios --profile production
```
4. Submit the build to TestFlight:
```
eas submit --platform ios --latest
```
## Troubleshooting
- If you encounter issues with the mixed configuration state, consider fully committing to either the bare or managed workflow
- For build errors related to native code, check the iOS logs in the EAS build output
- For App Store Connect submission errors, verify your app metadata and screenshots in App Store Connect
## References
- [Expo Application Services Documentation](https://docs.expo.dev/eas/)
- [Expo Prebuild Documentation](https://docs.expo.dev/workflow/prebuild/)
- [TestFlight Documentation](https://developer.apple.com/testflight/)

View File

@ -1,21 +1,42 @@
{
"cli": {
"version": ">= 3.7.2"
"cli": {
"version": ">= 3.7.2"
},
"build": {
"development-simulator": {
"developmentClient": true,
"distribution": "internal",
"ios": {
"simulator": true
}
},
"build": {
"development-simulator": {
"developmentClient": true,
"distribution": "internal",
"ios": {
"simulator": true
}
},
"development": {
"developmentClient": true,
"distribution": "internal",
"android": {
"buildType": "apk"
}
"development": {
"developmentClient": true,
"distribution": "internal",
"android": {
"buildType": "apk"
}
},
"preview": {
"distribution": "internal",
"ios": {
"resourceClass": "m1-medium"
}
},
"production": {
"autoIncrement": true,
"ios": {
"resourceClass": "m1-medium"
}
}
},
"submit": {
"production": {
"ios": {
"appleId": "YOUR_APPLE_ID_EMAIL",
"ascAppId": "YOUR_APP_STORE_CONNECT_APP_ID",
"appleTeamId": "YOUR_APPLE_TEAM_ID"
}
}
}
}

View File

@ -1,6 +1,24 @@
// lib/theme/constants.ts
import { COLORS } from './colors';
/**
* Application configuration
*/
/**
* Set to true for production builds (TestFlight, App Store)
* This should be automatically configured based on the EAS build profile
*
* For local development, keep this as false
* For TestFlight/App Store builds, set to true
*/
export const IS_PRODUCTION = true;
/**
* App version information
*/
export const APP_VERSION = '1.0.0';
export interface NavigationThemeColors {
background: string;
border: string;

741
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -63,23 +63,22 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"date-fns": "^4.1.0",
"expo": "^52.0.35",
"expo": "~52.0.41",
"expo-av": "~15.0.2",
"expo-crypto": "~14.0.2",
"expo-dev-client": "~5.0.12",
"expo-file-system": "~18.0.10",
"expo-dev-client": "~5.0.15",
"expo-file-system": "~18.0.12",
"expo-linking": "~7.0.4",
"expo-navigation-bar": "~4.0.8",
"expo-nip55": "^0.1.5",
"expo-random": "^14.0.1",
"expo-router": "~4.0.16",
"expo-router": "~4.0.19",
"expo-secure-store": "~14.0.1",
"expo-splash-screen": "~0.29.22",
"expo-sqlite": "~15.1.2",
"expo-sqlite": "~15.1.3",
"expo-status-bar": "~2.0.1",
"expo-system-ui": "~4.0.8",
"jest": "~29.7.0",
"jest-expo": "~52.0.3",
"jest-expo": "~52.0.6",
"lucide-react-native": "^0.378.0",
"nativewind": "^4.1.23",
"nostr-tools": "^2.10.4",
@ -106,7 +105,6 @@
"@types/jest": "^29.5.14",
"@types/lodash": "^4.17.15",
"@types/react": "~18.3.12",
"@types/react-native": "^0.72.8",
"@types/uuid": "^10.0.0",
"babel-plugin-module-resolver": "^5.0.2",
"expo-haptics": "^14.0.1",