npm package: @mobiloud/ml-review-popup
Description: A modern, iOS-style popup widget designed specifically for MobiLoud mobile apps to prompt users to leave app store reviews
Author: MobiLoud
Latest version: 1.0.0

# Review Prompt Popup Widget

A modern, iOS-style popup widget designed specifically for MobiLoud mobile apps to prompt users to leave app store reviews.

## Features

- ⭐ **Modern Design**: Clean, iOS-style interface with smooth animations
- 📱 **MobiLoud Integration**: Built specifically for MobiLoud app platform
- 🔄 **Smart Review Logic**: "Never show again" after user accepts, retry logic for declines
- 🍪 **Session Management**: Control how often the popup appears using cookies
- 🎯 **URL Targeting**: Show popup only on specific pages
- 🐛 **Debug Mode**: Test in browsers without app environment
- 🌓 **Dark Mode Support**: Manual dark mode control (disabled by default)
- 📊 **Analytics Integration**: Optional Google Analytics event tracking with fail-safe error handling
- 🛡️ **CSS Isolation**: All classes prefixed with `ml-` to prevent conflicts

## Installation

Include the script from the CDN in your HTML `<head>`:

```html
<!DOCTYPE html>
<html>
<head>
    <script src="https://cdn.jsdelivr.net/npm/@mobiloud/ml-review-popup/script.js"></script>
</head>
<body>
    <!-- Your page content -->
</body>
</html>
```

## Basic Usage

```javascript
// Create and show a basic review popup
const popup = createReviewPopup({
    heading: "Rate Our App",
    text: "Love using our app? Your feedback helps us improve and reach more people!",
    enableAnalytics: true,
    autoTrigger: true,
    delay: 3000 // Show after 3 seconds
});
```

## Configuration Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `heading` | string | "Rate Our App" | Main heading text |
| `text` | string | "Love using our app? Your feedback helps us improve and reach more people like you!" | Description text |
| `image` | string | null | Optional image URL (64x64px recommended) |
| `acceptText` | string | "Rate Now" | Accept button text |
| `declineText` | string | "Maybe Later" | Decline button text (empty string hides button) |
| `successMessage` | string | "✅ Thank you for your feedback!" | Message shown after accepting |
| `autoTrigger` | boolean | false | Automatically show popup on page load (once per session) |
| `delay` | number | 0 | Delay in milliseconds before auto-showing |
| `triggerElement` | string/Element | null | CSS selector or element to trigger popup |
| `allowedUrls` | array | null | URL patterns where popup can show |
| `debugMode` | boolean | false | Enable testing in browsers |
| `maxSessions` | number | null | Maximum times to show popup |
| `timeframeDays` | number | null | Timeframe for session limit (in days) |
| `initialDelay` | number | null | Days to wait before first display |
| `darkMode` | boolean | false | Enable dark mode styling |
| `enableAnalytics` | boolean | false | Enable Google Analytics event tracking |
| `colors` | object | {} | Color customization options (see Color Customization section) |
| `onAccept` | function | console.log | Callback when user accepts |
| `onDecline` | function | console.log | Callback when user declines |
| `onClose` | function | console.log | Callback when popup is closed |

## Review-Specific Behavior

### "Never Show Again" Logic
- When user clicks accept ("Rate Now"), the popup sets a permanent cookie flag
- Once accepted, the popup will **never show again** for that user
- This prevents users from being asked multiple times after they've already reviewed

### Decline Behavior
- When user declines, normal session limit logic applies
- Popup can show again based on `maxSessions` and `timeframeDays` settings
- Perfect for periodic review prompts (e.g., every 7 days for 30 days maximum)

### Cookie Tracking
- Uses cookie name: `ml_review_popup_tracking`
- Stores: session count, timestamps, never-show-again flag
- Automatic cleanup and timeframe management

## Color Customization

The widget supports comprehensive color customization to match your app's branding. You can customize all visual elements including buttons, text, backgrounds, and success messages.

### Available Color Options

