🧙♂️ Transform LifeRPG into The Wizard's Grimoire - Production-Ready Application
✨ Major Features Added: - Complete magical theming and rebranding from LifeRPG to The Wizard's Grimoire - Production-grade React frontend with Tailwind CSS v4 and magical aesthetics - Comprehensive analytics dashboard with Recharts integration (ScryingPortal) - Push notifications system with PWA service worker support - Drag & drop functionality using @dnd-kit for habit reordering - Social features with friends system and leaderboards - Performance optimization tools and monitoring - Mobile app enhancement with PWA installation support 🏗️ Technical Infrastructure: - Advanced service worker with offline support and background sync - Zustand state management for scalable application state - Production-ready UI component system with enhanced Button, Card, Input - Progressive Web App (PWA) with manifest and app installation - FastAPI backend with comprehensive API endpoints - Docker containerization and CI/CD pipeline setup 📱 Progressive Web App Features: - Offline functionality with intelligent caching - Push notification support for habit reminders - App installation on mobile and desktop platforms - Background sync for offline data management - Performance monitoring and optimization tools 🎨 User Experience: - Magical wizard/grimoire theming throughout application - Responsive design optimized for all device sizes - Drag & drop habit management with smooth animations - Interactive analytics with multiple chart types - Social connectivity with friends and competitive features - Comprehensive notification and performance settings 🔧 Developer Experience: - Modern development stack with Vite and React - Comprehensive testing setup and CI/CD pipelines - Code quality tools with pre-commit hooks - Docker development environment - Detailed documentation and implementation guides This represents a complete transformation from prototype to production-ready application with enterprise-grade features and magical user experience.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import type { ExpoConfig } from 'expo/config';
|
||||
|
||||
const OIDC_ISSUER = process.env.EXPO_PUBLIC_OIDC_ISSUER || 'https://example-idp.com/realms/liferpg';
|
||||
const OIDC_CLIENT_ID = process.env.EXPO_PUBLIC_OIDC_CLIENT_ID || 'liferpg-mobile';
|
||||
const OIDC_REDIRECT = process.env.EXPO_PUBLIC_OIDC_REDIRECT || 'liferpg:/oauthredirect';
|
||||
const OIDC_SCOPES = (process.env.EXPO_PUBLIC_OIDC_SCOPES || 'openid profile email offline_access').split(' ');
|
||||
const API_BASE = process.env.EXPO_PUBLIC_API_BASE_URL || 'https://api.example.com';
|
||||
|
||||
function toUrl(maybe: string): URL | null {
|
||||
try {
|
||||
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(maybe)) return new URL(maybe);
|
||||
if (/^[a-z][a-z0-9+.-]*:\/[^/]/i.test(maybe)) return new URL(maybe.replace(':/', '://'));
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const parsedRedirect = toUrl(OIDC_REDIRECT);
|
||||
|
||||
const config: ExpoConfig = {
|
||||
name: 'LifeRPG Mobile',
|
||||
slug: 'liferpg-mobile',
|
||||
scheme: 'liferpg',
|
||||
version: '0.0.1',
|
||||
orientation: 'portrait',
|
||||
platforms: ['android', 'ios'],
|
||||
// Use repo image as icon until dedicated asset exists
|
||||
icon: '../../Res/128px-Role-playing_video_game_icon.svg.png',
|
||||
userInterfaceStyle: 'automatic',
|
||||
updates: { enabled: true },
|
||||
extra: {
|
||||
oidc: {
|
||||
issuer: OIDC_ISSUER,
|
||||
clientId: OIDC_CLIENT_ID,
|
||||
redirectUrl: OIDC_REDIRECT,
|
||||
scopes: OIDC_SCOPES,
|
||||
},
|
||||
apiBaseUrl: API_BASE,
|
||||
},
|
||||
ios: {
|
||||
bundleIdentifier: 'com.liferpg.mobile',
|
||||
associatedDomains: [],
|
||||
},
|
||||
android: {
|
||||
package: 'com.liferpg.mobile',
|
||||
intentFilters: [
|
||||
{
|
||||
action: 'VIEW',
|
||||
autoVerify: false,
|
||||
category: ['BROWSABLE', 'DEFAULT'],
|
||||
data: [
|
||||
parsedRedirect
|
||||
? ((() => {
|
||||
const base: any = { scheme: parsedRedirect.protocol.replace(':', '') };
|
||||
if (parsedRedirect.host) base.host = parsedRedirect.host;
|
||||
const path = parsedRedirect.pathname;
|
||||
if (path && path !== '/') base.path = path;
|
||||
return base;
|
||||
})())
|
||||
: { scheme: 'liferpg', host: 'oauthredirect' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
experiments: {
|
||||
// Keep default
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
Reference in New Issue
Block a user