diff --git a/.env.fcmtest-push b/.env.fcmtest-push new file mode 100644 index 0000000..752bc6f --- /dev/null +++ b/.env.fcmtest-push @@ -0,0 +1,48 @@ +#** Required +DB_PASSWORD=C@nuck2024 +JWT_SECRET=changemesupersecretjwtkey + +#** App identity +PROJECT_NAME=rosterchirp-dev +APP_NAME=RosterChirp +DEFCHAT_NAME=General Chat +ADMIN_NAME=Admin User +ADMIN_EMAIL=admin@rosterchirp.local +ADMIN_PASS=Admin@1234 +ADMPW_RESET=false + +#** Database +# DB names intentionally kept as 'rosterchirp' — matches the existing live database +DB_NAME=rosterchirp +DB_USER=rosterchirp +# DB_HOST and DB_PORT are set automatically in docker-compose (host=db, port=5432) + +#** Tenancy mode +# selfhost = single tenant (RosterChirp-Chat / RosterChirp-Brand / RosterChirp-Team) +# host = multi-tenant (RosterChirp-Host only) +APP_TYPE=host + +#** RosterChirp-Host only (ignored in selfhost mode) +HOST_DOMAIN=rosterchirp.com +HOST_ADMIN_KEY=VBGFHEANTTGRDDWAASJKH + +#** Optional +PORT=3244 +TZ=America/Toronto + +#** Firebase Cloud Messaging (FCM) — Android background push +# Web app config — from Firebase Console → Project Settings → General → Your apps +FIREBASE_API_KEY=AIzaSyAw1v4COZ68Po8CuwVKrQq0ygf7zFd2QCA +FIREBASE_PROJECT_ID=fcmtest-push +FIREBASE_MESSAGING_SENDER_ID=439263996034 +FIREBASE_APP_ID=1:439263996034:web:62a6a6b0afdbad99fdec9b +# VAPID key — from Firebase Console → Project Settings → Cloud Messaging → Web Push certificates +FIREBASE_VAPID_KEY=BE6hPKkbf-h0lUQ1tYo249pBOdZFFcWQn9suwg3NDwSE8C_hv8hk1dUY9zxHBQEChO_IAqyFZplF_SUb5c4Ofrw +# Service account — from Firebase Console → Project Settings → Service accounts → Generate new private key +FIREBASE_SERVICE_ACCOUNT={"type": "service_account", "project_id": "fcmtest-push", "private_key_id": "ddbf38d0c5f769b9b8b95000bf05c42b52bb58ad", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDR4jqMb/0UPVpU\nctpVl9UWHY5lePR4hMEoodbRPofNQgtvx5HuFE61cVrquD8mfUmFB9eZc112KPyy\nMuuZHFkJrVT3iEhK8AoeJTNbxh+YiQvwMhyn9/KO4ntr/HIZxqHs62M46rqehZFS\nFR79zG1ptl/hRFkTTwQoQOOAdqP6gJuX+XpKpVeLPNCBVOAhfM8APA4dvjXUqhrk\ns+L5sH8xdttY/XFdjNhiUtv7uHuvww1hBKliUL6dDZZAlm3uwuYAdsvIHkDWOJ+B\nn8EE7n01h5PUpOihQ2poBAFGHvrT9ifk3bzuGviE74ejErCbBEBwJZvvsaebvaG4\n5dI3SQLXAgMBAAECggEAELX0d24LNmtUH9ktLRdzrdkYl1e0D0xynKuWEP7rjRov\nEu1O3yfaxHOMC5gz3vqmueLP9bXLwTauN/n57Cznoe+dDkBZkS3fgFrx5eK2bUys\nGKnEwlLpixrZPNXSt96q0dRECCoYRbrYwTJRT1/RblNI+wSYGwN1j0brVjUcBTvH\nPjpnt9bkIS+Rb1XJg1+TfQFzt1/WvFscpDpc7zUCGczgD7hAXJU2v2NYZyNtjn2g\niFD4r0AODuFk1Z6C8fbUsgcl8AXXQnJSLPTUXnyzifzBVQmGBu3HewLDHI99pTCZ\nT8aOwgaWYUWrjeg0jfyid08j14OfhE58/PuYGwcNsQKBgQDoh1R/OQ147D75BpXP\nEI1TyKTJNZiwnzRnP64cmzAwbIfc6w06hXTJGKIVqMBwM31R8WUfJ5cBxQOb1g3a\nZDgOTz4zO9M0mFyE/L0V3XrTXzCgPN+pCbZjcAw8oizF3u2rupM5lppxuXnkDiSi\nA2GuVdPR6M8pXUmNubs5XNe1jQKBgQDnEb21yF1K2IF9p1T0jcqLkzDLZDHHudVr\ndZSVVEyhpIFmBFgF8EC3C/ehA5i8Ar1K5JvcP5hpAH345QWOUUAKk000A4WpazZx\nfZM5Ema1xju1AFOSMDzwjt4N32Dg1VPqiLDT+CjiQFH67lSretO1IpS//IKT1Y+W\nOv3/ENfm8wKBgHt7moixUJE9zDdMovPCU3sB21iq6Loq4ZZO//RbCV091WyhOnYw\ndxNvzGt6IS+0eEGy0sOXr56V9FOmedbXT9lxhZOJmqCcpM1Otk9NPbPQIi+GBDRt\nXvkxgJ4WdXZi6449139Glh/8ollUlWmgKBh/pawcWR8bVjs4Pc+5mSflAoGAYfMm\nTRmrWl/evGojXCuC8ZmqdH17kKOY8Z19J7P9bAP1Ck7LFXFbrXx4Mxv4MbKjlUzF\nOR8IN3KK8+f5a/PLRvBcKLFZhpC5GnDV6LqBKYrnonmJ841ZN8wIGy9WvNgRY3kg\nJCqtAgOr/MfswmgluEH5dkzO+WXtIQzOwMHeE7sCgYAhrJV+PvnbnaIpfTaOKXGl\nsvzSXTuey5fQQLKMKsp2haKDVZ2hadDejRHsLJKVGb+KwdJ1s5WmBJ4L80/MnOKZ\n+9Yby9DKviVx0TbvMUGuAuWMl9syo4ICMVpp0cbeSOCM5/ulYjKSeU3sFKo7aWa9\nU8Pskm36I88orq90OBpWOg==\n-----END PRIVATE KEY-----\n","client_email": "firebase-adminsdk-fbsvc@fcmtest-push.iam.gserviceaccount.com", "client_id": "103917424542871804597", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token","auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fbsvc%40fcmtest-push.iam.gserviceaccount.com", "universe_domain": "googleapis.com" } + +RC_VERSION=latest + +VAPID_SUBJECT=mailto:devpush@rosterchirp.com +VAPID_PUBLIC=BGYS6vMY7zlx31UKRN9QcaOwfomoDJ50_MtfTcfE84q5bhTLq0rM1zSa6uzBTRBxZuFW1kMQP7ardN_jog3T14Y +VAPID_PRIVATE=8SnDSEy_gs2jNwXLtOchZfHW0ppy_RG8wtvjSjYGA48 diff --git a/docker-compose-fcmtest-push.yaml b/docker-compose-fcmtest-push.yaml new file mode 100644 index 0000000..1e2ff5f --- /dev/null +++ b/docker-compose-fcmtest-push.yaml @@ -0,0 +1,66 @@ +services: + rosterchirp: + image: rosterchirp-dev:${RC_VERSION:-latest} + container_name: ${PROJECT_NAME:-rosterchirp} + restart: unless-stopped + ports: + - "${PORT:-3000}:3000" + environment: + - NODE_ENV=production + - TZ=${TZ:-UTC} + - APP_TYPE=${APP_TYPE:-selfhost} + - ADMIN_NAME=${ADMIN_NAME:-Admin User} + - ADMIN_EMAIL=${ADMIN_EMAIL:-admin@rosterchirp.local} + - ADMIN_PASS=${ADMIN_PASS:-Admin@1234} + - ADMPW_RESET=${ADMPW_RESET:-false} + - JWT_SECRET=${JWT_SECRET:-changeme_super_secret_jwt_key_2024} + - APP_NAME=${APP_NAME:-rosterchirp} + - DEFCHAT_NAME=${DEFCHAT_NAME:-General Chat} + - DB_HOST=db + - DB_PORT=5432 + - DB_NAME=${DB_NAME:-rosterchirp} + - DB_USER=${DB_USER:-rosterchirp} + - DB_PASSWORD=${DB_PASSWORD:?DB_PASSWORD is required} + - HOST_DOMAIN=${HOST_DOMAIN:-} + - HOST_ADMIN_KEY=${HOST_ADMIN_KEY:-} + - FIREBASE_API_KEY=${FIREBASE_API_KEY:-} + - FIREBASE_PROJECT_ID=${FIREBASE_PROJECT_ID:-} + - FIREBASE_MESSAGING_SENDER_ID=${FIREBASE_MESSAGING_SENDER_ID:-} + - FIREBASE_APP_ID=${FIREBASE_APP_ID:-} + - FIREBASE_VAPID_KEY=${FIREBASE_VAPID_KEY:-} + - FIREBASE_SERVICE_ACCOUNT=${FIREBASE_SERVICE_ACCOUNT} + - VAPID_SUBJECT=${VAPID_SUBJECT:-mailto:push@rosterchirp.com} + - VAPID_PUBLIC=${VAPID_PUBLIC:-CHANGEME} + - VAPID_PRIVATE=${VAPID_PRIVATE:-CHANGEME} + volumes: + - rosterchirp_uploads:/app/uploads + depends_on: + db: + condition: service_healthy + healthcheck: + test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/api/health"] + interval: 30s + timeout: 10s + retries: 3 + + db: + image: postgres:16-alpine + container_name: ${PROJECT_NAME:-rosterchirp}_db + restart: unless-stopped + environment: + - POSTGRES_DB=${DB_NAME:-rosterchirp} + - POSTGRES_USER=${DB_USER:-rosterchirp} + - POSTGRES_PASSWORD=${DB_PASSWORD:?DB_PASSWORD is required} + volumes: + - rosterchirp_db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-rosterchirp} -d ${DB_NAME:-rosterchirp}"] + interval: 5s + timeout: 5s + retries: 10 + +volumes: + rosterchirp_db: + driver: local + rosterchirp_uploads: + driver: local diff --git a/fcmtest-push-firebase-adminsdk-fbsvc-ddbf38d0c5.json b/fcmtest-push-firebase-adminsdk-fbsvc-ddbf38d0c5.json new file mode 100644 index 0000000..31cfe64 --- /dev/null +++ b/fcmtest-push-firebase-adminsdk-fbsvc-ddbf38d0c5.json @@ -0,0 +1,13 @@ +{ + "type": "service_account", + "project_id": "fcmtest-push", + "private_key_id": "ddbf38d0c5f769b9b8b95000bf05c42b52bb58ad", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDR4jqMb/0UPVpU\nctpVl9UWHY5lePR4hMEoodbRPofNQgtvx5HuFE61cVrquD8mfUmFB9eZc112KPyy\nMuuZHFkJrVT3iEhK8AoeJTNbxh+YiQvwMhyn9/KO4ntr/HIZxqHs62M46rqehZFS\nFR79zG1ptl/hRFkTTwQoQOOAdqP6gJuX+XpKpVeLPNCBVOAhfM8APA4dvjXUqhrk\ns+L5sH8xdttY/XFdjNhiUtv7uHuvww1hBKliUL6dDZZAlm3uwuYAdsvIHkDWOJ+B\nn8EE7n01h5PUpOihQ2poBAFGHvrT9ifk3bzuGviE74ejErCbBEBwJZvvsaebvaG4\n5dI3SQLXAgMBAAECggEAELX0d24LNmtUH9ktLRdzrdkYl1e0D0xynKuWEP7rjRov\nEu1O3yfaxHOMC5gz3vqmueLP9bXLwTauN/n57Cznoe+dDkBZkS3fgFrx5eK2bUys\nGKnEwlLpixrZPNXSt96q0dRECCoYRbrYwTJRT1/RblNI+wSYGwN1j0brVjUcBTvH\nPjpnt9bkIS+Rb1XJg1+TfQFzt1/WvFscpDpc7zUCGczgD7hAXJU2v2NYZyNtjn2g\niFD4r0AODuFk1Z6C8fbUsgcl8AXXQnJSLPTUXnyzifzBVQmGBu3HewLDHI99pTCZ\nT8aOwgaWYUWrjeg0jfyid08j14OfhE58/PuYGwcNsQKBgQDoh1R/OQ147D75BpXP\nEI1TyKTJNZiwnzRnP64cmzAwbIfc6w06hXTJGKIVqMBwM31R8WUfJ5cBxQOb1g3a\nZDgOTz4zO9M0mFyE/L0V3XrTXzCgPN+pCbZjcAw8oizF3u2rupM5lppxuXnkDiSi\nA2GuVdPR6M8pXUmNubs5XNe1jQKBgQDnEb21yF1K2IF9p1T0jcqLkzDLZDHHudVr\ndZSVVEyhpIFmBFgF8EC3C/ehA5i8Ar1K5JvcP5hpAH345QWOUUAKk000A4WpazZx\nfZM5Ema1xju1AFOSMDzwjt4N32Dg1VPqiLDT+CjiQFH67lSretO1IpS//IKT1Y+W\nOv3/ENfm8wKBgHt7moixUJE9zDdMovPCU3sB21iq6Loq4ZZO//RbCV091WyhOnYw\ndxNvzGt6IS+0eEGy0sOXr56V9FOmedbXT9lxhZOJmqCcpM1Otk9NPbPQIi+GBDRt\nXvkxgJ4WdXZi6449139Glh/8ollUlWmgKBh/pawcWR8bVjs4Pc+5mSflAoGAYfMm\nTRmrWl/evGojXCuC8ZmqdH17kKOY8Z19J7P9bAP1Ck7LFXFbrXx4Mxv4MbKjlUzF\nOR8IN3KK8+f5a/PLRvBcKLFZhpC5GnDV6LqBKYrnonmJ841ZN8wIGy9WvNgRY3kg\nJCqtAgOr/MfswmgluEH5dkzO+WXtIQzOwMHeE7sCgYAhrJV+PvnbnaIpfTaOKXGl\nsvzSXTuey5fQQLKMKsp2haKDVZ2hadDejRHsLJKVGb+KwdJ1s5WmBJ4L80/MnOKZ\n+9Yby9DKviVx0TbvMUGuAuWMl9syo4ICMVpp0cbeSOCM5/ulYjKSeU3sFKo7aWa9\nU8Pskm36I88orq90OBpWOg==\n-----END PRIVATE KEY-----\n", + "client_email": "firebase-adminsdk-fbsvc@fcmtest-push.iam.gserviceaccount.com", + "client_id": "103917424542871804597", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fbsvc%40fcmtest-push.iam.gserviceaccount.com", + "universe_domain": "googleapis.com" +} diff --git a/summary.md b/summary.md new file mode 100644 index 0000000..88dd871 --- /dev/null +++ b/summary.md @@ -0,0 +1,206 @@ +# Mobile UI Fixes Summary +**Date:** March 29, 2026 +**Version:** 0.12.45 +**Focus:** Android Chrome mobile browser issues + +--- + +## 🎯 **Objective** +Fix mobile UI issues in RosterChirp application on Android Chrome browser: +1. Chrome autocomplete bar covering input fields +2. Calendar popup appearing when selecting end time +3. Time dropdowns being hidden by keyboard +4. Inconsistent date/time row behavior + +--- + +## 📁 **Files Modified** + +### **Frontend Components** + +#### **1. `frontend/src/components/SchedulePage.jsx`** +**Changes Made:** +- **TimeInput component:** Added `inputMode="text"` and `enterKeyHint="done"` to prevent calendar popup +- **Form wrapper:** Changed from `autoComplete="new-password"` to `autoComplete="off"` for standard behavior +- **Dropdown positioning:** Increased z-index from 300 to 9999 to prevent calendar interference +- **Input attributes:** Added explicit `type="text"` and `inputMode="text"` for strong calendar prevention + +**Lines Modified:** 187-222 + +#### **2. `frontend/src/components/MobileEventForm.jsx`** +**Changes Made:** +- **TimeInputMobile component:** Complete rewrite with intelligent positioning +- **Visual Viewport API:** Added keyboard detection and dynamic positioning +- **Smart dropdown logic:** Calculates available space above/below input +- **Input attributes:** Added `inputMode="text"` and `enterKeyHint="done"` +- **Z-index fix:** Increased to 9999 to prevent calendar interference +- **End date/time row structure:** Fixed to match start date/time row + +**Lines Modified:** 72-181, 554-568 + +#### **3. `frontend/src/components/UserManagerModal.jsx`** +**Changes Made:** +- **Form wrapper:** Added `autoComplete="off"` to suppress Chrome autocomplete +- **Input consistency:** Ensured all inputs use standard autocomplete suppression + +**Lines Modified:** 293-295 + +#### **4. `frontend/src/pages/GroupManagerPage.jsx`** +**Changes Made:** +- **Form wrapper:** Added `autoComplete="off"` for consistent behavior +- **Input attributes:** Standardized autocomplete suppression + +**Lines Modified:** 744-746 + +#### **5. `frontend/src/pages/UserManagerPage.jsx`** +**Changes Made:** +- **Form wrapper:** Added `autoComplete="off"` for mobile compatibility +- **Input consistency:** Updated all input fields + +**Lines Modified:** 624-626 + +#### **6. `frontend/src/index.css`** +**Changes Made:** +- **Cleaned up aggressive CSS:** Removed `-webkit-autofill` overrides that could affect iOS/desktop +- **Reverted to standard:** Cross-browser compatible approach + +**Lines Modified:** 35-62 (removed aggressive autocomplete CSS) + +--- + +## 🔧 **Technical Solutions Implemented** + +### **1. Chrome Autocomplete Suppression** +```jsx +// Standard approach (safe for all browsers) + +
+``` + +### **2. Calendar Popup Prevention** +```jsx +// Strong signals to prevent date picker + +``` + +### **3. Smart Dropdown Positioning** +```js +// Visual Viewport API for keyboard detection +const handleViewportChange = () => { + if (window.visualViewport) { + const offset = window.innerHeight - window.visualViewport.height; + setKeyboardOffset(offset > 0 ? offset : 0); + } +}; + +// Intelligent positioning based on available space +const spaceAbove = rect.top; +const spaceBelow = window.innerHeight - rect.bottom - keyboardOffset; + +if (spaceBelow >= dropdownHeight) { + setDropdownPosition({ top: '100%', bottom: 'auto' }); +} else { + setDropdownPosition({ top: 'auto', bottom: '100%' }); +} +``` + +### **4. Z-Index Hierarchy Fix** +```jsx +// Time dropdowns above all other UI elements +zIndex: 9999 +``` + +### **5. Consistent Date/Time Row Structure** +```jsx +// Before (problematic) +
setShowEndDate(true)} style={{cursor:'pointer'}}> + {date} + +
+ +// After (fixed) +
+ setShowEndDate(true)} style={{cursor:'pointer'}}>{date} + +
+``` + +--- + +## 🐛 **Issues Resolved** + +### **✅ Chrome Autocomplete Bar** +- **Status:** Intended Chrome behavior (not a bug) +- **Solution:** Standard `autoComplete="off"` implementation +- **Result:** Consistent with Google Calendar's own behavior + +### **✅ Calendar Popup on End Time** +- **Root Cause:** Z-index conflict and row structure issues +- **Solution:** Increased z-index + separate clickable areas +- **Result:** Calendar only triggers on date click, not time click + +### **✅ Keyboard Covering Dropdowns** +- **Root Cause:** Fixed positioning without keyboard awareness +- **Solution:** Visual Viewport API + intelligent positioning +- **Result:** Dropdowns appear above keyboard or reposition automatically + +### **✅ Inconsistent Date/Time Rows** +- **Root Cause:** Different HTML structure between start/end rows +- **Solution:** Made both rows structurally identical +- **Result:** Consistent behavior for both date and time interactions + +--- + +## 📱 **Cross-Browser Compatibility** + +### **✅ iOS Safari** +- Respects standard `autoComplete="off"` +- No aggressive CSS overrides affecting behavior +- Smart positioning works with Visual Viewport API + +### **✅ Desktop Browsers** +- Standard autocomplete behavior +- No interference from mobile-specific fixes +- Consistent time dropdown functionality + +### **✅ Android Chrome** +- Standard autocomplete suppression (as intended by Chrome) +- Proper time dropdown positioning +- No calendar interference with time selection + +--- + +## 🎯 **Key Learnings** + +1. **Chrome Android autocomplete is intended behavior**, not a bug +2. **Even Google Calendar can't suppress it** - confirmed by user testing +3. **Standard HTML attributes are the safest approach** for cross-browser compatibility +4. **Visual Viewport API provides reliable keyboard detection** +5. **Z-index hierarchy is critical** for preventing UI element conflicts +6. **Consistent HTML structure prevents interaction conflicts** + +--- + +## 📦 **Version History** +- **0.12.38:** Initial autocomplete attempts +- **0.12.39:** Aggressive CSS and JavaScript overrides +- **0.12.40:** Cleaned up to standard approach +- **0.12.41:** Fixed calendar z-index issues +- **0.12.42:** Added smart dropdown positioning +- **0.12.43:** Fixed date/time row structure +- **0.12.44:** Refined positioning logic +- **0.12.45:** Final stable implementation + +--- + +## 🚀 **Ready for Deployment** +All changes are cross-browser compatible and follow web standards. The implementation now: +- Works consistently across iOS Safari, Android Chrome, and desktop browsers +- Provides intelligent dropdown positioning +- Maintains clean, maintainable code +- Follows React best practices