```javascript
const reviewPopup = createReviewPopup({
    colors: {
        // Button colors
        acceptButton: '#007AFF',        // Accept button background
        acceptButtonHover: '#0056CC',   // Accept button hover state
        acceptButtonText: 'white',      // Accept button text color
        declineButton: '#f5f5f5',       // Decline button background
        declineButtonHover: '#e5e5e5',  // Decline button hover state
        declineButtonText: '#666',      // Decline button text color
        
        // Close button colors
        closeButton: '#f5f5f5',         // Close button background
        closeButtonHover: '#e5e5e5',    // Close button hover state
        closeButtonText: '#666',        // Close button text color
        
        // Text and background colors
        headingText: '#1a1a1a',         // Popup heading text color
        bodyText: '#666',               // Popup body text color
        background: 'white',            // Popup background color
        
        // Success message colors
        successBackground: '#e8f5e9',   // Success message background
        successBorder: '#34c759',       // Success message border
        successText: '#256029',         // Success message text color
        
        // Dark mode colors (used when darkMode is enabled)
        darkMode: {
            background: '#2c2c2e',
            headingText: 'white',
            bodyText: '#a1a1a6',
            declineButton: '#48484a',
            declineButtonHover: '#5a5a5c',
            declineButtonText: '#a1a1a6',
            closeButton: '#48484a',
            closeButtonHover: '#5a5a5c',
            closeButtonText: '#a1a1a6',
            successBackground: '#1e3a1e',
            successBorder: '#30d158',
            successText: '#30d158'
        }
    }
});
```

### Color Usage Notes

- **Flexible Formats**: All color values accept standard CSS color formats (hex, rgb, rgba, named colors, etc.)
- **Advanced Backgrounds**: The `background` property supports gradients and advanced CSS background values
- **Automatic Dark Mode**: Dark mode colors are automatically applied when `darkMode: true` is set
- **Sensible Defaults**: If a color is not specified, the widget uses defaults that match the original design
- **Dynamic Application**: Colors are applied when the popup is created, allowing for runtime customization

### Color Customization Examples

#### Brand Color Scheme
```javascript
const reviewPopup = createReviewPopup({
    heading: "Love Our App?",
    colors: {
        acceptButton: '#e74c3c',        // Your brand red
        acceptButtonHover: '#c0392b',
        headingText: '#2c3e50',         // Your brand dark blue
        background: '#ecf0f1'           // Light gray background
    }
});
```

#### Gradient Background
```javascript
const reviewPopup = createReviewPopup({
    colors: {
        background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
        headingText: 'white',
        bodyText: '#f8f9fa'
    }
});
```

## Common Usage Patterns

### 1. Auto-trigger on Page Load

```html
<!DOCTYPE html>
<html>
<head>
    <script src="https://cdn.jsdelivr.net/npm/@mobiloud/ml-review-popup/script.js"></script>
</head>
<body>
    <!-- Your page content -->
    
    <script>
        // Automatically show popup after page loads
        const popup = createReviewPopup({
            heading: "Love Our App?",
            text: "Your 5-star review helps us reach more people and keeps improving our app!",
            enableAnalytics: true,
            autoTrigger: true,
            delay: 3000 // Show after 3 seconds
        });
    </script>
</body>
</html>
```

### 2. Trigger on Button Click

```html
<!DOCTYPE html>
<html>
<head>
    <script src="https://cdn.jsdelivr.net/npm/@mobiloud/ml-review-popup/script.js"></script>
</head>
<body>
    <button id="rate-app">Rate Our App</button>
    
    <script>
        // Create popup but don't auto-trigger
        const popup = createReviewPopup({
            heading: "Rate Our App",
            text: "Your feedback means the world to us! Leave a review to help others discover our app.",
            enableAnalytics: true,
            autoTrigger: false // Don't auto-trigger
        });

        // Show popup when button is clicked
        document.getElementById('rate-app').addEventListener('click', () => {
            popup.show();
        });
    </script>
</body>
</html>
```

### 3. Trigger on Specific User Action

```html
<!DOCTYPE html>
<html>
<head>
    <script src="https://cdn.jsdelivr.net/npm/@mobiloud/ml-review-popup/script.js"></script>
</head>
<body>
    <!-- Your page content -->
    
    <script>
        // Create popup without auto-trigger
        const popup = createReviewPopup({
            heading: "Enjoying Our App?",
            text: "Your review helps us improve and reach more users like you!",
            enableAnalytics: true,
            autoTrigger: false
        });

        // Show popup when user scrolls down 50%
        let hasShown = false;
        window.addEventListener('scroll', () => {
            if (!hasShown && window.scrollY > document.body.scrollHeight * 0.5) {
                popup.show();
                hasShown = true;
            }
        });
    </script>
</body>
</html>
```

