diff --git a/.claude/commands/complete-linear-issue.md b/.claude/commands/complete-linear-issue.md deleted file mode 100644 index ac42e2d..0000000 --- a/.claude/commands/complete-linear-issue.md +++ /dev/null @@ -1,12 +0,0 @@ -# Complete Linear Issue - -When invoked, the user will provide an **issue ID** (e.g. `ABC-123`) after the command. - -## Steps -1. Fetch the Linear issue by id via the Linear MCP server -1. Use the git branch name from the issue (if not already on a branch off from main) -2. Read the issue title, description, comments, and attachments to gain full context -3. Look through the relevant parts of the codebase -4. Think through the problem and solution in detail, make sure to take the simplest path that will complete the issue properly. -5. Outline step-by-step implementation with numbered steps and bullet points -6. Before proceeding with any changes, check in and ask me for approval for the plan, and if you need to, any clarifying questions diff --git a/.claude/commands/fix-ci.md b/.claude/commands/fix-ci.md deleted file mode 100644 index 0ce8609..0000000 --- a/.claude/commands/fix-ci.md +++ /dev/null @@ -1,15 +0,0 @@ -Please identify the issue in this pull request: $ARGUMENTS - -** If no pull request was provided, identify the branch and find the current -pull request** - -Follow the steps below to fix the issue: - -1. Identify the pull request in question, by looking at the supplied arguments - or branch name. -2. Use `script/pr-ci-failures ` to get a list of all the failed - workflow runs and jobs for the PR. -3. For each failed workflow run and job, identify the issue and fix it. -4. Run the corresponding build or test step that failed locally to validate that - your fix has worked. -5. Check in all the fixes, commit them, and push them to the branch. diff --git a/.claude/commands/fix-pr.md b/.claude/commands/fix-pr.md deleted file mode 100644 index 91ffbbe..0000000 --- a/.claude/commands/fix-pr.md +++ /dev/null @@ -1,19 +0,0 @@ -Please identify the issue in this pull request: $ARGUMENTS - -** If no pull request was provided, identify the branch and find the current -pull request** - -Follow the steps below to fix the issue: - -1. Identify the pull request in question, by looking at the supplied arguments - or branch name. -2. Use `script/pr-review-comments ` to get a list of all the comments - on the PR. -3. For each comment, identify the issue and fix it. -4. Run the corresponding build or test step that failed locally to validate that - your fix has worked. -5. If I provided a --push flag, commit the changes and push them to the branch. -6. If I did not provide a --push flag, get all your fixes ready to commit but - don't commit them yet. -7. Ask me to review your changes. -8. If I approve, commit the changes and push them to the branch. diff --git a/.claude/commands/inspo.md b/.claude/commands/inspo.md deleted file mode 100644 index d37d46d..0000000 --- a/.claude/commands/inspo.md +++ /dev/null @@ -1,13 +0,0 @@ -Please download this resource & explore looking for the following context: $ARGUMENTS - -- Create a directory at the root called .inspo if it doesn't exist -- Download the resource to the .inspo directory - - If the resource is a zip file, unzip it - - If the resource is on github use git clone to download it - - If the resource looks like a website, use curl to download it. -- Explore the layout of the resource & looking for files that are relevant to - the context you'd been asked to explore -- Continue thinking hard about what this may inform for you -- If the resource uses the `effect` library use the `effect-mcp` to read extra - documentation about anything you covered or do not understand. -- Finally summarize you findings in a markdown file called .inspo/resource/summary.md diff --git a/.claude/commands/review-pr.md b/.claude/commands/review-pr.md deleted file mode 100644 index a279b04..0000000 --- a/.claude/commands/review-pr.md +++ /dev/null @@ -1,22 +0,0 @@ -Please help me review this pull request: $ARGUMENTS - -** If no pull request was provided, identify the branch and find the current -pull request** - -Follow the steps below to review the pull request: - -1. Check the summary in the body of the blue request to see what the expected - changes are. -2. Look through the bottom of the door change and consider any issues that jump - out at you right away. -3. Re-run yearn, then yarn test locally to see if the build and test all work - successfully. -4. Based on the output of these previous steps, compile a new summary of the - changes that were made and create a list of any follow-up actions to be - taken. -5. Wait for further input from me to decide whether or not we are going to take - action on any of your suggestions. -6. Once provided with this feedback, begin taking those actions, run the build - and test, then assuming everything passes, push the changes back to the PR. -7. Finally, if the PR has not been reviewed on GitHub, we will apply a review - summary confirmed before running this. diff --git a/.cursor/commands/complete-linear-issue.md b/.cursor/commands/complete-linear-issue.md deleted file mode 100644 index 31b881f..0000000 --- a/.cursor/commands/complete-linear-issue.md +++ /dev/null @@ -1,12 +0,0 @@ -# Complete Linear Issue - -When invoked, the user will provide an **issue ID** (e.g. `ABC-123`) after the command. - -## Steps -1. Fetch the Linear issue by id via the Linear MCP server -1. Use the git branch name from the issue (if not already on a branch off from main) -2. Read the issue title, description, and comments to gain full context -3. Look through the relevant parts of the codebase -4. Think through the problem and solution in detail, make sure to take the simplest path that will complete the issue properly. -5. Outline step-by-step implementation with numbered steps and bullet points -6. Before proceeding with any changes, check in and ask me for approval for the plan, and if you need to, any clarifying questions diff --git a/.cursor/commands/fix-ci.md b/.cursor/commands/fix-ci.md deleted file mode 100644 index 0ce8609..0000000 --- a/.cursor/commands/fix-ci.md +++ /dev/null @@ -1,15 +0,0 @@ -Please identify the issue in this pull request: $ARGUMENTS - -** If no pull request was provided, identify the branch and find the current -pull request** - -Follow the steps below to fix the issue: - -1. Identify the pull request in question, by looking at the supplied arguments - or branch name. -2. Use `script/pr-ci-failures ` to get a list of all the failed - workflow runs and jobs for the PR. -3. For each failed workflow run and job, identify the issue and fix it. -4. Run the corresponding build or test step that failed locally to validate that - your fix has worked. -5. Check in all the fixes, commit them, and push them to the branch. diff --git a/.cursor/commands/fix-pr.md b/.cursor/commands/fix-pr.md deleted file mode 100644 index 91ffbbe..0000000 --- a/.cursor/commands/fix-pr.md +++ /dev/null @@ -1,19 +0,0 @@ -Please identify the issue in this pull request: $ARGUMENTS - -** If no pull request was provided, identify the branch and find the current -pull request** - -Follow the steps below to fix the issue: - -1. Identify the pull request in question, by looking at the supplied arguments - or branch name. -2. Use `script/pr-review-comments ` to get a list of all the comments - on the PR. -3. For each comment, identify the issue and fix it. -4. Run the corresponding build or test step that failed locally to validate that - your fix has worked. -5. If I provided a --push flag, commit the changes and push them to the branch. -6. If I did not provide a --push flag, get all your fixes ready to commit but - don't commit them yet. -7. Ask me to review your changes. -8. If I approve, commit the changes and push them to the branch. diff --git a/.cursor/commands/inspo.md b/.cursor/commands/inspo.md deleted file mode 100644 index d37d46d..0000000 --- a/.cursor/commands/inspo.md +++ /dev/null @@ -1,13 +0,0 @@ -Please download this resource & explore looking for the following context: $ARGUMENTS - -- Create a directory at the root called .inspo if it doesn't exist -- Download the resource to the .inspo directory - - If the resource is a zip file, unzip it - - If the resource is on github use git clone to download it - - If the resource looks like a website, use curl to download it. -- Explore the layout of the resource & looking for files that are relevant to - the context you'd been asked to explore -- Continue thinking hard about what this may inform for you -- If the resource uses the `effect` library use the `effect-mcp` to read extra - documentation about anything you covered or do not understand. -- Finally summarize you findings in a markdown file called .inspo/resource/summary.md diff --git a/.cursor/commands/update-sdk-docs.md b/.cursor/commands/update-sdk-docs.md index e6f29f0..84173e3 100644 --- a/.cursor/commands/update-sdk-docs.md +++ b/.cursor/commands/update-sdk-docs.md @@ -29,6 +29,7 @@ Use this command when an SDK ships a new version and the docs need to be updated - Draft a checklist of doc updates needed before editing anything. 3. **Update the docs** - Make the checklist happen inside `content/docs//…`, keeping the authoring rules from `content/README/AGENTS.md` in mind (H2s only, `` sparingly, callouts at top, navigation via `meta.json`, etc.). + - Mirror any changes to the upstream changelog by copying changes from `reference//CHANGELOG.md` to `content/docs//changelog.mdx` whenever it changes so the SDK section stays current. - Examples of common edits: - Add or update API reference pages under `sdk-reference/`. - Revise quickstart/install instructions when setup steps changed. diff --git a/AGENTS.md b/AGENTS.md index 81aa185..0e1e7f5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,146 +1,127 @@ -# AGENTS.md - Documentation Site Development +# AGENTS.md -This file provides guidance for working with the Superwall documentation site infrastructure and build system. +Instructions for AI coding agents working in this repository (Superwall docs site). -For documentation content guidelines (adding/editing docs), see `content/README/AGENTS.md`. +For documentation-writing guidelines (tone, content conventions, etc.), see `content/README/AGENTS.md`. -## Documentation Site Commands +## Project overview +- **Stack**: Next.js + Fumadocs (MDX) + TailwindCSS +- **Hosting**: Cloudflare (via `@opennextjs/cloudflare`) +- **Docs**: multi-platform SDK docs (iOS, Android, Flutter, Expo, React Native) + dashboard/integration guides +- **Search/AI**: built-in search + AI search mode; support center integration +- **Theming**: custom themes; dark/light mode is disabled by default -From the docs directory (`apps/docs`): +## Non-negotiable rules +- **Never deploy without explicit user approval.** +- **Edit source docs only**: update files in `content/docs/**` (and `content/shared/**` if used). + **Never edit `public/**`** (it is generated and will be overwritten). +- **SDK references first**: if a task needs SDK APIs/changelogs, run `bun run download:references` before writing. +## Common commands (run from repo root) ```bash -# Generate documentation files -bun run build - -# Start documentation development server +# Dev (http://localhost:8293) bun run dev -# Generate AI-optimized documentation files -tsx scripts/generate-llm-files.ts -tsx scripts/generate-md-files.ts +# Build (runs all generators, then MDX + Next build) +bun run build + +# Clear caches (when build/dev gets weird) +rm -rf .next +bun run clear:cache -# Copy documentation images +# Content generation / assets +bun run generate:llm +bun run generate:md bun run copy:docs-images +bun run watch:images -# Deploy to Cloudflare (staging) -bun run deploy:staging +# Cloudflare builds / preview +bun run build:cf +bun run preview -# Deploy to Cloudflare (production) +# Deploy (DO NOT RUN WITHOUT USER APPROVAL) +bun run deploy:staging bun run deploy ``` -DO NOT EVER DEPLOY WITHOUT CHECKING WITH THE USER - -## SDK References - -When working on tasks that require referencing any of the Superwall SDKs (iOS, Android, Flutter, or React Native), ALWAYS run the following command first to ensure you have the latest SDK source code: +## Content + navigation system +- **Navigation** is defined by `meta.json` files alongside content directories. + - If a `meta.json` does **not** include `"..."`, every page must be explicitly listed. + - Use **relative paths without extensions** (e.g. `"guides/my-guide"`, not `"guides/my-guide.mdx"`). + - You can nest objects to group related pages. +- **Adding a new page**: + - Create the `.mdx` file under the correct `content/docs/**` folder. + - Update the folder’s `meta.json` to include it in navigation. + - Run `bun run build` to verify generation + routing. +- **Images**: assets in docs content are copied into `public/` during build (use `bun run copy:docs-images`; in dev use `bun run watch:images`). + +## Routing, redirects, and base path +- The site is served under **`/docs`** (`basePath` + `assetPrefix` in `next.config.ts`). +- `/docs/sdk/*` is a selector route that redirects to the chosen platform page. +- Redirects are generated from `redirects-map.ts` (`folderRedirectsMap`, `fileRedirectsMap`, `externalRedirectsMap`) inside `next.config.ts`. + **Changes require a rebuild** to take effect. + +## MDX / remark pipeline (order matters) +Custom remark plugins are wired in `source.config.ts`. The current order is: +1. `remark-image-paths` (must run first) +2. `remark-follow-export` +3. Fumadocs “existing” plugins +4. `remark-include` +5. `remark-directive` +6. `remark-tabs-syntax` +7. `remark-code-language` +8. `remark-codegroup-to-tabs` +9. `remark-sdk-filter` (must run last; removes non-matching SDK blocks) + +## Key files (where to look first) +- **Config**: `source.config.ts`, `next.config.ts`, `open-next.config.ts`, `tsconfig.json` +- **Redirects**: `redirects-map.ts` +- **Layout/routing**: `src/app/layout.config.tsx`, `src/app/(docs)/[[...slug]]/page.tsx` +- **Source plumbing**: `src/lib/source.ts`, `src/mdx-components.tsx` +- **Plugins**: `plugins/*` +- **Scripts**: `scripts/*` (title map, generators, image copy/watch, cache clear, etc.) +- **New UI/components**: put React components in `src/components/` and wire site-level layout/nav in `src/app/layout.config.tsx` / `src/lib/source.ts`. + +## What requires a rebuild (not just hot reload) +- `meta.json` navigation edits +- `redirects-map.ts` changes +- remark plugin changes (`plugins/*` or `source.config.ts`) +- new/updated images (make sure copy/watch runs) + +## Cloudflare / deployment notes +- Cloudflare builds use `bun run build:cf` (OpenNext adapter). +- Cloudflare Workers disallow `eval()`: `next.config.ts` aliases the eval-based `fumadocs-ui` `hide-if-empty` component to `src/components/HideIfEmptyStub.tsx`. + +## Performance notes +- Builds use Turbo (`next dev --turbo`) and Cloudflare’s CDN for delivery. + +## SDK references (required for SDK-accuracy work) +Run: ```bash bun run download:references ``` +This clones/pulls SDK repos into `reference/` (gitignored). Use the source to confirm API signatures and behavior before updating docs. -This command will: -- Clone the iOS, Android, Flutter, and React Native SDK repositories into the `reference/` directory -- If the repositories already exist, it will pull the latest changes -- The reference directory is gitignored, so these repositories won't be committed - -SDK repositories: -- iOS: `reference/ios/` (superwall-ios) -- Android: `reference/android/` (superwall-android) -- Flutter: `reference/flutter/` (Superwall-Flutter) -- React Native: `reference/react-native/` (expo-superwall) - -After downloading the references, you should: -1. Directly examine the source files in these repositories to understand the actual implementation -2. Look at the public API files to understand the exact syntax and method signatures -3. Check example projects or tests to see how features are used in practice -4. Use this first-hand knowledge to ensure documentation is accurate and reflects the current SDK capabilities - -This direct inspection is critical for: -- Writing accurate code examples with correct syntax -- Understanding available features and their proper usage -- Ensuring documentation matches the actual SDK implementation -- Identifying any undocumented features or recent changes +## SDK changelogs (strict rules) +- **Docs location**: `content/docs//changelog.mdx` for `ios`, `android`, `flutter`, `expo`, `react-native` +- **Source of truth**: `reference//CHANGELOG.md` +- **Process**: run `bun run download:references`, then copy upstream changelog **verbatim** +- **Allowed edits**: keep required frontmatter (`title: "Changelog"` + short per-SDK description). Optionally add one top-level deprecation callout. +- **Formatting**: no MDX components/wrappers in the changelog body; escape angle brackets that would parse as tags (e.g. `< 8`). +- **Navigation**: ensure each SDK `meta.json` lists `changelog` right after `index`. ## AI SDK / AI SDK Elements - - For any AI SDK or AI SDK Elements work, fetch the latest docs via Context7 MCP before making changes. -## Documentation Site Architecture - -### Build System -- Uses Fumadocs for documentation generation -- Source files in `/content/docs/` → Generated files in `/public/` -- Build process transforms `.mdx` to `.md` and processes content -- Turbo handles build caching and optimization - -### Key Files and Directories -- `source.config.ts` - Fumadocs configuration -- `src/lib/source.ts` - Source configuration and icons -- `src/app/layout.config.tsx` - Site layout configuration -- `plugins/` - Custom remark plugins for content processing -- `scripts/` - Build and generation scripts - -### Navigation and Routing -- Navigation structure defined by `meta.json` files in content directories -- Pages auto-generated based on file structure in `/content/docs/` -- Custom components in `src/components/` for enhanced UI -- `/docs/sdk/*` wildcard path renders the SDK selector page before redirecting to the chosen platform (e.g. `/docs/sdk/quickstart/tracking-subscription-state` → `/docs/ios/quickstart/tracking-subscription-state`). - -### Content Processing Plugins -- `remark-tabs-syntax` - Processes tab syntax -- `remark-code-language` - Handles code language detection -- `remark-image-paths` - Processes image paths -- `remark-sdk-filter` - Filters content by SDK - -## Development Workflow - -### Local Development -1. Start dev server: `bun run dev` -2. Edit content in `/content/docs/` -3. Changes automatically rebuild and refresh -4. Test build locally: `bun run build` - -### Adding New Features -- Custom components go in `src/components/` -- Icons and assets in `src/lib/source.ts` -- Layout changes in `src/app/layout.config.tsx` - -### Build and Deploy -- Staging: `bun run deploy:staging` -- Production: `bun run deploy` -- Uses Cloudflare Pages for hosting - -## Site Configuration - -### Themes and Styling -- Custom themes in component configuration -- TailwindCSS for styling -- Dark/light mode disabled by default - -### Search and AI Features -- AI search integration -- Custom search dialog component -- Support center integration - -## Performance Considerations -- Images automatically optimized during build -- Static site generation for fast loading -- CDN distribution via Cloudflare -- Build caching with Turbo for fast rebuilds - -## Troubleshooting Site Issues - -### Build Failures -- Check for syntax errors in `.mdx` files -- Verify `meta.json` files are valid JSON -- Run `bun run build` to see detailed error messages - -### Development Server Issues -- Clear cache: `rm -rf .next` -- Restart dev server -- Check for port conflicts - -### Deployment Issues -- Verify Cloudflare configuration -- Check build logs in deployment dashboard -- Ensure all environment variables are set \ No newline at end of file +## Environment variables +- **`SEARCH_MODE`**: `'fumadocs'` (default) or `'rag'` (external AI search) +- **`NEXTJS_ENV`**: development/production +- Optional integration keys may exist (Slack, Mesh, Unify, RB2B, Pylon). See project environment documentation if present. + +## TypeScript path aliases +- `@/*` → `src/*` +- `@/.source` → `.source/index.ts` (generated by Fumadocs) + +## Troubleshooting +- **Build failures**: check MDX syntax; validate `meta.json`; ensure listed pages exist; run `bun run build` for full errors. +- **Dev server issues**: `rm -rf .next`, `bun run clear:cache`, verify port 8293 is free, reinstall dependencies if needed. \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 6aec4e5..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,203 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -For documentation content guidelines (adding/editing docs), see `content/README/AGENTS.md`. - -## Project Overview - -This is the Superwall documentation site built with Next.js, Fumadocs, and deployed to Cloudflare Pages. The site serves SDK documentation for multiple platforms (iOS, Android, Flutter, Expo) along with dashboard guides and integration documentation. - -## Essential Commands - -### Development -```bash -# First-time setup: build documentation files -bun run build - -# Start development server at http://localhost:8293 -bun run dev - -# Clear Next.js cache if needed -rm -rf .next - -# Clear build cache -bun run clear:cache -``` - -### Build & Deploy -```bash -# Build for production -bun run build - -# Deploy to staging -bun run deploy:staging - -# Deploy to production -bun run deploy -``` - -**CRITICAL: DO NOT EVER DEPLOY WITHOUT CHECKING WITH THE USER** - -### Content Generation Scripts -```bash -# Generate AI-optimized markdown files -tsx scripts/generate-llm-files.ts - -# Generate standard markdown files -tsx scripts/generate-md-files.ts - -# Copy documentation images -bun run copy:docs-images - -# Watch for image changes during development -bun run watch:images -``` - -## Architecture Overview - -### Content Pipeline -The documentation follows a strict source-to-output pipeline: - -1. **Source files**: `/content/docs/**/*.mdx` (ALWAYS edit here) -2. **Processing**: Build scripts + remark plugins transform content -3. **Generated output**: `/public/**/*.md` (NEVER edit - auto-generated) - -**CRITICAL**: Always edit files in `/content/docs/`, NEVER in `/public/`. The `/public/` directory contains auto-generated files that will be overwritten during build. - -### Multi-Platform SDK Architecture -The site uses a sophisticated system to serve documentation for multiple SDKs from shared and platform-specific content: - -- **Platform folders**: `/content/docs/{ios,android,flutter,expo}/` -- **Shared content**: `/content/shared/` contains reusable MDX files -- **SDK filtering**: `remark-sdk-filter` plugin removes platform-specific content blocks during build -- **SDK selector pattern**: `/docs/sdk/*` routes redirect to platform-specific pages (e.g., `/docs/sdk/quickstart/install` → `/docs/ios/quickstart/install`) - -### Remark Plugin Pipeline -Content processing happens through a series of custom remark plugins (defined in `source.config.ts`): - -1. `remark-image-paths` - Resolves and processes image paths (runs first) -2. `remark-follow-export` - Handles exported content references -3. `remark-include` (fumadocs) - Processes file includes from shared content -4. `remark-directive` - Parses custom directive syntax (e.g., `:::expo`) -5. `remark-tabs-syntax` - Transforms tab syntax for multi-platform code -6. `remark-code-language` - Detects and sets code block languages -7. `remark-codegroup-to-tabs` - Converts code groups to tabbed interfaces -8. `remark-sdk-filter` - Removes non-matching SDK-specific blocks (runs last) - -The order matters - image path resolution must happen before other transformations. - -### Navigation System -- **Structure**: Each folder can have a `meta.json` file defining navigation order and hierarchy -- **Pages without catch-all**: If `meta.json` doesn't include `"..."`, every page must be explicitly listed -- **Nested navigation**: Related APIs can be grouped as nested objects (e.g., `PaywallOptions` under `SuperwallOptions`) -- **Auto-generation**: Pages are generated based on file structure + meta.json configuration -- **File references**: Use relative paths without extensions (e.g., `"guides/my-guide"` not `"guides/my-guide.mdx"`) - -### Redirects System -- **Configuration**: `redirects-map.ts` defines URL redirects -- **Types**: - - `folderRedirectsMap` - Redirects from root to folder paths - - `fileRedirectsMap` - File-to-file redirects (e.g., legacy SDK installation paths) - - `externalRedirectsMap` - External URL redirects -- **Processing**: `next.config.ts` generates redirect rules from these maps during build -- **Note**: Changes to redirects require rebuild to take effect - -### Deployment & Hosting -- **Platform**: Cloudflare Pages via OpenNext.js adapter (`@opennextjs/cloudflare`) -- **Environments**: Production and staging -- **Build process**: `bun run build:cf` creates Cloudflare-compatible output -- **Webpack customization**: `next.config.ts` includes aliases to replace eval-based components (not allowed on Cloudflare Workers) -- **Base path**: All routes are prefixed with `/docs` (configured in next.config.ts) - -## Key Files & Their Purposes - -### Configuration -- `source.config.ts` - Fumadocs configuration and remark plugin chain -- `next.config.ts` - Next.js config, redirects, webpack aliases, basePath (`/docs`) -- `redirects-map.ts` - URL redirect mappings (exported as const objects) -- `tsconfig.json` - TypeScript config with path aliases (`@/*` → `src/*`) -- `.env.example` - Environment variable templates - -### Layout & Routing -- `src/app/layout.config.tsx` - Site layout, navigation, theme configuration -- `src/lib/source.ts` - Documentation source configuration and SDK icons -- `src/mdx-components.tsx` - Custom MDX component overrides -- `src/app/(docs)/[[...slug]]/page.tsx` - Dynamic route handler for all doc pages - -### Custom Plugins -- `plugins/remark-sdk-filter.ts` - Removes SDK-specific blocks (directive `:::expo` or JSX `
`) -- `plugins/remark-tabs-syntax.ts` - Processes custom tab syntax -- `plugins/remark-code-language.ts` - Detects code block languages -- `plugins/remark-image-paths.ts` - Resolves image paths for build -- `plugins/remark-follow-export.ts` - Handles content exports -- `plugins/remark-codegroup-to-tabs.ts` - Converts code groups to tabs - -### Build Scripts -- `scripts/generate-title-map.ts` - Creates title lookup map for pages -- `scripts/generate-llm-files.ts` - Generates AI-optimized documentation -- `scripts/generate-md-files.ts` - Converts MDX to plain markdown -- `scripts/copy-docs-images.cjs` - Copies images from content to public -- `scripts/watch-docs-images.ts` - Watches for image changes during dev -- `scripts/clear-cache.ts` - Clears build cache - -## Adding New Documentation Pages - -When adding new documentation pages: - -1. Create the `.mdx` file in the appropriate `/content/docs/` subdirectory -2. **Update the corresponding `meta.json` file** in the same folder to include the new page in navigation -3. Use relative paths without file extensions in `meta.json` (e.g., `"guides/my-new-guide"`) -4. Group related APIs as nested objects when appropriate -5. Run `bun run build` to generate output files and verify - -### SDK Documentation Structure -Each SDK follows a standard structure: -``` -content/docs/{sdk}/ -├── quickstart/ # Getting started guides -├── guides/ # Conceptual docs and tutorials -│ ├── advanced/ # Advanced topics (collapsed in nav with meta.json) -│ └── ... -└── sdk-reference/ # API reference (one MDX per public symbol) -``` - -## Environment Variables - -See `.env.example` for required environment variables: -- `SEARCH_MODE` - Toggle between 'fumadocs' (default) or 'rag' (uses external AI search at mcp.superwall.com) -- `NEXTJS_ENV` - Development or production -- Integration keys for Slack, Mesh, Unify, RB2B, Pylon (optional for local dev) - -## Development Workflow Notes - -- **Dev server port**: 8293 (configured in package.json dev:next script) -- **Changes requiring rebuild**: Redirects, remark plugin modifications, meta.json changes, image additions -- **Auto-reload**: Content changes in `/content/docs/` rebuild automatically during dev -- **Image handling**: Images in `/content/docs/images/` are copied to `/public/` during build -- **Component customization**: Add custom components to `src/components/` and reference in MDX -- **Turbo cache**: Build uses Turbo for caching and optimization - -## TypeScript Path Aliases -- `@/*` maps to `src/*` -- `@/.source` maps to `.source/index.ts` (generated by Fumadocs) - -## Troubleshooting - -### Build Failures -- Check for syntax errors in `.mdx` files -- Verify `meta.json` files are valid JSON -- Ensure all pages in meta.json exist as files -- Run `bun run build` to see detailed error messages - -### Development Server Issues -- Clear cache: `rm -rf .next` -- Run full rebuild: `bun run build` -- Check for port conflicts (port 8293) -- Verify node_modules are installed - -### Deployment Issues -- Verify Cloudflare configuration in wrangler.jsonc -- Check build logs in deployment dashboard -- Ensure all environment variables are set -- Verify redirects-map.ts exports are valid diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/content/docs/android/changelog.mdx b/content/docs/android/changelog.mdx new file mode 100644 index 0000000..bb4f8dc --- /dev/null +++ b/content/docs/android/changelog.mdx @@ -0,0 +1,1162 @@ +--- +title: "Changelog" +description: "Release notes for the Superwall Android SDK" +--- + +## 2.6.6 + +## Enhancements +- Add dynamic notification support and scheduling enabling deeper personalization of notifications +- Provides a `CustomerInfo` class API, allowing you to observe the customer's purchases and subscription lifecycle via `Superwall.instance.customerInfo` flow +- Provides a new delegate method to observe customer info changes - `fun customerInfoDidChange(from: CustomerInfo, to: CustomerInfo)` +- Provides a `CustomerInfoDidChange` event to track customer info changes +- Overrides `Collection.plus`and `.toSet` methods to ensure our merging methods are used. +- Provides a `userAttributesDidChange(newAttributes: Map)` method in Superwall Delegate to track external (i.e. paywall) attribute changesg +- Allows triggering a `transaction_abandon` offer on a `paywall_decline` offer and vice-versa, whereas previously it would trigger a presentation error. +- Add a `Superwall.teardown` method and `Superwall.instance.refreshConfiguration()` for development with hot-reload based frameworks + +## ⚠️ Warning ⚠️ +If you are using a Purchase Controller and web2app or app2web purchases, you will have to update your purchase controller +to listen to `Superwall.instance.customerInfo` which will provide you with the relevant web entitlements and call +`setSubscriptionStatus` accordingly. + +## 2.6.5 + +## Dependencies +- Reverts `androidx.lifecycle:lifecycle-runtime-ktx` to 2.8.4 to ensure old Compose BOM compatiblity + +## Enhancements +- Improves error messaging in play store errors + +## Fixes +- Fixes edge case bug with wrong entitlement being matched in cases where product ID's match and base plans differentiate by suffix only +- Fixes issue with composable paywall state updates not firing in onAttach + +## 2.6.4 + +## Enhancements +- Improves error and timeout handling +- Hardens paywall recreation in case of render process crash + +## 2.6.3 + +## Enhancements +- Adds `productIdentifier` to RedemptionResult's `PaywallInfo` object + +## Fixes +- Fixes nested scrolling issue in Modal webviews +- Removes node removal for `com.google.android.gms.permission.AD_ID` from Manifest +- Ensures remote entitlements in the background refresh without feature flags + +## 2.6.2 + +## Enhancements +- Adds `Superwall.instance.consume(purchaseToken)` method to help easily consume in-app purchases + +## Fixes +- Fixes issue with deeplink params not being handled properly in some cases +- Fixes issue with Drawer and Modal displays on Android 14 Samsung devices +- Fixes selection issue with some OTP, ensures after consuming the Status is synced + +## 2.6.1 + +## Enhancements +- Enables Stripe and Paddle checkout via in-app payment sheets +- Improves product handling and redemption for Stripe and Paddle + +## Fixes +- Fixes issue with Google's Play Billing library auto-reconnection + +## 2.6.0 ⚠️ [Deprecated] + +## Notes +- This version is deprecated due to discovery of an issue in Play Billing library which could cause runtime issues +- Please use version 2.6.1 + +## 2.5.8 + +## Fixes +- Fix lifetime purchase entitlements not being discovered in some cases on purchase +- Fix potential ANR issues where some animations would end up looping over on main thread +- Fix webview client not behaving properly when using a resetted paywall + +## 2.6.0-alpha + +- Add app2web support, allowing users to purchase Stripe or Paddle products without leaving your app +- Add `PaymentSheet` purchase type enabling quick bottom sheet purchases +- Add support for Android app links purchase redeeming + +## 2.5.7 + +## Fixes +- Fix `demandScore` and `demandTier` getting removed from some events +- Fixes paywall navigation resetting after backgrounding +- Removes webview flags which can cause off-screen render issues + +## 2.5.6 + +## Enhancements + +- Add support for rerouting back button if enabled in paywall settings + - Handled by `SuperwallOptions.PaywallOptions.onBackPressed`, which enables you to consume the back press or let the SDK consume it +- Add support for redeeming web entitlements with Paddle + +## Fixes +- Fix binary .so file regression to ensure 16kb page size compatibility +- Fix potential issue with paywall not dismissing due to paywall_decline concurrency issue + +## 2.5.5 + +## Enhancements +- Expose `signature` on `StoreTransaction` + +## Fixes +- ⚠️ Important - in the recent versions we have added usage of Google AppSetId and AdID to enable automatic attribution with Google's ad networks. +- Due to issues with Google's detection of the usage, they have been removed temporarily and will be added back once the issue is resolved. +- As an alternative, you can set those attribution identifiers using `AttributionProvider.GOOGLE_APP_SET | AttributionProvider.GOOGLE_ADS` + +## 2.5.4 + +## Enhancements +- `PaywallComposable` now reapplies theme on system change and `PaywallView` now exposes an `onThemeChanged()` method +- `DeepLink` event now exposes query params + +## 2.5.3 + +## Enhancements +- Adds ability to specify a custom height and corner radius for the drawer presentation style. +- Adds ability to display a `Popup` presentation style +- Adds ability to request reviews from paywall actions, including: + - Value `device.reviewRequestCount` that returns total request counts + - Method `device.reviewRequestsInHour|Day|Week|Year|reviewRequestsSinceInstall` computed methods for granular targeting +- Adds `Superwall.instance.setIntegrationAttributes` method enabling you to set integration identifiers for the users from different platforms (Adjust, Mixpanel, Meta, etc.) +- Adds `Superwall.instance.showAlert` method enabling you to easily show an alert over the current paywall +- Adds `PaywallOptions.timeoutAfter` to easily control timeout of paywalls when not using fallback loading +- Adds `Superwall.instance.setIntegrationAttributes` and `Superwall.instance.integrationAttributes` to set integration identifiers (i.e. Mixpanel, Appsflyer, etc) +- Adds user attributes to `TransactionComplete` and `PaywallOpen` events +- Adds device ID to device attributes + +## Fixes + +- Fixes memory leak issues in `PaywallBuilder` would keep the paywall view alive in some cases +- Fixes issue where some events would be missing properties + +## 2.5.1 + +## Enhancements +- Improves subscribed user experience in cases with slow configuration + +## 2.5.0 + +## Enhancements +- Updates Google Play billing library to v8. Unfortunately, Google has broken backwards compatibility with previous versions, so if you're using the standalone library too ensure it is compatible with v8. +- Adds kotlin version to device variables + +## Fixes +- Reduces noisy logging when product is missing an offer +- Ensures that getting experimental properties works for consumable products + + +## 2.4.1 + +## Fixes +- Google Play Billing integration with newer libraries (such as RC 9.*) +- Reduces noisy logging when product is missing an offer +- Ensures that getting experimental properties works for consumable products + +## 2.4.0 + +## Enhancements +- Increases superscript version to 1.0.2 with improved type and null safety + +## 2.3.3 + +## Fixes +- Ensure properties are always properly serialized + +## 2.3.2 + +## Enhancements +- Adds new properties to count placement occurrences in specific time: `placementsInHours`, `placementsInDay`, `placementsInWeek`, `placementsInMonth`, `placementsSinceInstall` + +## Fixes +- Fixes an issue where a redemption could succeed but throw an error +- Fixes an issue with receipt manager when there is no purchases and experimental properties are enabled + +## 2.3.1 + +## Fixes +- Fixes an issue where entitlements would not be reset on time +- Ensures `redeem` is only done on initial config not on config refresh + +## Enhancements + +- Adds `overrideProductsByName` property to allow globally overriding products on paywalls. This property accepts a map of product names to product identifiers (strings). Local overrides provided via `PaywallOverrides` take precedence over global overrides. +- Adds `ProductOverride` sealed class to provide flexible product override handling with support for both product IDs and `StoreProduct` objects. + +## 2.3.0 + +## Enhancements + +- Deprecated `Superwall.instance.handleDeepLink` in favor of static `Superwall.handleDeepLink` to ensure links received before `configure` completion are handled properly +- Adds `externalAcountId`, provided to Google Play billing upon purchase as a SHA256 of the userId or the userId itself if `passIdentifiersToPlayStore` option is provided. +- Adds a `SuperwallOption` named `enableExperimentalDeviceVariables`. When set to true, this enables additional device-level variables: `latestSubscriptionPeriodType`, `latestSubscriptionState`, and `latestSubscriptionWillAutoRenew`. These properties provide information about the most recent Google Play subscription on the device and can be used in audience filters. Note that due to their experimental nature, they are subject to change in future updates. +- Update `com.android.billingclient` to version 7.1.1 to align with Google's latest requirements + +## Fixes +- Fixes issues with paywall destruction when activity performs a hot reload (i.e. during update) +- Fixes issue where the feature block would be triggered on non-gated paywalls when the app is minimised + +## 2.2.3 + +## Fixes +- Fix potential issue with device enrichment and attributes synchronisation causing a lock + +## 2.2.2 + +## Enhancements + +- Adds `demandScore` and `demandTier` to device attributes using an off-device advanced machine learning model. A user is assigned these based on a variety of factors to determine whether they're more or less likely to convert and can be used within audience filters. +- Adds `deviceTier` to the device attributes using an on-device scoring system to place the device in a tier based on it's capabilities. This can be used in audience filters to display different paywalls based on the user's device capabilities. The value can be one of the following values `ultraLow`, `low`, `mid`, `high`, `ultra_high`, `unknown`. NOTE: This property is still experimental + +## Fix + +- Fixes potential issue in `web2app` with subscription being overriden by the polling due to extra API calls + +## 2.2.0 + +## Enhancements +- Updates binaries to work on 16kb page sizes + +## 2.1.2 + +## Fixes +- Fix issue with deep link referrer throwing a DeadObjectException + +## 2.1.1 + +## Enhancements + +- Add optimisticLoading paywall option that hides the shimmer when HTML is loaded +- Prevent stopping the paywall handler listening with onDismiss when reason is None +- Improve PaywallBuilder API for non-kotlin and non-coroutine users +- Expose `deviceAttributes()` function to retrieve session device attributes + + +## 2.1.0 + +## Enhancements + +- Updates kotlin version to 2.0.21 +- Updates `compileSDK` to 35 +- Adds web checkout and redemption support +- Adds SuperwallDelegate methods `willRedeemLink` and `didRedeemLink` + +## Fixes +- Removes lock while reading cache that could cause ANR on the main thread +- Fixes issue where experiment and variant ID would be missing due to concurrency issues + +## 2.1.0-beta.1 + +## Enhancements + +- Updates kotlin version to 2.0.21 +- Updates `compileSDK` to 35 +- Adds web checkout and redemption support +- Adds SuperwallDelegate methods `willRedeemLink` and `didRedeemLink` + +## 2.0.8 + +## Fixes +- Fixes the serialization issue with Kotlin 2.0 + +## 2.0.7 + +## Enhancements +- Improves how errors are handled when loading, improving the UX and reloading in real failure cases +- Added `device.subscriptionStatus` to the device object + +## Fixes +- Fixes an issue where users of kotlin 2.0 would experience a `NoAudienceMatch` when evaluating rules + +## 2.0.6 + +## Fixes +- Fix potential crash while setting render priority + +## 2.0.5 + +## Fixes +- Fix issue with `original_transaction_id` missing when using a `PurchaseController` + +# 2.0.4 + +## Enhancements +- Provide overloads for Java interop +- Provide utility functions for Java interop with `PurchaseController` + +# 2.0.3 + +## Enhancements + +- Renames `SuperwallPlacement` back to `SuperwallEvent` + +# 2.0.2 + +## Fixes + +- Fixes issue with `NoAudienceMatch` appearing on some devices and issues with certain campaign rules + +# 2.0.1 + +## Enhancements +- Changes back to `handleSuperwallEvent` naming with a deprecation notice and a typealias for previous methods + +## Fixes +- Removes extra failure logging when displaying alerts +- Finds nearest activity instead of relying just on Context in `PaywallComposable` +- Improves cleanup in `PaywallComposable` + +# 2.0.0 + +Our 2.0.0 release brings some major and minor changes to both our API's and core features. For more information, please look at our [migration docs](https://superwall.com/docs/migrating-to-v2-android) + +## Enhancements + +- Adds `PaywallBuilder` class as an alternative to existing `getPaywallView` method. This provides a cleaner API and an ability to change purchase loading bar and shimmer view. +- Ensure safety of static webview calls that are known to fail randomly due to Webview's internal issues +- Adds `purchase` method to `Superwall` you can use to purchase products without having to resort on paywalls. To purchase a product you can pass it in one of the following objects: + - Google Play's `ProductDetails` + - Superwall's `StoreProduct` object + - Or a string containing the product identifier, i.e. `Superwall.instance.purchase("product_id:base_plan:offer")` +- Adds `restorePurchases` method to `Superwall` you can use to handle restoring purchases +- Adds `getProducts` method to `Superwall` you can use to retrieve a list of `ProductDetails` given the product ID, i.e. i.e. `Superwall.instance.purchase("product_id:base_plan:offer")` +- Adds support for observing purchases done outside of Superwall paywalls. You can now observe purchases done outside of Superwall paywalls by setting the `shouldObservePurchases` option to true and either: + - Manually by calling `Superwall.instance.observe(PurchasingObserverState)` or utility methods `Superwall.instance.observePurchaseStart/observePurchaseError/observePurchaseResult` + - Automatically by replacing `launchBillingFlow` with `launchBillingFlowWithSuperwall`. This will automatically observe purchases done outside of Superwall paywalls. +- Adds consumer proguard rules to enable consumer minification +- `Superwall.instance` now provides blocking or callback based version of multiple calls, suffixed with `*Sync` +- Improves preloading performance and reduces impact on the main thread +- Reduces minSDK to 22 + + +## Breaking Changes + +- `SuperwallPaywallActivity` and `PaywallView` have been moved into `com.superwall.sdk.paywall.view` package from `com.superwall.sdk.paywall.vc` package. +- Removes `PaywallComposable` and Jetpack Compose support from the main SDK artifact in favor of `Superwall-Compose` module for Jetpack Compose support: + - You can find it at `com.superwall.sdk:superwall-compose:2.0.0-alpha` + - Usage remains the same as before, but now you need to include the `superwall-compose` module in your project. +- Removed methods previously marked as Deprecated +- `SubscriptionStatus.Active` now takes in a set of `Entitlements`, while `Inactive` and `Active` have been turned into objects. +- `Superwall.instance.register` now uses `placement` instead of `event` as the argument name +- `preloadPaywalls` now uses `placementNames` instead of `eventNames` as the argument name +- `PaywallPresentationHandler.onDismiss` now has two arguments, `PaywallInfo` and `PaywallResult` +- `PaywallComposable` now uses `placement` argument instead of `event` +- `TriggerResult.NoRuleMatch` and `TriggerResult.EventNotFound` have been renamed to `TriggerResult.NoAudienceMatch` and `TriggerResult.PlacementNotFound` +- `PresentationResult.NoRuleMatch` and `PresentationResult.EventNotFound` have been renamed to `PresentationResult.NoAudienceMatch` and `PresentationResult.PlacementNotFound` +- `SuperwallEvent` has been renamed to `SuperwallPlacement`, belonging properties with `eventName` have been renamed to `placementName` +- `SuperwallEventInfo` has been renamed to `SuperwallPlacementInfo` +- `ComputedPropertyRequest.eventName` has been renamed to `ComputedPropertyRequest.placementName` +- `Superwall.instance.events` has been renamed to `Superwall.instance.placements` +- `LogScope.events` has been renamed to `LogScope.placements` +- `PaywallPresentationRequestStatusReason.EventNotFound` has been renamed to `PaywallPresentationRequestStatusReason.PlacementNotFound` +- `PaywallSkippedReason.EventNotFound` has been renamed to `PaywallSkippedReason.PlacementNotFound` +- `SuperwallDelegate.handleSuperwallEvent` method has been renamed to `SuperwallDelegate.handleSuperwallPlacement` +- Removed `PurchaseResult.Restored` + +# 2.0.0-beta.5 + +## Breaking changes +- `Superwall.instance.register` now uses `placement` instead of `event` as the argument name +- `preloadPaywalls` now uses `placementNames` instead of `eventNames` as the argument name +- Superwall's `PaywallPresentationHandler.onDismiss` now has two arguments, `PaywallInfo` and `PaywallResult` +- `PaywallComposable` now uses `placement` argument instead of `event` +- Remove `PurchaseResult.Restored` + +# 2.0.0-beta.4 + +## Breaking changes +- `SuperwallEvents.entitlementStatusDidChange` has been renamed to `SuperwallEvents.subscriptionStatusDidChange` +- `SuperwallDelegate.entitlementStatusDidChange` has been renamed to `SuperwallEvents.entitlementStatusDidChange` +- `TriggerResult.NoRuleMatch` and `TriggerResult.EventNotFound` have been renamed to `TriggerResult.NoAudienceMatch` and `TriggerResult.PlacementNotFound` +- `PresentationResult.NoRuleMatch` and `PresentationResult.EventNotFound` have been renamed to `PresentationResult.NoAudienceMatch` and `PresentationResult.PlacementNotFound` + +## 2.0.0-beta.3 + +### Breaking changes + +- `SuperwallEvent` has been renamed to `SuperwallPlacement`, belonging properties with `eventName` have been renamed to `placementName` +- `SuperwallEventInfo` has been renamed to `SuperwallPlacementInfo` +- `ComputedPropertyRequest.eventName` has been renamed to `ComputedPropertyRequest.placementName` +- `Superwall.instance.events` has been renamed to `Superwall.instance.placements` +- `LogScope.events` has been renamed to `LogScope.placements` +- `PaywallPresentationRequestStatusReason.EventNotFound` has been renamed to `PaywallPresentationRequestStatusReason.PlacementNotFound` +- `PaywallSkippedReason.EventNotFound` has been renamed to `PaywallSkippedReason.PlacementNotFound` +- `SuperwallDelegate.handleSuperwallEvent` method has been renamed to `SuperwallDelegate.handleSuperwallPlacement` + +## 2.0.0-beta.2 + +### Breaking Changes + +- API Changes: + - Migration of `setEntitlementStatus` to `setSubscriptionStatus` + - Exposing `Superwall.instance.entitlementsStatus` + - Migration of `SuperwallDelegate.entitlementStatusDidChange` to `SuperwallDelegate.subscriptionStatusDidChange` + +## 2.0.0-beta.1 + +## Enhancements + +- Add `PaywallBuilder` class as an alternative to existing `getPaywallView` method. This provides a cleaner API and an ability to change purchase loading bar and shimmer view. +- Add callback versions of new 2.0 methods +- Ensure safety of static webview calls that are known to fail randomly due to Webview's internal issues + + +## 2.0.0-Alpha.1 + +### Breaking Changes + +- `SuperwallPaywallActivity` and `PaywallView` have been moved into `com.superwall.sdk.paywall.view` package from `com.superwall.sdk.paywall.vc` package. +- Removes `PaywallComposable` and Jetpack Compose support from the main SDK artifact in favor of `Superwall-Compose` module for Jetpack Compose support: + - You can find it at `com.superwall.sdk:superwall-compose:2.0.0-alpha` + - Usage remains the same as before, but now you need to include the `superwall-compose` module in your project. +- Removed methods previously marked as Deprecated +- Removes `SubscriptionStatus`, together with belonging update methods and `subscriptionStatusDidChange` callback. +- These are replaced with `EntitlementStatus` and `entitlementStatusDidChange` callback. You can find more details on this migration in our docs. + +### Enhancements +- Adds `purchase` method to `Superwall` you can use to purchase products without having to resort on paywalls. To purchase a product you can pass it in one of the following objects: + - Google Play's `ProductDetails` + - Superwall's `StoreProduct` object + - Or a string containing the product identifier, i.e. `Superwall.instance.purchase("product_id:base_plan:offer")` + +- Adds `restorePurchases` method to `Superwall` you can use to handle restoring purchases +- Adds `getProducts` method to `Superwall` you can use to retrieve a list of `ProductDetails` given the product ID, i.e. i.e. `Superwall.instance.purchase("product_id:base_plan:offer")` + +- Adds support for observing purchases done outside of Superwall paywalls. You can now observe purchases done outside of Superwall paywalls by setting the `shouldObservePurchases` option to true and either: + - Manually by calling `Superwall.instance.observe(PurchasingObserverState)` or utility methods `Superwall.instance.observePurchaseStart/observePurchaseError/observePurchaseResult` + - Automatically by replacing `launchBillingFlow` with `launchBillingFlowWithSuperwall`. This will automatically observe purchases done outside of Superwall paywalls. +- Adds consumer proguard rules to enable consumer minification +- Reduces minSDK to 22 +- Adds `purchaseToken` to purchase events +- `Superwall.instance` now provides blocking or callback based version of multiple calls, suffixed with `*Sync` +- Improves preloading performance and reduces impact on the main thread + +## 1.5.5 + +## Fixes +- Fixes potential distribution issues for variant selection in edge cases +- Fixes potential memory leaks of paywall calling activity + + +## 1.5.4 + +## Fixes +- Fixes issue when a paywall would dismiss with `ForNextPaywall` but next paywall could not be shown due to triggers or limits. Now it resolves into the proper dismiss status. + +## 1.5.3 + +### Enhancements +- Add `purchaseToken` to TransactionComplete + +## 1.5.2 + +### Fixes +- Fix chromium crashes caused by race conditions in webview's implementation + +## 1.5.1 + +### Enhancements +- Updates superscript dependencies to reduce minSDK version + +### Fixes +- Adds consumer proguard rules to avoid minifying JNA classes during minification + +## 1.5.0 + +### Enhancements + +- Adds `shimmerView_start` and `shimmerView_complete` events to track the loading of the shimmer animation. +- Makes `hasFreeTrial` match iOS SDK behavior by returning `true` for both free trials and non-free introductory offers +- Adds `Superwall.instance.events` - A SharedFlow instance emitting all Superwall events as `SuperwallEventInfo`. This can be used as an alternative to a delegate for listening to events. +- Adds a new shimmer animation +- Adds support for SuperScript expression evaluator + +### Fixes + +- Fixes concurrency issues with subscriptions triggered in Cordova apps + +## 1.5.0-beta.2 + +## Enhancements +- Adds `shimmerView_start` and `shimmerView_complete` events to track the loading of the shimmer animation. +- Makes `hasFreeTrial` match iOS SDK behavior by returning `true` for both free trials and non-free introductory offers + +## 1.5.0-beta.1 + +## Enhancements +- Adds `Superwall.instance.events` - A SharedFlow instance emitting all Superwall events as `SuperwallEventInfo`. This can be used as an alternative to a delegate for listening to events. +- Adds a new shimmer animation +- Adds support for SuperScript expression evaluator + +## 1.4.1 + +### Enhancements +- Adds `appVersionPadded` attribute + +### Fixes +- Fixes issue where `PaywallPresentationHandler.onError` would be skipped in case of `BillingError`s + +## 1.4.0 + +## Enhancements + +- Improves paywall loading and preloading performance +- Reduces impact of preloading on render performance +- Updates methods to return `kotlin.Result` instead of relying on throwing exceptions +- This introduces some minor breaking changes: + - `configure` completion block now provides a `Result` that can be used to check for success or failure + - `handleDeepLink` now returns a `Result` + - `getAssignments` now returns a `Result>` + - `confirmAllAssignments` now returns a `Result>` + - `getPresentationResult` now returns a `Result` + - `getPaywallComponents` now returns a `Result` +- Removes Gson dependency +- Adds `isScrollEnabled` flag to enable remote control of Paywall scrollability +- Adds `PaywallResourceLoadFail` event to enable tracking of failed resources in Paywall +- Improves bottom navigation bar color handling + +## Fixes +- Fixes issue where paywalls without fallback would fail to load and missing resource would cause a failure event +- Fixes issue with `trialPeriodDays` rounding to the higher value instead of lower, i.e. where `P4W2D` would return 28 days instead of 30, it now returns 30. +- Fixes issue with system navigation bar not respecting paywall color +- Fixes issues with cursor allocation in Room transaction +- Improves handling of chromium render process crash + +## 1.4.0-beta.3 + +- Fixes issue where paywalls without fallback would fail to load and missing resource would cause a failure event + +## 1.4.0-beta.2 + +## Enhancements + +- Removes Gson dependency +- Adds `isScrollEnabled` flag to enable remote controll of Paywall scrollability + +## Fixes +- Fixes issue with `trialPeriodDays` rounding to the higher value instead of lower, i.e. where `P4W2D` would return 28 days instead of 30, it now returns 30. +- Fixes issue with system navigation bar not respecting paywall color +- Reduces impact of preloading on render performance +- Fixes issues with cursor allocation in Room transaction + + +## 1.4.0-beta.1 + +- Updates methods to return `kotlin.Result` instead of relying on throwing exceptions +- This introduces some minor breaking changes: + - `configure` completion block now provides a `Result` that can be used to check for success or failure + - `handleDeepLink` now returns a `Result` + - `getAssignments` now returns a `Result>` + - `confirmAllAssignments` now returns a `Result>` + - `getPresentationResult` now returns a `Result` + - `getPaywallComponents` now returns a `Result` + +## 1.3.1 + +### Fixes +- Fixes issue when destroying activities during sleep would cause a background crash +- Fixes issue when using Superwall with some SDK's would cause a crash (i.e. Smartlook SDK) + +## 1.3.0 + +### Enhancements +- The existing `getPaywall` method has been deprecated and renamed to `getPaywallOrThrow`. The new `getPaywall` method now returns a `kotlin.Result` instead of throwing an exception. +- Adds a new option to `SuperwallOptions` - `passIdentifiersToPlayStore` which allows you to pass the user's identifiers (from `Superwall.instance.identify(userId: String, ...)`) to the Play Store when making a purchase. Note: When passing in identifiers to use with the play store, please make sure to follow their [guidelines](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder#setObfuscatedAccountId(java.lang.String). +- Adds `Superwall.instance.confirmAllAssignments()`, which confirms assignments for all placements and returns an array of all confirmed experiment assignments. Note that the assignments may be different when a placement is registered due to changes in user, placement, or device parameters used in audience filters. + +### Fixes + +- Fixes issues with Paywall sometimes not displaying when returning from background +- Fixes issue with SDK crashing when WebView is not available +- Fixes issue with `SuperwallPaywallActivity` NPE +- Update visibility of internal `getPaywall` methods to `internal` to prevent misuse + +## 1.2.9 + +### Fixes + +- Fixes issues with `MODAL` presentation style and scrolling containers +- Fixes issues with `FULLSCREEN` presentation style rendering behind navigation + +## 1.2.8 + +### Fixes +- Fixes issues with Paywall presentation styles not being properly passed + +## 1.2.7 + +### Enhancements +- Exposes current configuration status via `Superwall.instance.configurationStatus` + +### Fixes +- Fixes issues with Paywall previews not loading + +## 1.2.6 + +### Fixes +- Fixes issue where the paywall would not show in some cases when using `minutes_since` +- Fixes issue with wrong URL being logged when a paywall fails to load + +## 1.2.5 + +### Enhancements +- Adds a `Modifier` to `PaywallComposable` to allow for more control +- Adds a `PaywallView.setup(...)` method to allow for easy setup when using `PaywallView` directly +- Adds support for `MODAL` presentation style + +### Fixes +- Fixes issue with displaying `PaywallComposable` +- Resolves issue where users would get `UninitializedPropertyAccessException` when calling `Superwall.instance` + +## 1.2.4 + +### Enhancements +- For users who are not able to upgrade their AGP or Gradle versions, we have added a new artifact `superwall-android-agp-7` which keeps compatibility. + +### Enhancements + +- Fixes issue with decoding custom placements from paywalls. + +## 1.2.3 + +### Enhancements + +- Expose `placementName`, `paywallInfo` and `params` on a `custom_placement` event + +## 1.2.2 + +### Enhancements + +- Adds support for multiple paywall URLs, in case one CDN provider fails. +- `ActivityEncapsulatable` now uses a WeakReference instead of a reference +- SW-2900: Adds Superwall.instance.localeIdentifier as a convenience variable that you can use to dynamically update the locale used for evaluating rules and getting localized paywalls. +- SW-2919: Adds a `custom_placement` event that you can attach to any element in the paywall with a dictionary of parameters. When the element is tapped, the event will be tracked. The name of the placement can be used to trigger a paywall and its params used in audience filters. +- Adds support for bottom sheet presentation style (DRAWER), no animation style and default animation. +- Adds `build_id` and `cache_key` to `PaywallInfo`. +- SW-2917: Tracks a `config_attributes` event after calling `Superwall.configure`, which contains info about the configuration of the SDK. This gets tracked whenever you set the delegate. +- Adds in device attributes tracking after setting the interface style override. +- To comply with new Google Play Billing requirements we now avoid setting empty `offerToken` for one-time purchases + +## 1.2.1 + +### Enhancements + +- Adds the ability for the SDK to refresh the Superwall configuration every session start, subject to a feature flag. +- Tracks a `config_refresh` Superwall event when the configuration is refreshed. +- SW-2890: Adds `capabilities` to device attributes. This is a comma-separated list of capabilities the SDK has that you can target in audience filters. This release adds the `paywall_event_receiver` capability. This indicates that the paywall can receive transaction from the SDK. +- SW-2902: Adds `abandoned_product_id` to a `transaction_abandon` event to use in audience filters. You can use this to show a paywall if a user abandons the transaction for a specific product. + +## 1.2.0 + +### Enhancements +- Adds DSL methods for configuring the SDK. You can now use a configuration block: + ```kotlin + fun Application.configureSuperwall( + apiKey: String, + configure: SuperwallBuilder.() -> Unit, + ) + ``` + +This allows you to configure the SDK in a more idiomatic way: + ```kotlin + configureSuperwall(CONSTANT_API_KEY){ + options { + logging { + level = LogLevel.debug + } + paywalls { + shouldPreload = false + } + } + } + ``` + +### Deprecations + +This release includes multiple deprecations that will be removed in upcoming versions. +Most are internal and will not affect the public API, those that will are marked as such and a simple migration +path is provided. The notable ones in the public API are as follows: + +- Deprecated `DebugViewControllerActivity` in favor of `DebugViewActivity` +- Deprecated `PaywallViewController` in favor of `PaywallView` + - Deprecated belonging methods: + - `viewWillAppear` in favor of `beforeViewCreated` + - `viewDidAppear` in favor of `onViewCreated` + - `viewWillDisappear` in favor of `beforeOnDestroy` + - `viewDidDisappear` in favor of `destroyed` + - `presentAlert` in favor of `showAlert` +- Deprecated `PaywallViewControllerDelegate` in favor of `PaywallViewCallback` + - Deprecated belonging methods: + - `didFinish` in favor of `onFinished` +- Deprecated `PaywallViewControllerEventDelegate` in favor of `PaywallViewEventCallback` + - Users might also note deprecation of `PaywallWebEvent.OpenedUrlInSafari` in favor of `PaywallWebEvent.OpenedUrlInChrome` + - `didFinish` in favor of `onFinished` + +- In `Superwall`, the following methods were deprecated: + - `Superwall.paywallViewController` in favor of `Superwall.paywallView` + - `Superwall.eventDidOccur` argument `paywallViewController` in favor of `paywallView` + - `Superwall.dismiss` in favor of `Superwall.presentPaywallView + - `Superwall.presentPaywallViewController` in favor of `Superwall.presentPaywallView` +- Deprecated `Paywallmanager.getPaywallViewController` in favor of `PaywallManager.getPaywallView` +- Deprecated `DebugManager.viewController` in favor of `DebugManager.view` +- Deprecated `DebugViewController` in favor of `DebugView` +- Deprecated `LogScope.debugViewController` in favor of `LogScope.debugView` +- Deprecated `PaywallPresentationRequestStatus.NoPaywallViewController` in favor of `NoPaywallView` + +## 1.1.9 + +### Deprecations + +- Deprecated configuration method `Superwall.configure(applicationContext: Context, ...)` in favor of `Superwall.configure(applicationContext: Application, ...)` to enforce type safety. The rest of the method signature remains the same. + +### Fixes + +- SW-2878: and it's related leaks. The `PaywallViewController` was not being properly detached when activity was stopped, causing memory leaks. +- SW-2872: Fixes issue where `deviceAttributes` event and fetching would not await for IP geo to complete. +- Fixes issues on tablet devices where the paywall would close after rotation/configuration change. + +## 1.1.8 + +### Enhancements + +- SW-2859: Adds error message to `paywallWebviewLoad_fail`. +- SW-2866: Logs error when trying to purchase a product that has failed to load. +- SW-2869: Add `Reset` event to track when `Superwall.instance.reset` is called. +- SW-2867: Prevents Geo api from being called when app is in the background +- SW-2431: Improves coroutine scope usages & threading limits +- Toolchain and dependency updates + +### Fixes + +- SW-2863: Fixed a `NullPointerException` some users on Android 12 & 13 would experience when calling `configure`. + +## 1.1.7 + +### Enhancements + +- SW-2805: Exposes a `presentation` property on the `PaywallInfo` object. This contains information about the presentation of the paywall. +- SW-2855: Adds `restore_start`, `restore_complete`, and `restore_fail` events. + +### Fixes + +- SW-2854: Fixed issue where abandoning the transaction by pressing back would prevent the user from restarting the transaction. + +## 1.1.6 + +### Enhancements + +- SW-2833: Adds support for dark mode paywall background color. +- Adds ability to target devices based on their IP address location. Use `device.ipRegion`, +`device.ipRegionCode`, `device.ipCountry`, `device.ipCity`, `device.ipContinent`, or `device.ipTimezone`. +- Adds `event_name` to the event params for use with audience filters. + +### Fixes + +- Fixes issue with products whose labels weren't primary/secondary/tertiary. + +## 1.1.5 + +### Fixes + +- Fixes thread safety crash when multiple threads attempted to initialize the `JavaScriptSandbox` + internally. + +## 1.1.3 + +### Enhancements + +- Tracks an `identity_alias` event whenever identify is called to alias Superwall's anonymous ID with a +developer provided id. +- Adds `setInterfaceStyle(interfaceStyle:)` which can be used to override the system interface style. +- Adds `device.interfaceStyleMode` to the device template, which can be `automatic` or `manual` if +overriding the interface style. + +### Fixes + +- Uses `JavascriptSandbox` when available for filter expression evaluation on a background thread +instead of running code on the main thread in a webview. +- Fixes crash where the loading spinner inside the `PaywallViewController` was being updated outside + the main thread. + +## 1.1.2 + +### Enhancements + +- Updates build.gradle configuration which means we can now upload the SDK to maven central. You no +longer need to specify our custom repo in your build.gradle to get our SDK and therefore installation +should be easier. + +### Fixes + +- Fixes `ConcurrentModificationException` crash that sometimes happened when identifying a user. +- Fixes crash on purchasing a free trial when using `getPaywall`. + +## 1.1.1 + +### Fixes + +- Fixes an issue loading products with offers. + +## 1.1.0 + +### Enhancements + +- SW-2768: Adds `device.regionCode` and `device.preferredRegionCode`, which returns the `regionCode` +of the locale. For example, if a locale is `en_GB`, the `regionCode` will be `GB`. You can use this +in the filters of your campaign. +- Adds support for unlimited products in a paywall. +- SW-2785: Adds internal feature flag to disable verbose events like `paywallResponseLoad_start`. + +### Fixes + +- SW-2732: User attributes weren't being sent on app open until identify was called. Now they are +sent every time there's a new session. +- SW-2733: Fixes issue where the spinner would still show on a paywall if a user had previously + purchased on it. +- SW-2744: Fixes issue where using the back button to dismiss a paywall presented via `getPaywall` +would call `didFinish` in the `PaywallViewControllerDelegate` with the incorrect values. +- Fixes issue where an invalid paywall background color would prevent the paywall from opening. If +this happens, it will now default to white. +- SW-2748: Exposes `viewWillAppear`, `viewDidAppear`, `viewWillDisappear` and `viewDidDisappear` +methods of `PaywallViewController` which you must call when using `getPaywall`. +- Stops `Superwall.configure` from being called multiple times. +- `getPresentationResult` now confirms assignments for holdouts. +- Gracefully handles unknown local notification types if new ones are added in the future. +- SW-2761: Fixes issue where "other" responses in paywall surveys weren't showing in the dashboard. + +## 1.0.2 + +### Fixes + +- Prevents a paywall from opening in a separate activity inside a task manager when using `taskAffinity` +within your app. + +## 1.0.1 + +### Fixes + +- Fixes serialization of `feature_gating` in `SuperwallEvents`. +- Changes the product loading so that if preloading is enabled, it makes one API request to get all +products available in paywalls. This results in fewer API requests. Also, it adds retry logic on failure. +If billing isn't available on device, the `onError` handler will be called. + +## 1.0.0 + +### Breaking Changes + +- Changes the import path for the `LogScope`, and `LogLevel`. + +### Fixes + +- Fixes rare thread-safety crash when sending events back to Superwall's servers. +- Calls the `onError` presentation handler block when there's no activity to present a paywall on. +- Fixes issue where the wrong product may be presented to purchase if a free trial had already been +used and you were letting Superwall handle purchases. +- Fixes `IllegalStateException` on Samsung devices when letting Superwall handle purchases. +- Keeps the text zoom of paywalls to 100% rather than respecting the accessibility settings text zoom, +which caused unexpected UI issues. +- Fixes rare `UninitializedPropertyAccessException` crash caused by a threading issue. +- Fixes crash when the user has disabled the Android System WebView. + +## 1.0.0-alpha.45 + +### Fixes + +- Fixes issue where the `paywallProductsLoad_fail` event wasn't correctly being logged. This is a +"soft fail", meaning that even though it gets logged, your paywall will still show. The error message +with the event has been updated to include all product subscription IDs that are failing to be retrieved. + +## 1.0.0-alpha.44 + +### Fixes + +- Fixes rare issue where paywall preloading was preventing paywalls from showing. + +## 1.0.0-alpha.43 + +### Enhancements + +- Adds `handleLog` to the `SuperwallDelegate`. + +## 1.0.0-alpha.42 + +### Fixes + +- Makes sure client apps use our proguard file. + +## 1.0.0-alpha.41 + +### Fixes + +- Removes need for `SCHEDULED_EXACT_ALARM` permission in manifest. + +## 1.0.0-alpha.40 + +### Fixes + +- Fixes issue presenting paywalls to users who had their device language set to Russian, Polish or +Czech. + +## 1.0.0-alpha.39 + +### Fixes + +- Adds missing `presentationSourceType` to `PaywallInfo`. +- Fixes issue where the status bar color was always dark regardless of paywall color. +- Adds `TypeToken` to proguard rules to prevent r8 from 'optimizing' our code and causing a crash. + +## 1.0.0-alpha.38 + +### Enhancements + +- SW-2682: Adds `Superwall.instance.latestPaywallInfo`, which you can use to get the `PaywallInfo` of +the most recently presented view controller. +- SW-2683: Adds `Superwall.instance.isLoggedIn`, which you can use to check if the user is logged in. + +### Fixes + +- Removes use of `USE_EXACT_ALARM` permission that was getting apps rejected. +- Fixes issue with scheduling notifications. The paywall wasn't waiting to schedule notifications + before dismissal so the permissions wasn't always showing. + +## 1.0.0-alpha.37 + +### Enhancements + +- SW-2684: Adds error logging if the `currentActivity` is `null` when trying to present a paywall. + +### Fixes + +- Fixes bug where paywalls might not present on first app install. +- Fixes bug where all `paywallResponseLoad_` events were being counted as `paywallResponseLoad_start`. +- Adds ProGuard rule to prevent `DefaultLifecycleObserver` from being removed. + +## 1.0.0-alpha.36 + +### Enhancements + +- Adds `X-Subscription-Status` header to all requests. +- Caches the last `subscriptionStatus`. +- Adds `subscriptionStatus_didChange` event that is fired whenever the subscription status changes. +- Calls the delegate method `subscriptionStatusDidChange` whenever the subscription status changes. +- SW-2676: Adds a completion block to the `configure` method. + +### Fixes + +- Fixes issue where the main thread was blocked when accessing some properties internally. +- SW-2679: Fixes issue where the `subscription_start` event was being fired even if a non-recurring product was +purchased. + +## 1.0.0-alpha.35 + +### Fixes + +- Fixes issue where `transaction_complete` events were being rejected by the server. + +## 1.0.0-alpha.34 + +### Breaking Changes + +- Changes `Superwall.instance.getUserAttributes()` to `Superwall.instance.userAttributes`. +- `SuperwallOptions.logging.logLevel` is now non-optional. Set it to `LogLevel.none` to prevent +logs from being printed to the console. + +### Enhancements + +- SW-2663: Adds `preloadAllPaywalls()` and `preloadPaywalls(eventNames:)` to be able to manually +preload paywalls. +- SW-2665: Adds `Superwall.instance.userId` so you can access the current user's id. +- SW-2668: Adds `preferredLocale` and `preferredLanguageLocale` to the device attributes for use in rules. +- Adds `Superwall.instance.logLevel` as a convenience variable to set and get the log level. + +### Fixes + +- SW-2664: Fixes race condition between resetting and presenting paywalls. + +## 1.0.0-alpha.33 + +### Fixes + +- Fixes issue where a user would be asked to enable notifications even if there weren't any attached +to the paywall. + +## 1.0.0-alpha.32 + +### Enhancements + +- SW-2214: Adds ability to use free trial notifications with a paywall. +- Adds `cancelAllScheduledNotifications()` to cancel any scheduled free trial notifications. +- SW-2640: Adds `computedPropertyRequests` to `PaywallInfo`. +- SW-2641: Makes `closeReason` in `PaywallInfo` non-optional. + +### Fixes + +- Fixes issue where thrown exceptions weren't always being caught. + +## 1.0.0-alpha.31 + +### Enhancements + +- SW-2638: Adds `Restored` to `PurchaseResult`. +- SW-2644: Adds `RestoreType` to `SuperwallEvent.TransactionRestore`. +- SW-2643: Makes `storePayment` non-optional for a `StoreTransaction`. +- SW-2642: Adds `productIdentifier` to `StorePayment`. + +### Fixes + +- SW-2635: Fixes crash that sometimes occurred if an app was trying to get Superwall's paywall +configuration in the background. + +## 1.0.0-alpha.30 + +### Enhancements + +- SW-2154: The SDK now includes a paywall debugger, meaning you can scan the QR code of any paywall in the +editor to preview it on device. You can change its localization, view product attributes, and view +the paywall with or without a trial. + +### Fixes + +- More bug fixes relating to the loading of products. + +## 1.0.0-alpha.29 + +### Fixes + +- SW-2631: Fixes issue where paywalls weren't showing if the products within them had a base plan or +offer ID set. + +## 1.0.0-alpha.28 + +### Fixes + + - SW-2615: Fixes crash on Android versions < 8 when accessing the Android 8+ only class Period. +- SW-2616: Fixes crash where the `PaywallViewController` was sometimes being added to a new parent view before +being removed from it's existing parent view. + +## 1.0.0-alpha.27 + +### Breaking Changes + +- #SW-2218: Changes the `PurchaseController` purchase function to `purchase(activity:productDetails:basePlanId:offerId:)`. +This adds support for purchasing offers and base plans. + +### Enhancements + +- #SW-2600: Backport device attributes +- Adds support for localized paywalls. +- Paywalls are only preloaded if their associated rules can match. +- Adds status bar to full screen paywalls. + +### Fixes + +- Fixes issue where holdouts were still matching even if the limit set for their corresponding rules were exceeded. +- #SW-2584: Fixes issue where prices with non-terminating decimals were causing products to fail to load. + +## 1.0.0-alpha.26 + +### Fixes +- Additional fixes to make Google billing more robust. +- Fixes an issue that causes `transaction_complete` events to fail. + +## 1.0.0-alpha.25 + +### Fixes + +- Fixes [Google Billing Crash on Samsung devices](https://community.revenuecat.com/sdks-51/how-to-fix-crash-too-many-bind-requests-999-for-service-intent-inappbillingservice-3317). + +## 1.0.0-alpha.24 + +### Fixes + +- Fixes an issue that could cause "n/a" to be displayed on a paywall in place of the proper subscription period string. + +## 1.0.0-alpha.23 + +### Fixes + +- Fixes an issue where calling `identify` right after `configure` would hang b/c network requests need to access the user id to add to headers. +- Fixes a potential crash when loading data from disk + +## 1.0.0-alpha.22 + +### Fixes + +- Fixes threading issue + +## 1.0.0-alpha.21 + +### Fixes + +- Changes Activity to AppCompatActivity + +## 1.0.0-alpha.20 + +### Enhancements + +### Fixes + +- Fixes `app_open` race condition +- Prevents calling purchase twice +- Disables interactivity during purchase + +## 1.0.0-alpha.19 + +### Fixes + +- Fixes `app_launch` event not triggering + +## 1.0.0-alpha.18 + +### Enhancements + +- Adds the ability to provide an `ActivityProvider` when configuring the SDK. This is an interface +containing the function `getCurrentActivity()`, which Superwall will use to present paywalls from. +You would typically conform an `ActivityLifecycleTracker` to this interface. + +### Fixes + +- Fixes a crash when storing a `List` to user attributes and if that List or a Map had a null value. + +## 1.0.0-alpha.17 + +### Enhancements + +- Adds automatic purchase controller +- Improves memory handling for webviews +- Hides the loading indicator on a paywall if transactionBackgroundView is set to NONE + +## 1.0.0-alpha.14 + +### Enhancements + +- Adds `trigger_session_id` to Superwall Events. +- Resets the scroll position of the paywall on close. + +### Fixes + +- Fixes issue where an invalid currency code on a device would crash the app when trying to retrieve products. + +## 1.0.0-alpha.13 + +### Fixes + +- Fixes concurrency issues when setting and retrieving values like the appUserId and seed. + +## 1.0.0-alpha.11 + +### Fixes + +- Can now use both non-recurring products and subscription products in paywalls. +- Fixes a crash issue that was caused by a lazy variable being accessed before it was initialized. diff --git a/content/docs/android/meta.json b/content/docs/android/meta.json index 8633399..e088790 100644 --- a/content/docs/android/meta.json +++ b/content/docs/android/meta.json @@ -4,6 +4,7 @@ "root": true, "pages": [ "index", + "changelog", "---Quickstart---", "quickstart/install", @@ -31,6 +32,7 @@ "sdk-reference/userId", "sdk-reference/subscriptionStatus", "sdk-reference/handleDeepLink", + "sdk-reference/getPresentationResult", "sdk-reference/SuperwallDelegate", "sdk-reference/PurchaseController", "sdk-reference/SuperwallEvent", diff --git a/content/docs/android/sdk-reference/Superwall.mdx b/content/docs/android/sdk-reference/Superwall.mdx index 65c6374..d47cf0b 100644 --- a/content/docs/android/sdk-reference/Superwall.mdx +++ b/content/docs/android/sdk-reference/Superwall.mdx @@ -60,6 +60,14 @@ Superwall.instance.setUserAttributes(mapOf( )) ``` +Reset the user: +```kotlin +Superwall.instance.reset() +``` + + Avoid calling `Superwall.instance.reset()` repeatedly. Resetting rotates the anonymous user ID, clears local paywall assignments, and requires the SDK to re-download configuration state. Only trigger a reset when a user explicitly logs out or you intentionally need to forget their identity. See [User Management](/android/quickstart/user-management) for more guidance. + + Set delegate: ```kotlin Superwall.instance.delegate = this diff --git a/content/docs/android/sdk-reference/getPresentationResult.mdx b/content/docs/android/sdk-reference/getPresentationResult.mdx new file mode 100644 index 0000000..fec57ad --- /dev/null +++ b/content/docs/android/sdk-reference/getPresentationResult.mdx @@ -0,0 +1,82 @@ +--- +title: "getPresentationResult()" +description: "Check the outcome of a placement without presenting a paywall." +--- + +## Purpose +Retrieves the presentation result for a placement without presenting the paywall. Call this when you need to know whether a placement would show a paywall, send the user to a holdout, or fail due to missing configuration before you decide how to render UI. + +## Signature +```kotlin +suspend fun Superwall.getPresentationResult( + placement: String, + params: Map? = null, +): Result +``` + +```kotlin +fun Superwall.getPresentationResultSync( + placement: String, + params: Map? = null, +): Result +``` + + + `getPresentationResultSync` blocks the calling thread. Prefer the suspend function inside a coroutine whenever possible. + + +## Parameters + +| Name | Type | Description | +|------|------|-------------| +| `placement` | `String` | The placement to evaluate. | +| `params` | `Map?` | Optional custom parameters that feed audience filters. Keys starting with `$` are dropped by the SDK. Nested maps or lists are ignored. Defaults to `null`. | + + +## Returns / State +Returns a Kotlin `Result`. + +- On success, the wrapped `PresentationResult` can be: + - `PresentationResult.PlacementNotFound` – Placement is missing from any live campaign. + - `PresentationResult.NoAudienceMatch` – No audience matched, so nothing would show. + - `PresentationResult.Paywall(experiment)` – A paywall would be presented; inspect the `experiment`. + - `PresentationResult.Holdout(experiment)` – The user is in a holdout group for that experiment. + - `PresentationResult.PaywallNotAvailable` – The SDK could not present (no activity, already showing, offline, etc.). +- On failure, the `Result` contains the thrown exception (for example, the SDK is not configured yet). Inspect it with `exceptionOrNull()` or `onFailure`. + +## Usage +```kotlin +lifecycleScope.launch { + val result = Superwall.instance.getPresentationResult( + placement = "premium_feature", + params = mapOf("source" to "settings") + ) + + result + .onSuccess { presentation -> + when (presentation) { + is PresentationResult.Paywall -> { + logExperiment(presentation.experiment) + showLockedState() + } + is PresentationResult.Holdout -> showHoldoutBanner() + is PresentationResult.NoAudienceMatch -> unlockFeature() + is PresentationResult.PlacementNotFound -> Timber.w("Missing placement configuration") + is PresentationResult.PaywallNotAvailable -> showOfflineMessage() + } + } + .onFailure { error -> + Timber.e(error, "Unable to fetch presentation result") + } +} +``` + +```kotlin +// Blocking usage (for example, inside a worker) +val result = Superwall.instance.getPresentationResultSync("premium_feature") +val presentation = result.getOrNull() ?: return +``` + +## Related +- [`register()`](/android/sdk-reference/register) – Registers a placement and may present a paywall. +- [`SuperwallDelegate`](/android/sdk-reference/SuperwallDelegate) – Receive callbacks when paywalls are presented. diff --git a/content/docs/dashboard/dashboard-campaigns/campaigns-placements.mdx b/content/docs/dashboard/dashboard-campaigns/campaigns-placements.mdx index 4b246e8..a2be0a6 100644 --- a/content/docs/dashboard/dashboard-campaigns/campaigns-placements.mdx +++ b/content/docs/dashboard/dashboard-campaigns/campaigns-placements.mdx @@ -21,6 +21,16 @@ Superwall.shared.register(placement: "caffeineLogged") { In short, add placements for everything you want to feature gate, and things you may _want_ to in the future. +### Placement parameters + +Placement parameters let you attach contextual data when registering a placement ([SDK docs](/docs/sdk/quickstart/feature-gating)). That data travels with the placement into the dashboard so you can branch logic or personalize the experience without shipping new app code. + +Once parameters arrive in the dashboard, you can: + +- Reference them in [audience filters](/campaigns-audience#using-user-properties-or-placement-parameters) to decide which users should see a paywall, holdout, or rule group. +- Surface them in the paywall editor as custom variables to drive copy, images, or logic. See [Using Placement Parameters](/docs/using-placement-parameters) for templating examples. +- Pass them along to analytics exports or downstream workflows so your broader stack understands the same context the campaign used. + ### The placements interface Under the placements section, you can: diff --git a/content/docs/dashboard/dashboard-campaigns/campaigns-standard-placements.mdx b/content/docs/dashboard/dashboard-campaigns/campaigns-standard-placements.mdx index 98b2fd8..7a821dd 100644 --- a/content/docs/dashboard/dashboard-campaigns/campaigns-standard-placements.mdx +++ b/content/docs/dashboard/dashboard-campaigns/campaigns-standard-placements.mdx @@ -16,7 +16,7 @@ Standard placements are events that Superwall automatically manages. The followi Visit [Superwall Events](/tracking-analytics) to see a full list of parameters that you can use with these events. Here are a few examples of how they might be used: -#### Using the `paywall_decline` event +## Using the `paywall_decline` event This is registered when a user manually dismisses any paywall. You can combine this with rules to show a paywall when a user closes a specific paywall. First, [add](/campaigns-placements#adding-a-placement) the standard placement to a campaign: @@ -30,7 +30,7 @@ Here, when a user closes the paywall named `PaywallA`, a new paywall will show. Note that you can't reference parameters that you've passed in to your original register call in your rules for `paywall_decline`. -#### Using the `survey_response` event +## Using the `survey_response` event This is registered when a response to a paywall survey has been recorded. First, you need to make sure your paywall [has a survey attached](/surveys). @@ -38,11 +38,20 @@ You can combine this with rules to show a paywall whenever a survey response is For example, if the user selected a survey option named `Too Expensive`, you could present another paywall with a discounted option. This is a great opportunity to show a discounted paywall to improve your conversion rate. -#### Using the `deepLink_open` event +## Using the `deepLink_open` event This is registered when a user opens the app via a deep link. First, you need to make sure to [tell Superwall when a deep link has been opened](/in-app-paywall-previews). -You can use the URL parameters of the deep link within your rules. Just [add](/campaigns-placements#adding-a-placement) the standard placement `deepLink_open` to a campaign. Then, you could set up a filter that fires based off of its parameters along with a `params.path` rule to see if its a certain path you've setup. +You can use the URL parameters of the deep link within your rules. This works for both URL schemes and universal links. After the app has emitted the first `deepLink_open` event for a given URL, these fields become available to audience filters: + +- `params.url` +- `params.path` +- `params.host` +- `params.query` +- `params.pathExtension` +- `params.lastPathComponent` +- `params.fragment` +- All query string parameters (for example, `params.offer` for `?offer=July20`) For example, you could make three conditions to match this deep link: `myapp://paywall?offer=July20`. Here's how: diff --git a/content/docs/dashboard/dashboard-settings/overview-settings-revenue-tracking-google-play.mdx b/content/docs/dashboard/dashboard-settings/overview-settings-revenue-tracking-google-play.mdx index 4d51d1c..1fa87da 100644 --- a/content/docs/dashboard/dashboard-settings/overview-settings-revenue-tracking-google-play.mdx +++ b/content/docs/dashboard/dashboard-settings/overview-settings-revenue-tracking-google-play.mdx @@ -87,7 +87,7 @@ API" button and a green API Enabled check. 2. Select "Monetize with Play" 3. Select "Monetization setup" 4. Under "Google Play Billing", check "Enable real-time notifications" -5. Copy the "Topic Name" from Superwall and paste it into the "Topic name" field - in the Google Play Console. +5. Copy the "Topic Name" from Superwall and paste it into the "Topic name" field in the Google Play Console. + - If you do not see this field, ensure the service account from [Step 3](#3-add-service-account-to-google-play-console) has been correctly added to the Play Console 6. Under "Notification content", select "Subscriptions, voided purchases, and all one-time products" 7. Click "Save" diff --git a/content/docs/dashboard/products.mdx b/content/docs/dashboard/products.mdx index 7896942..185b80a 100644 --- a/content/docs/dashboard/products.mdx +++ b/content/docs/dashboard/products.mdx @@ -43,7 +43,7 @@ localized. most common cause for products not working correctly when testing. -### Entitlements +## Entitlements Entitlements represent the amount of access or features users are entitled to. They can be used to offer different tiers of service, or just represent a single "active" subscription if your app only has one level of service (i.e. "Pro" unlocks everything). All products are granted a default entitlement. @@ -65,16 +65,16 @@ From there, give it a name, and click **Create**: At this point, you can go back to your products and attach one or more entitlements to each one. -#### Editing entitlements +### Editing entitlements To edit or delete an entitlement, click on the trailing pencil icon or the trash can icon to remove one. Note that if your entitlement is associated with any product, you'll need to remove it first before you can delete it. ![](/images/editEntitlement.png) -### Getting product identifiers +## Getting product identifiers _If you use RevenueCat to handle in-app subscriptions, skip to [Using RevenueCat](/products#using-revenuecat)_ -#### Using App Store Connect +### Using App Store Connect On **App Store Connect**, head over to **Your App ▸ App Store ▸ Subscriptions ▸ _Your Subscription Group_**: @@ -102,7 +102,7 @@ To add in-app products, no base plan id is required. For **period**, select **No ![Google Play Products](/images/google-play-in-app-products.png) -**Google Play Offers** +#### Google Play Offers Google play allows you to create multiple base plans and multiple offers for each base plan. When using Superwall, you can either specify a specific offer @@ -116,7 +116,7 @@ will use the following logic to choose the best offer for the user: - Find the longest free trial the customer is eligible for - If there is no free trial, find the cheapest introductory period the customer is eligible for - If there is none, fall back to the base plan -- If you have an offer on one of your products that you never want to automatically be selected by this logic (for example, because it is a discount only used for a specific customer group), you can add the tag `sw-ignore-offer` to that offer. +- If you have an offer on one of your products that you never want to automatically be selected by this logic (for example, because it is a discount only used for a specific customer group), add the tag `sw-ignore-offer` to that offer in Google Play Console. To add the `sw-ignore-offer` tag in **Google Play Console**: @@ -125,7 +125,7 @@ To add the `sw-ignore-offer` tag in **Google Play Console**: Superwall will ignore any offer that includes this tag when selecting the best offer for the user. -That means that if your eligiblitiy criteria is set so that someone can use an +That means that if your eligibility criteria is set so that someone can use an offer only once, we'll respect that and choose from the best remaining offers. **Specifying Offers** @@ -141,7 +141,7 @@ Play Console, and is based on the user's purchase history. ![Multiple Offers](/images/multiple-offers.png) -#### Using RevenueCat +### Using RevenueCat For those who use RevenueCat, Superwall can automatically pre-populate your product identifiers to choose from when adding a product. In the **Add Product** modal, Superwall will display **any product attached to an offering** by following the steps below. @@ -153,25 +153,25 @@ Then, add your [RevenueCat Public API Key](https://docs.revenuecat.com/docs/auth ![](/images/a4ef0c4-Screenshot_2022-11-25_at_17.13.57.png "Screen Shot 2022-05-17 at 2.58.21 PM.png") -### Using products in paywalls +## Using products in paywalls After you've added products to an app, you're ready to start using them in paywalls. Check out our [docs](/paywall-editor-products) for a step-by-step guide on how to do that. -### Understanding how consumable and non-consumable products work +## Understanding how consumable and non-consumable products work Superwall uses entitlements to determine access to features instead of treating purchases as a simple “subscribed/unsubscribed” status. To that end, here is how to work with consumable and non-consumable products: - **Consumable products** (e.g., credits, tokens, energy boosts) generally _aren't_ associated with an entitlement. - **Non-consumable products** (e.g., a lifetime unlock) _should_ be linked to an entitlement in Superwall. This ensures users who purchase them receive the appropriate access to features. Note that on iOS, if you're using consumables you need to include `SKIncludeConsumableInAppPurchaseHistory` (Apple's docs [here](https://developer.apple.com/documentation/bundleresources/information-property-list/skincludeconsumableinapppurchasehistory)) in your `info.plist` file. Ensure its type is set to `Boolean` and its value is `YES`. Note that on pre-iOS 18 devices, StoreKit 1 will be used when this key is present. -### Understanding paid offer types +## Understanding paid offer types Any **paid up front** or **pay as you go** product offer types will also be referenced using the `trial` variables. In Superwall, these are represented as "paid trials". For example, to reference the product's trial price of $3.99 in the image below, you'd use `products.selected.trialPeriodPrice`: ![](/images/paidOfferExample.png) For more on setting customized text using Liquid Templating, visit this [doc](/paywall-editor-liquid). -### A note on StoreKit configuration files +## A note on StoreKit configuration files If you're using a StoreKit Configuration file, pricing information will come from there during local testing. Therefore, it's important to keep your StoreKit Configuration file, Superwall, and the App Store products all in sync. Follow our [Setting up StoreKit testing](/testing-purchases) guide for more information. diff --git a/content/docs/expo/changelog.mdx b/content/docs/expo/changelog.mdx new file mode 100644 index 0000000..6e6d1dd --- /dev/null +++ b/content/docs/expo/changelog.mdx @@ -0,0 +1,500 @@ +--- +title: "Changelog" +description: "Release notes for the Superwall Expo SDK" +--- + +## 1.0.0 + +### Major Changes + +- 197c0c8: Add missing SDK configuration options from native iOS and Android SDKs: + + - `shouldObservePurchases` (iOS & Android): Observe purchases made outside of Superwall + - `shouldBypassAppTransactionCheck` (iOS only): Disables app transaction check on SDK launch + - `maxConfigRetryCount` (iOS only): Number of retry attempts for config fetch (default: 6) + - `useMockReviews` (Android only): Enable mock review functionality + + Also fixes `enableExperimentalDeviceVariables` not being passed to the Android native SDK. + + **Breaking change**: Removed deprecated `collectAdServicesAttribution` option (AdServices attribution is now collected automatically by the native iOS SDK). + +## 0.8.1 + +### Patch Changes + +- ec326e8: bump ios to 4.10.6 + +## 0.8.0 + +### Minor Changes + +- 0fcbf57: hotfix web2app redemption by disabling shouldShowWebPurchaseConfirmationAlert per default + +### Patch Changes + +- 5768d51: expose shouldShowWebPurchaseConfirmationAlert option + +## 0.7.2 + +### Patch Changes + +- 5e2491a: improve event listens to always subscribe no matter what + +## 0.7.1 + +### Patch Changes + +- bab902d: fix compat android serialziaiton + +## 0.7.0 + +### Minor Changes + +- 183a7d2: feat: comprehensive error handling for SDK configuration failures + + Added robust error handling to prevent apps from hanging indefinitely when SDK configuration fails (e.g., during offline scenarios). This introduces three new ways for developers to handle configuration errors: + + **New Features:** + + - Added `configurationError` state to store for programmatic error access + - Added `onConfigurationError` callback prop to `SuperwallProvider` for error tracking/analytics + - Added `SuperwallError` component for declarative error UI rendering + - Listen to native `configFail` events to capture configuration failures + - Improved `SuperwallLoading` and `SuperwallLoaded` to respect error states + + **Breaking Changes:** None - all changes are backward compatible + + **Fixes:** + + - Fixed app hanging in loading state when offline or configuration fails + - Fixed unhandled promise rejections in deep link initialization + - Fixed loading state not resetting on configuration failure + + Developers can now gracefully handle offline scenarios and provide better UX when SDK initialization fails. + +### Patch Changes + +- 4e246c9: fix: resolve Android handleDeepLink promise consistently with iOS + + Fixed Android crash on app launch caused by "Not a superwall link" error. The Android implementation now resolves the handleDeepLink promise with a boolean value (matching iOS behavior) instead of rejecting it for non-Superwall links. This prevents unhandled promise rejections that were causing production app crashes. + + Additionally added error handling in TypeScript as a safety net for any future edge cases. + +- 4e246c9: fix: filter our expo specific deeplinks + +## 0.6.11 + +### Patch Changes + +- ed77ab7: fix: filter our expo specific deeplinks + +## 0.6.10 + +### Patch Changes + +- 2e2fc96: fix: compat products when empty crashing + +## 0.6.9 + +### Patch Changes + +- 7f28aa0: bump kotlin to 2.6.4 +- ec246be: Added integration attributes support for third-party platforms + +## 0.6.8 + +### Patch Changes + +- f70ebe4: Fix undefined being returned for PaywallResult + +## 0.6.7 + +### Patch Changes + +- 02a9d2d: add missing productIdentifier to RedmeptionPaywallInfo + +## 0.6.6 + +### Patch Changes + +- 9c5a9a5: bump ios to 4.10.1 + +## 0.6.5 + +### Patch Changes + +- e4c6aec: add getEntitlements to useUser hook + +## 0.6.4 + +### Patch Changes + +- fedde41: fix: compat superwall options on android + +## 0.6.3 + +### Patch Changes + +- 0278ad4: bump ios to 4.10.0. This fixes missing localization for app2web restore flow + +## 0.6.2 + +### Patch Changes + +- 72519cd: remove unused expo plugin + +## 0.6.1 + +### Patch Changes + +- 7920773: fix(android): handle nullable properties in RedemptionResult JSON serialization + + Fixed a Kotlin compilation error where nullable properties (`variantId`, `experimentId`, `productIdentifier`) were being assigned directly to a `Map<String, Any>`. Now using the null-safe let operator to conditionally add these properties only when they have values. + +## 0.6.0 + +### Minor Changes + +- b816292: # Custom Purchase Controller API Improvement + + Changed `CustomPurchaseControllerContext` return types from `Promise` to `Promise` for cleaner success handling. + + Now you can simply not return anything for success instead of `return undefined`: + + ```tsx + import Purchases, { PURCHASES_ERROR_CODE } from "react-native-purchases"; + + { + try { + const products = await Purchases.getProducts([params.productId]); + const product = products[0]; + + if (!product) { + return { type: "failed", error: "Product not found" }; + } + + await Purchases.purchaseStoreProduct(product); + // Success - no return needed ✨ + } catch (error: any) { + if (error.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) { + return { type: "cancelled" }; + } + return { type: "failed", error: error.message }; + } + }, + + onPurchaseRestore: async () => { + try { + await Purchases.restorePurchases(); + // Success - no return needed ✨ + } catch (error: any) { + return { type: "failed", error: error.message }; + } + }, + }} + > + {/* Your app */} + ; + ``` + +### Patch Changes + +- acb9956: feature: add StoreProduct Type to exports + +## 0.5.1 + +### Patch Changes + +- 889aaf7: fix: improve custom purchase type handling +- 56a72c9: Bump Android version to 2.6.3 +- 465a215: Exposes Product identifier in Redemption Info + +## 0.5.0 + +### Minor Changes + +- 8c2c14f: User identification and attribute operations are now non-blocking async calls, preventing UI freezes while ensuring proper state synchronization + + Thanks to @gursheyss for the PR #90 + +## 0.4.1 + +### Patch Changes + +- 86a3b28: Update Android version to 2.6.1 adding app2web support +- 6df6cc4: Adds paddle store identifiers + +## 0.4.0 + +### Minor Changes + +- 6d3e625: bump ios to fix critical webview bug + +## 0.3.2 + +### Patch Changes + +- 5555e8e: make error handling more defensive + +## 0.3.1 + +### Patch Changes + +- 4a3f540: fix: compat typeissues + +## 0.3.0 + +### Minor Changes + +- 9ed73eb: feat: improve error handling of Custom Purchase Controller + +## 0.2.9 + +### Patch Changes + +- bd460a7: Expose signature in android StoreTransaction +- e9eeff8: Expose appAcounttoken and purchaseToken on Android StoreTransaction + +## 0.2.8 + +### Patch Changes + +- e0b57bc: fix(compat): none nullable access +- 314be3c: bump deps + +## 0.2.7 + +### Patch Changes + +- adccfe4: Update Android SDK to 2.5.4 and iOS to 4.8.2 + +## 0.2.6 + +### Patch Changes + +- 10bb039: force release? + +## 0.2.5 + +### Patch Changes + +- 95636a6: Bump internal android sdk to 2.5.1 + +## 0.2.4 + +### Patch Changes + +- c274e6a: Add typed SuperwallOptions and fix mispelled option name + +## 0.2.3 + +### Patch Changes + +- f9372f1: Exposes StoreTransaction in /compat + +## 0.2.2 + +### Patch Changes + +- 3f832c7: Updates Android SDK to 2.3.2 + +## 0.2.1 + +### Patch Changes + +- 4327c59: expose internal types + +## 0.2.0 + +### Minor Changes + +- 3b58ea4: Updates Android SDK to 2.3.1 (with Google Play Billing library 7) + +## 0.1.3 + +### Patch Changes + +- d273d2a: bump expo module + +## 0.1.2 + +### Patch Changes + +- f243226: fix: type issues + +## 0.1.1 + +### Patch Changes + +- 707e513: temp fix swift types + +## 0.1.0 + +### Minor Changes + +- b39e98e: feat: Remove the export of the internal SuperwallExpoModule Class, + this class should have not been used since it's an internal class and could break the state of the internal SuperwallStore. + If you have used in prior for a usecase that the current SDK doesn't support, please open an issue. + +### Patch Changes + +- 32112a6: feat: handle deeplink automatically, no need for manual handling + +## 0.0.18 + +### Patch Changes + +- 3a93b2b: feat: fix inital loading state + +## 0.0.17 + +### Patch Changes + +- db980b6: fix missing types on native + +## 0.0.16 + +### Patch Changes + +- e19e626: require Expo 53+ + +## 0.0.15 + +### Patch Changes + +- 020c22a: fix: old exports + +## 0.0.14 + +### Patch Changes + +- 6153163: mark things as internal +- 6fbaa94: add types to TransactionProductIdentifier + +## 0.0.13 + +### Patch Changes + +- 2ead245: feat: add getDeviceAttributes +- efbd9d5: feat: add getDeviceAttributes to ios + +## 0.0.12 + +### Patch Changes + +- 4751b75: Fixes issues with identify on Android, updates Android SDK to 2.2.3 + +## 0.0.11 + +### Patch Changes + +- 9c053b3: feat: add experimentalDeviceVariables for ios + +## 0.0.10 + +### Patch Changes + +- d5beb70: fix(compat): subscription event emitter not firing + +## 0.0.9 + +### Patch Changes + +- 67edd16: feat: export internal SuperwallExpoModule for advance usage + +## 0.0.8 + +### Patch Changes + +- 0175478: feat: set subscription status to UNKNOWN on startup +- d8390ab: feat: bump ios SDK version + +## 0.0.7 + +### Patch Changes + +- f5a1d9a: fix: signout state changes +- 4df7557: fix: ios getSubscriptionStatus + +## 0.0.6 + +### Patch Changes + +- fc22062: fix: android getSubscriptionStatus returning undefined + +## 0.0.5 + +### Patch Changes + +- 8f4d758: fix compat subscriptionStatus access failing +- 9d98a30: fix: android sdk version not being passed correctly + +## 0.0.4 + +### Patch Changes + +- eb98aeb: feat: add ability to use CustomPurchaseController + + Just wrap your app with CustomPurchaseControllerProvider and pass your own handler functions to it. + It will await the result of these handler functions to continue the purchase/restore flow. + + ```tsx + { + // Set stuff in ur system here + if (params.platform === "ios") { + console.log("onPurchase", params); + } else { + console.log("onPurchase", params.productId); + } + return; + }, + onPurchaseRestore: async () => { + console.log("onPurchaseRestore"); + // Set stuff in ur system here + return; + }, + }} + > + + + + + + + + + + ``` + +## 0.0.3 + +### Patch Changes + +- 72d9879: fix: adding ability to let superwall manage subscriptions + +## 0.0.2 + +### Patch Changes + +- 8914f05: Initialize new experimental Hook based SDK. + +## 0.0.1 + +### Patch Changes + +- 0cd5243: Inital Release +- 0cd5243: Change Delegate class to normal class from abstract + +## Unpublished + +### 🛠 Breaking changes + +### 🎉 New features + +### 🐛 Bug fixes + +### 💡 Others diff --git a/content/docs/expo/guides/setting-locale.mdx b/content/docs/expo/guides/setting-locale.mdx new file mode 100644 index 0000000..098eb79 --- /dev/null +++ b/content/docs/expo/guides/setting-locale.mdx @@ -0,0 +1,65 @@ +--- +title: "Setting a Locale" +description: Override the default device locale when using the Expo SDK so you can preview localized paywalls and targeting. +--- + +## Overview + +The Expo SDK automatically uses the device's locale to localize paywalls and evaluate campaign rules. Override the locale with the `localeIdentifier` option when you need to: +- preview translations without changing the simulator or device settings +- QA rules that gate content by market or language +- capture store screenshots or marketing assets in a specific language + +`localeIdentifier` accepts standard BCP‑47 identifiers such as `en_US`, `en_GB`, or `fr_CA`. You can reference Apple's [complete locale list](https://gist.github.com/jacobbubu/1836273) if you need the exact identifier for a region. + +## Set the locale before Superwall config + +`` configures the native SDK only once, so be sure the locale you want to test is decided before the provider mounts. + +```tsx +import { SuperwallProvider, type PartialSuperwallOptions } from "expo-superwall"; + +const localeOptions: PartialSuperwallOptions = { + localeIdentifier: "fr_FR", +}; + +export default function App() { + return ( + + + + ); +} +``` + +## Verify the active locale + +Use `useSuperwall()` and `getDeviceAttributes()` to confirm which locale the native SDK currently sees. This is helpful when debugging targeting issues. + +```tsx +import { useEffect } from "react"; +import { useSuperwall } from "expo-superwall"; + +export function LocaleDebug() { + const superwall = useSuperwall(); + + useEffect(() => { + superwall.getDeviceAttributes().then((attrs) => { + console.log("[Superwall] localeIdentifier:", attrs.localeIdentifier); + }); + }, [superwall]); + + return null; +} +``` + +## Related resources + +- [Localization overview](/localization) +- [In-App Paywall Previews](/expo/quickstart/in-app-paywall-previews) diff --git a/content/docs/expo/guides/testing-purchases.mdx b/content/docs/expo/guides/testing-purchases.mdx new file mode 100644 index 0000000..ac09fc1 --- /dev/null +++ b/content/docs/expo/guides/testing-purchases.mdx @@ -0,0 +1,6 @@ +--- +title: "StoreKit testing (iOS only)" +description: "How to set up StoreKit testing for iOS when using the Expo SDK." +--- + +../../../shared/testing-purchases.mdx diff --git a/content/docs/expo/meta.json b/content/docs/expo/meta.json index 8577a5c..604ea32 100644 --- a/content/docs/expo/meta.json +++ b/content/docs/expo/meta.json @@ -4,6 +4,7 @@ "root": true, "pages": [ "index", + "changelog", "---Quickstart---", "quickstart/install", @@ -21,6 +22,7 @@ "---SDK Reference---", "sdk-reference/index", + "sdk-reference/getPresentationResult", "sdk-reference/components", "sdk-reference/hooks", @@ -32,6 +34,8 @@ "guides/migrating-react-native", "guides/managing-users", "guides/experimental-flags", + "guides/testing-purchases", + "guides/setting-locale", "guides/advanced", "guides/migrations", "[Troubleshooting](https://support.superwall.com/articles/4780985851-troubleshooting-expo-sdk)", diff --git a/content/docs/expo/quickstart/in-app-paywall-previews.mdx b/content/docs/expo/quickstart/in-app-paywall-previews.mdx index ae743ab..530bd68 100644 --- a/content/docs/expo/quickstart/in-app-paywall-previews.mdx +++ b/content/docs/expo/quickstart/in-app-paywall-previews.mdx @@ -1,5 +1,5 @@ --- -title: In-App Paywall Previews +title: "Handling Deep Links" --- ../../../shared/in-app-paywall-previews.mdx \ No newline at end of file diff --git a/content/docs/expo/sdk-reference/getPresentationResult.mdx b/content/docs/expo/sdk-reference/getPresentationResult.mdx new file mode 100644 index 0000000..b3a1f78 --- /dev/null +++ b/content/docs/expo/sdk-reference/getPresentationResult.mdx @@ -0,0 +1,111 @@ +--- +title: "getPresentationResult()" +description: "Check the outcome of a placement without presenting a paywall." +--- + +## Purpose +Retrieves the presentation result for a placement without presenting the paywall. Call this when you need to know whether a placement would show a paywall, send the user to a holdout, or fail due to missing configuration before you decide how to render UI. + +## Signature +Hook usage: +```ts +const { getPresentationResult } = useSuperwall() + +await getPresentationResult( + placement: string, + params?: Record +): Promise +``` + +Compat API usage: +```ts +import Superwall from "expo-superwall/compat" + +await Superwall.getPresentationResult({ + placement: string, + params?: Map +}): Promise +``` + +Both variants return a promise that resolves to a `PresentationResult` object from `expo-superwall/compat`. + +## Parameters + +| Name | Type | Description | +|------|------|-------------| +| `placement` | `string` | Placement to evaluate. Always await `Superwall.configure` before calling. | +| `params` | `Record` or `Map` | Optional parameters that feed audience filters. Keys beginning with `$` are reserved and removed. Nested maps or arrays are not supported. Defaults to omitted. | + + +## Returns / State +The promise resolves to one of the `PresentationResult` subclasses exported from `expo-superwall/compat`: +- `PresentationResultPaywall` – A paywall would be shown. Includes an `experiment` field (`id`, `groupId`, etc.). +- `PresentationResultHoldout` – The user is placed in a holdout for the experiment. +- `PresentationResultNoAudienceMatch` – No matching audience rules. +- `PresentationResultPlacementNotFound` – The placement name is not attached to any campaign. +- `PresentationResultUserIsSubscribed` – The SDK determined the user is already active, so no paywall will show. +- `PresentationResultPaywallNotAvailable` – The paywall could not be displayed (no activity, already showing, offline, etc.). + +If configuration fails or the native module throws, the promise rejects—catch and handle these errors as you would any async call. + +## Usage +```tsx +import { + PresentationResultPaywall, + PresentationResultHoldout, + PresentationResultNoAudienceMatch, + PresentationResultPlacementNotFound, +} from "expo-superwall/compat" +import { useSuperwall } from "expo-superwall" + +export function FeatureGate() { + const { getPresentationResult } = useSuperwall() + + const checkAccess = async () => { + const result = await getPresentationResult("premium_feature", { source: "settings" }) + + if (result instanceof PresentationResultPaywall) { + setExperiment(result.experiment) + setState("locked") + } else if (result instanceof PresentationResultHoldout) { + setState("holdout") + } else if (result instanceof PresentationResultNoAudienceMatch) { + unlockFeature() + } else if (result instanceof PresentationResultPlacementNotFound) { + console.warn("Placement missing from dashboard") + } else { + fallbackFlow() + } + } + + return {/* Divider */} diff --git a/src/components/SmartRootToggle.tsx b/src/components/SmartRootToggle.tsx index 56ab38e..4fc619e 100644 --- a/src/components/SmartRootToggle.tsx +++ b/src/components/SmartRootToggle.tsx @@ -1,26 +1,39 @@ 'use client' -import { useState, useMemo } from 'react' +import { useMemo, useState } from 'react' import { usePathname } from 'next/navigation' import Link from 'next/link' -import { ChevronsUpDown } from 'fumadocs-ui/internal/icons' +import { Check, ChevronsUpDown } from 'fumadocs-ui/internal/icons' import { cn } from 'fumadocs-ui/utils/cn' import { isActive } from 'fumadocs-ui/utils/is-active' import { useSidebar } from 'fumadocs-ui/contexts/sidebar' +import { useTreeContext } from 'fumadocs-ui/contexts/tree' +import { getSidebarTabs } from 'fumadocs-ui/utils/get-sidebar-tabs' import { Popover, PopoverContent, PopoverTrigger } from 'fumadocs-ui/components/ui/popover' import { type Option } from 'fumadocs-ui/components/layout/root-toggle' -import { getSmartNavigationUrl, parseDocsPath } from '@/lib/navigation-utils' +import { getSmartNavigationUrl, parseDocsPath, type SDKType } from '@/lib/navigation-utils' interface SmartOption extends Option { smartUrl?: string } interface SmartRootToggleProps { - options: Option[] + options?: Option[] placeholder?: React.ReactNode className?: string } +const GENERAL_ROOT_ORDER = ['dashboard', 'web-checkout', 'integrations', 'support'] as const +const SDK_TARGETS = new Set(['ios', 'android', 'flutter', 'expo', 'react-native', 'dashboard']) + +const isSdkRoot = (key: string): key is SDKType => SDK_TARGETS.has(key as SDKType) + +function getRootKey(url: string | undefined) { + if (!url) return '' + const normalized = url.replace(/^\/docs/, '').replace(/^\/+/, '') + return normalized.split('/')[0] ?? '' +} + /** * Smart Root Toggle that preserves current page path when switching between SDK sections */ @@ -28,34 +41,55 @@ export function SmartRootToggle({ options, placeholder, ...props }: SmartRootTog const [open, setOpen] = useState(false) const { closeOnRedirect } = useSidebar() const pathname = usePathname() + const { full: tree } = useTreeContext() + + const resolvedOptions = useMemo( + () => options ?? getSidebarTabs(tree), + [options, tree] + ) // Find the currently selected root folder option const selected = useMemo(() => { - return options.findLast((item) => + return resolvedOptions.findLast((item) => item.urls ? item.urls.has(pathname.endsWith('/') ? pathname.slice(0, -1) : pathname) : isActive(item.url, pathname, true) ) - }, [options, pathname]) + }, [resolvedOptions, pathname]) - // Generate smart navigation options + // Generate smart navigation options (only for known SDK roots) const smartOptions = useMemo(() => { - return options.map(option => { - // Extract the SDK name from the original URL - // The URLs are like "/ios", "/android", "/react-native", etc. not "/docs/ios" - const sdkMatch = option.url.match(/^\/([\w-]+)/) - if (!sdkMatch) return option - - const targetSdk = sdkMatch[1] as any - const smartUrl = getSmartNavigationUrl(targetSdk, pathname) - - return { - ...option, - smartUrl - } as SmartOption + const { sdk } = parseDocsPath(pathname) + + return resolvedOptions.map((option) => { + const rootKey = getRootKey(option.url) + if (!rootKey || !isSdkRoot(rootKey)) return option + + // Avoid unnecessary swaps when we are outside SDK docs and switching to dashboard + if (!sdk && rootKey === 'dashboard') return option + + const smartUrl = getSmartNavigationUrl(rootKey, pathname) + return { ...option, smartUrl } }) - }, [options, pathname]) - + }, [resolvedOptions, pathname]) + + const groupedOptions = useMemo(() => { + const used = new Set() + + const general = GENERAL_ROOT_ORDER.map((key) => { + const found = smartOptions.find((option) => getRootKey(option.url) === key) + if (found) used.add(found.url) + return found + }).filter(Boolean) as SmartOption[] + + const sdks = smartOptions.filter( + (option) => + !used.has(option.url) && + !GENERAL_ROOT_ORDER.includes(getRootKey(option.url) as typeof GENERAL_ROOT_ORDER[number]) + ) as SmartOption[] + + return { general, sdks } + }, [smartOptions]) const onClick = () => { closeOnRedirect.current = false @@ -64,13 +98,52 @@ export function SmartRootToggle({ options, placeholder, ...props }: SmartRootTog const item = selected ? : placeholder + const renderOption = (option: SmartOption) => { + const isSelected = selected && option.url === selected.url + if (!isSelected && option.unlisted) return null + + return ( + + {option.icon ? ( +
+ {option.icon} +
+ ) : null} +
+

{option.title}

+ {option.description ? ( +

+ {option.description} +

+ ) : null} +
+ + + ) + } + return ( {item ? ( @@ -78,24 +151,20 @@ export function SmartRootToggle({ options, placeholder, ...props }: SmartRootTog ) : null} - - {smartOptions.map((item) => ( - - - - ))} + +
+ {groupedOptions.general.map(renderOption)} + + {groupedOptions.sdks.length > 0 && ( + <> +
+

+ SDKs +

+ {groupedOptions.sdks.map(renderOption)} + + )} +
) @@ -108,14 +177,14 @@ function Item(props: Option) { return ( <> {props.icon && ( -
+
{props.icon}
)}
-

{props.title}

+

{props.title}

{props.description ? ( -

+

{props.description}

) : null}