## Google Analytics Integration

### Overview

The widget includes comprehensive Google Analytics event tracking to help you understand user interactions with your review prompt popup. Analytics is **disabled by default** but **should be enabled** in all production implementations for proper tracking and optimization.

### Key Features

- **Recommended for Production**: Analytics should be enabled in all production implementations
- **Optional by Default**: Analytics tracking is disabled by default (`enableAnalytics: false`) but should be explicitly enabled
- **Fail-Safe**: Works gracefully when Google Analytics is not available
- **No Setup Required**: Works with existing Google Analytics installations
- **Comprehensive Tracking**: Tracks all major user interactions
- **Debug Mode**: Detailed console logging when `debugMode: true`

### Events Tracked

| Event Name | Trigger | Event Category | Description |
|------------|---------|----------------|-------------|
| `ml_review_popup_displayed` | Popup becomes visible | engagement | Tracks when popup is shown to user |
| `ml_review_popup_accepted` | Accept button clicked | conversion | User clicks rate/review button |
| `ml_review_popup_declined` | Decline button clicked | engagement | User clicks decline/maybe later |
| `ml_review_popup_closed` | Close button/overlay clicked | engagement | User dismisses popup |

### Configuration

```javascript
const popup = createReviewPopup({
    heading: "Rate Our App",
    text: "Your feedback helps us improve!",
    enableAnalytics: true,  // Enable Google Analytics tracking
    debugMode: true,        // Optional: Enable detailed console logging
    autoTrigger: true
});
```

### Event Structure

All events are sent to Google Analytics with this structure:

```javascript
gtag('event', 'ml_review_popup_displayed', {
    event_category: 'engagement',        // 'engagement' or 'conversion'
    event_label: 'review_prompt_popup',
    page_url: window.location.href,
    page_title: document.title,
    user_agent: navigator.userAgent,
    timestamp: '2024-01-15T10:30:00.000Z',
    // Additional event-specific parameters
});
```

## Examples

### 1. Basic Review Prompt

```javascript
const popup = createReviewPopup({
    heading: "Love Our App?",
    text: "Your 5-star review helps us reach more people and keeps our app free!",
    enableAnalytics: true,
    autoTrigger: true,
    delay: 5000
});
```

### 2. Interval-Based Review Prompts

Show popup every 7 days, maximum 4 times over 30 days:

```javascript
const popup = createReviewPopup({
    heading: "Rate Our App",
    text: "Help others discover our app with your review!",
    enableAnalytics: true,
    maxSessions: 4,        // Show maximum 4 times
    timeframeDays: 30,     // Within 30 days
    initialDelay: 7,       // Wait 7 days before first show
    autoTrigger: true
});
```

### 3. Initial Delay with Recurring Pattern

Wait 5 days before first display, then show once every 2 months:

```javascript
const popup = createReviewPopup({
    heading: "Enjoying Our App?",
    text: "Your feedback helps us improve and grow our community!",
    enableAnalytics: true,
    initialDelay: 5,        // Wait 5 days before first display
    maxSessions: 1,         // Show once per timeframe
    timeframeDays: 60,      // Every 2 months
    autoTrigger: true
});
```

### 4. Single Button (No Decline)

```javascript
const popup = createReviewPopup({
    heading: "Rate Our App",
    text: "Help us reach more users by leaving a quick review!",
    enableAnalytics: true,
    acceptText: "Leave Review",
    declineText: "", // Empty string removes decline button
    autoTrigger: true
});
```

### 5. URL Targeting

Show only on specific pages:

```javascript
const popup = createReviewPopup({
    heading: "Love This Feature?",
    text: "Rate our app to help others discover these amazing features!",
    enableAnalytics: true,
    allowedUrls: [
        "/features/*",
        "/premium/*",
        "*/success/*"
    ],
    autoTrigger: true
});
```

### 6. Manual Trigger

Bind to a button or link:

```javascript
const popup = createReviewPopup({
    heading: "Rate Our App",
    text: "Your feedback helps us improve and reach more users!",
    enableAnalytics: true,
    triggerElement: "#rate-button",
    debugMode: false
});

// Or trigger programmatically
document.getElementById('my-button').addEventListener('click', () => {
    popup.show();
});
```

### 7. With Callbacks

```javascript
const popup = createReviewPopup({
    heading: "Love Our App?",
    text: "Your review means everything to us and our community!",
    enableAnalytics: true,
    onAccept: () => {
        // Custom callback - analytics automatically tracked
        console.log('User agreed to leave a review');
        // Additional custom tracking or actions
    },
    onDecline: () => {
        // Maybe try again later
        console.log('User declined, will ask again based on session limits');
    },
    onClose: () => {
        // Popup was closed
        console.log('Review popup closed');
    }
});
```

### 8. Custom Color Branding

```javascript
const popup = createReviewPopup({
    heading: "Rate Our Premium App",
    text: "Your review helps us continue building amazing features!",
    enableAnalytics: true,
    colors: {
        acceptButton: '#6f42c1',        // Purple brand color
        acceptButtonHover: '#5a2d91',   // Darker purple on hover
        acceptButtonText: '#ffd700',    // Gold text
        headingText: '#6f42c1',         // Purple heading
        background: '#f8f9ff',          // Light purple background
        declineButton: '#e9ecef',       // Light gray decline
        declineButtonText: '#6c757d'    // Gray text
    },
    autoTrigger: true
});
```

### 9. Debug Mode for Testing

```javascript
const popup = createReviewPopup({
    heading: "Test Review Popup",
    text: "This works in any browser for testing the review flow.",
    enableAnalytics: true,
    debugMode: true, // Bypasses MobiLoud app checks
    autoTrigger: true
});
```

## MobiLoud Integration

The widget integrates with MobiLoud's native bridge:

1. **App Detection**: Checks if user agent contains "canvas"
2. **Review Flow**: Calls `nativeFunctions.triggerReviewFlow()`
3. **No Status Monitoring**: Unlike push notifications, reviews don't require real-time status tracking

## Display Logic

The popup will only show when ALL conditions are met:

1. ✅ User is in MobiLoud app (or debugMode is true)
2. ✅ User hasn't already accepted and left a review
3. ✅ Current URL matches allowedUrls (if specified)
4. ✅ Initial delay period has passed (if specified)
5. ✅ Session limit not exceeded (if specified)

## Session Management

Control popup frequency with cookies:

```javascript
// Show maximum 2 times per month
const popup = createReviewPopup({
    maxSessions: 2,
    timeframeDays: 30,
    autoTrigger: true
});
```

### Initial Delay

Wait a specific number of days before the popup becomes eligible to show:

```javascript
// Wait 7 days before first display, then show once every 30 days
const popup = createReviewPopup({
    initialDelay: 7,        // Wait 7 days before first display
    maxSessions: 1,         // Show once per timeframe
    timeframeDays: 30,      // Every 30 days
    autoTrigger: true
});
```

**Common Use Cases:**
- **New user onboarding**: `initialDelay: 3` - Let users explore for 3 days first
- **Satisfaction-based prompts**: `initialDelay: 14, maxSessions: 1, timeframeDays: 30` - Monthly prompts after 2 weeks
- **Re-engagement**: `initialDelay: 30, maxSessions: 1, timeframeDays: 90` - Quarterly prompts after 30 days

The widget stores a cookie (`ml_review_popup_tracking`) with:
- `count`: Number of times shown
- `firstShown`: Timestamp of first display
- `firstEligibleDate`: When popup becomes eligible to show (set by `initialDelay`)
- `neverShowAgain`: Flag set when user accepts review prompt

## Styling

All CSS classes are prefixed with `ml-` to prevent conflicts:

- `.ml-review-popup-overlay` - Background overlay
- `.ml-review-popup` - Main popup container
- `.ml-review-popup-close` - Close button
- `.ml-review-popup-content` - Content wrapper
- `.ml-review-popup-heading` - Heading text
- `.ml-review-popup-image` - Optional image
- `.ml-review-popup-text` - Description text
- `.ml-review-popup-buttons` - Button container
- `.ml-review-popup-accept` - Accept button
- `.ml-review-popup-decline` - Decline button
- `.ml-review-popup-success` - Success message

## API Reference

### Global Function

```javascript
window.createReviewPopup(options)
```

Creates and returns a new `ReviewPromptPopup` instance.

### Instance Methods

#### `show()`
Manually show the popup:
```javascript
popup.show();
```

#### `hide()`
Manually hide the popup:
```javascript
popup.hide();
```

#### `destroy()`
Remove popup and clean up resources:
```javascript
popup.destroy();
```

## Browser Compatibility

- Chrome/Edge: Full support
- Safari: Full support
- Firefox: Full support
- MobiLoud WebView: Optimized

## Testing

Use debug mode for browser testing:

```javascript
const popup = createReviewPopup({
    debugMode: true, // Bypasses all app checks
    autoTrigger: true
});
```

## Troubleshooting

### Popup not showing?

1. Check browser console for errors
2. Verify you're in MobiLoud app (or using debugMode)
3. Ensure user hasn't already accepted review prompt
4. Check URL matches allowedUrls pattern
5. Verify session limits haven't been exceeded

### Cookies not working?

- Cookies require HTTP/HTTPS protocol
- Won't work with `file://` URLs
- Check browser privacy settings

### Style conflicts?

All classes are prefixed with `ml-` to prevent conflicts. If issues persist, increase CSS specificity or use `!important`.

## License

This widget is designed for use with MobiLoud mobile apps. Please refer to your MobiLoud license agreement for usage terms.

## How It Works

### Review Flow

The widget manages the review prompt flow as follows:

1. **Initial Check**: On page load, checks if user has already accepted review prompt
   - If `neverShowAgain` flag is set in cookies, popup won't show
   - If not set, continues with other display logic checks

2. **User Accepts**: When user clicks "Rate Now" button:
   - Sets permanent `neverShowAgain` flag in cookies
   - Calls `nativeFunctions.triggerReviewFlow()` to open native app store review
   - Popup will never show again for this user

3. **User Declines**: When user clicks "Maybe Later":
   - Normal session limit logic applies
   - Popup can show again based on `maxSessions` and `timeframeDays`
   - No permanent flag is set

### App Detection

The widget determines if it's running in a MobiLoud app by:

```javascript
navigator.userAgent.toLowerCase().includes('canvas')
```

This check ensures the popup only appears in the MobiLoud WebView, not in regular browsers (unless `debugMode: true`).

### Session Tracking

#### Auto-trigger Session Control

When `autoTrigger: true`, the popup will only show **once per page session** automatically. This prevents annoying repeated popups if the trigger conditions are met multiple times during a single page visit.

- First auto-trigger: Shows normally
- Subsequent auto-triggers: Blocked with console message
- Manual triggers: Always allowed (via button clicks or `popup.show()`)

#### Cookie-based Session Limiting

For longer-term control across page reloads, use `maxSessions` and `timeframeDays`:

```javascript
// Cookie structure stored as 'ml_review_popup_tracking':
{
    count: 2,                    // Times shown
    firstShown: 1234567890,      // Timestamp of first display
    firstEligibleDate: 1234567890, // When popup becomes eligible (initialDelay)
    neverShowAgain: false        // Permanent flag for accepted reviews
}
```

**Session limit logic:**
1. **Never show again check**: If `neverShowAgain` is true, popup never shows
2. **Initial delay**: If `initialDelay` is set, creates `firstEligibleDate` = now + delay days
3. **Eligibility check**: Popup won't show until current time >= `firstEligibleDate`
4. **First display**: Creates cookie with count=1 and current timestamp
5. **Subsequent displays**: Increments count if within timeframe
6. **Outside timeframe**: Resets count to 1 with new timestamp
7. **At limit**: Popup won't show until timeframe expires

### Review Submission Flow

1. **User Clicks Accept**: Triggers `onAccept` callback
2. **Set Never Show Flag**: Sets permanent `neverShowAgain` cookie flag
3. **Native Bridge Call**: Executes `nativeFunctions.triggerReviewFlow()`
4. **App Store Opens**: Native iOS/Android app store review interface appears
5. **Popup Closes**: Widget automatically hides popup
6. **Permanent Block**: Popup will never show again for this user

### Dark Mode Control

By default, the widget uses light mode styling. To enable dark mode:

```javascript
const popup = createReviewPopup({
    darkMode: true,  // Force dark mode
    // other options...
});
```

Note: The widget no longer automatically adapts to system theme preferences. Dark mode must be explicitly enabled via the `darkMode` option.

## Support

For support with this widget, please contact MobiLoud support or refer to the MobiLoud documentation.