Skip to content

feat: migrate to Purchasely 6.0 native SDK (6.0.0-rc.1)#57

Open
chouaibMo wants to merge 13 commits into
mainfrom
feat/migration-v6
Open

feat: migrate to Purchasely 6.0 native SDK (6.0.0-rc.1)#57
chouaibMo wants to merge 13 commits into
mainfrom
feat/migration-v6

Conversation

@chouaibMo

Copy link
Copy Markdown
Contributor

Supersedes #56, which GitHub auto-closed when this branch was renamed from release/6.0.0-rc.1 to feat/migration-v6.

Summary

Migrates the Purchasely Cordova plugin to the Purchasely 6.0 native SDKs (iOS Purchasely 6.0.0-rc.2, Android io.purchasely:core 6.0.0-rc.2), mirroring the Flutter migration in Purchasely-Flutter#120. Wrapper version → 6.0.0-rc.1.

This is a migration, not a rewrite — the breaking surfaces are SDK start, presentation display mode, and the action-interceptor result; the rest stays source-compatible (with deprecated aliases for renamed methods).

What changed

JavaScript (purchasely/www/Purchasely.js)

  • start(apiKey, …)start(options, success, error) config object
  • RunningMode values are now name strings (observer/full) — the native iOS/Android enums use different raw values, so the bridge maps by name; default is now observer. paywallObserver kept as a deprecated alias
  • synchronize(success, error) now reports completion (was fire-and-forget)
  • Deeplink: allowDeeplink / handleDeeplink (+ new allowCampaigns); old readyToOpenDeeplink / isDeeplinkHandled kept as deprecated aliases
  • setDefaultPresentationDismissHandler (old …ResultHandler kept as alias)
  • Presentation: isFullscreen bool → display-mode string (bool still normalized); add backPresentation(); removed presentSubscriptions, presentProduct/PlanWithIdentifier, show/hidePresentation (no v6 primitive)
  • Interceptor: onProcessAction(InterceptResult) replaces the bool (legacy bool mapped); PaywallAction kept + PresentationAction alias
  • New constants: InterceptResult, PresentationType, CloseReason, TransitionType, DimensionType, Store, StorekitVersion
  • Unit tests (__tests__/Purchasely.test.js) updated in lockstep — 80 tests pass

iOS (purchasely/src/ios/CDVPurchasely.m, Objective-C)

  • Migrated to the v6 @objc API (verified against the framework's generated Purchasely-Swift.h) — no Swift rewrite/shim needed: apiKey builder → startWithInitialized:; PLYPresentationBuilder.forPlacementId:/forScreenId:buildpreloadWithCompletion: / displayFrom:transitionType:; interceptAction:handler:PLYInterceptResult; setDefaultPresentationDismissHandler:; allowDeeplink:/handleDeeplink:/allowCampaigns:; closeAllScreens; backPresentation
  • PLYPresentationOutcome serialization (result int, closeReason, error, presentation)
  • Fixed v6 type changes: PLYAttribute is now an enum (was used as a pointer); declared PLYEventDelegate/PLYUserAttributeDelegate conformance on the implementing categories; guarded user-attribute callbacks (v6 delegate setters are _Nonnull)

Android (purchasely/src/android/PurchaselyPlugin.kt, Kotlin)

  • Migrated to io.purchasely:core 6.0.0-rc.2: options-dict start; PLYPresentationBase.builder().build().preload()loaded.display(activity, transition){ outcome }; interceptAction(…)PLYInterceptResult; dismiss handler; deeplink renames; synchronize success/error; CoroutineScope for suspend queries
  • Removed PLYProductActivity / PLYSubscriptionsActivity (+ layouts/themes, plugin.xml source-file/AndroidManifest entries) — dropped in native 6.0

CI / E2E

  • ci.yml / publish.yml aligned with Flutter (action bumps, workflow_dispatch, concurrency); prerelease tags publish under the npm next dist-tag, stable under latest
  • New e2e-android.yml / e2e-ios.yml + an Appium + WebdriverIO project (purchasely/example/e2e/) mirroring the Flutter E2E_TEST_INDEX suite: a deterministic WEBVIEW-context bridge suite (hard gate) and a best-effort dismiss suite; workflow_dispatch + nightly + scoped PRs

Docs

  • MIGRATION-v6.md (new), VERSIONS.md row 6.0.0-rc.1 → 6.0.0-rc.2 / 6.0.0-rc.2, READMEs, CLAUDE.md, example app updated to the 6.0 API

Verification

  • iOS example builds against Purchasely 6.0.0-rc.2 (BUILD SUCCEEDED; only a benign setThemeMode deprecation)
  • Android example builds app-debug.apk against io.purchasely:core 6.0.0-rc.2, and runs on an API 34 emulator — SDK initializes (sdkVersion=6.0.0-rc.2), events flow (PRESENTATION_LOADED/VIEWED, PURCHASE_TAPPED), no crash
  • 80/80 JS unit tests pass; version-consistency check passes

Notes for reviewers

  • RC: mark the GitHub release pre-release; npm publish auto-selects the next dist-tag for X.Y.Z-* tags
  • E2E: the deterministic bridge suite hard-gates; device suites are best-effort (dispatch/nightly), same policy as Flutter. To make device suites pass in CI, a placement (default ONBOARDING) must exist for com.purchasely.demo on the example API key's backend — they need one real-runner shakeout
  • Known limitations (documented as TODO(v6-verify)): drawer/popin transition dimensions default (JS sends only a display-mode string); a single interceptor completion is stashed per round-trip

See VERSIONS.md and the native release notes: iOS 6.0.0-rc.2, Android 6.0.0-rc.2.

🤖 Generated with Claude Code

chouaibMo and others added 12 commits July 2, 2026 16:22
- plugin.xml (both): plugin version 6.0.0-rc.1; iOS pod Purchasely 6.0.0-rc.2;
  Android io.purchasely:core + google-play 6.0.0-rc.2
- package.json / package-lock (main + example): 6.0.0-rc.1
- www/Purchasely.js cordovaSdkVersion fallback 6.0.0-rc.1
- VERSIONS.md: add 6.0.0-rc.1 -> 6.0.0-rc.2 / 6.0.0-rc.2 row

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- start(options, success, error): single config object instead of positional args
- RunningMode values are now name strings ('observer'/'full'); paywallObserver kept
  as a deprecated alias -> 'observer' (native iOS/Android enum raw values differ)
- synchronize(success, error) now reports completion
- deeplink renames: allowDeeplink / handleDeeplink (+ new allowCampaigns); old
  readyToOpenDeeplink / isDeeplinkHandled kept as deprecated JS aliases
- setDefaultPresentationDismissHandler (setDefaultPresentationResultHandler alias)
- presentation display mode string replaces isFullscreen bool (bool normalized);
  add backPresentation(); remove presentSubscriptions, presentProduct/PlanWithIdentifier,
  show/hidePresentation (no v6 primitive)
- interceptor: onProcessAction accepts InterceptResult string (legacy bool mapped);
  PaywallAction kept, add PresentationAction alias
- new constants: InterceptResult, PresentationType, CloseReason, TransitionType,
  DimensionType, Store, StorekitVersion
- update __tests__/Purchasely.test.js in lockstep (80 tests pass)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- start(options) config object; RunningMode/Store constants
- allowDeeplink / handleDeeplink; setDefaultPresentationDismissHandler
- interceptor uses PresentationAction + InterceptResult; fix closePaywall bug
- display-mode strings; add backPresentation; wire openDeeplink button
- remove presentSubscriptions / show / hide presentation buttons+handlers

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- ci.yml: actions/checkout@v7, setup-node@v5, setup-java@v5, cache@v5;
  Node 24; add workflow_dispatch + concurrency (cancel-in-progress)
- publish.yml: checkout@v7, setup-node@v5; publish prerelease tags (X.Y.Z-*)
  under npm 'next' dist-tag, stable under 'latest'

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- MIGRATION-v6.md: start(options), RunningMode (strings/observer default),
  synchronize completion, display-mode strings, removed methods, InterceptResult
  interceptor, deeplink renames, dismiss handler rename, new constants, @next install
- README (root + purchasely): 6.0 start(options) + display-mode examples, migration pointer
  (also fixes stale startWithAPIKey in purchasely/README)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- PurchaselyPlugin.kt migrated to io.purchasely:core 6.0.0-rc.2 (mirrors Flutter v6):
  start() parses one options dict; runningMode mapped by name (Observer/Full);
  PLYPresentationBase.builder().build().preload() -> loaded.display(activity, transition){outcome};
  fetchPresentation stashes Loaded for later display; closePresentation -> closeAllScreens();
  add backPresentation(); interceptAction() for all kinds -> single callback + onProcessAction(string);
  setDefaultPresentationDismissHandler; allowDeeplink/handleDeeplink/allowCampaigns;
  synchronize reports success/error; CoroutineScope for suspend queries; isEligibleToOffer rename
- remove PLYProductActivity/PLYSubscriptionsActivity (+ layouts/themes) — dropped in native 6.0;
  drop their plugin.xml source-file + AndroidManifest activity entries and stale example config
- build-extras.gradle: Java 11 / kotlin jvmTarget 11 (core 6.0 ships Java 11 bytecode)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- CDVPurchasely.m migrated to the v6 @objc API (verified against generated
  Purchasely-Swift.h; no Swift shim needed): start() parses one options dict ->
  PurchaselyBuilder chain -> startWithInitialized:; PLYPresentationBuilder
  forScreenId:/forPlacementId: -> build -> preloadWithCompletion: / display;
  displayFrom:transitionType:(PLYDisplayMode); closeAllScreens; backPresentation;
  interceptAction:handler: for all kinds -> single callback + onProcessAction(string
  -> PLYInterceptResult); setDefaultPresentationDismissHandler; allowDeeplink/
  handleDeeplink/allowCampaigns; synchronize success/error; PLYPresentationOutcome
  serialization (result int, closeReason, error, presentation)
- remove presentSubscriptions / presentPlan|ProductWithIdentifier / hide|showPresentation
- fix v6 type changes: PLYAttribute is an enum (was pointer) in setAttribute:;
  declare PLYEventDelegate/PLYUserAttributeDelegate conformance on the categories that
  implement them; guard user-attribute callbacks on attributeCommand (v6 delegate
  setters are _Nonnull, no native unregister)

Verified: iOS example builds against Purchasely 6.0.0-rc.2 (BUILD SUCCEEDED).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- revert build-extras.gradle Java-11 override: cordova-android 15 already compiles at
  Java 17 (>= 11), which consumes io.purchasely:core 6.0.0-rc.2 (Java 11 bytecode); the
  kotlin tasks.withType override failed to resolve in the cordova gradle context
- example devDeps/lock refreshed by platform add (cordova-android 15, cordova-ios 8.1);
  plugin version fields stay 6.0.0-rc.1

Verified: Android example builds app-debug.apk against io.purchasely:core 6.0.0-rc.2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mirrors the Flutter integration_test suite (E2E_TEST_INDEX) adapted to the Cordova
imperative API, driving the real Purchasely 6.0 native SDK.

- purchasely/example/e2e: WebdriverIO project (Appium uiautomator2 + xcuitest drivers)
  - WEBVIEW-context bridge suite (hard gate): getAnonymousUserId, allProducts,
    fetchPresentationForPlacement, synchronize completion, user-attribute round-trip
  - dismiss suite (best-effort): present placement + programmatic close -> closeReason
  - helpers/driver.js (context switch + bridge invoker), runner scripts with 3x retry
    and Flutter-style gating (hard bridge, best-effort dismiss), README
- .github/workflows/e2e-android.yml (emulator-runner, API 34) + e2e-ios.yml (simulator)
  on workflow_dispatch + nightly + scoped PRs; upload ci-logs artifacts
- CLAUDE.md: document e2e workflows + rc npm dist-tag

Note: device suites need the E2E placement to exist for com.purchasely.demo on the
example API key's backend; the deterministic bridge suite is the hard gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the v6 per-action interceptor surface across all three layers, matching
the native SDK (which intercepts per action) and the RN/Flutter contract:

- JS: interceptAction(kind, handler) returning an InterceptResult, plus
  removeActionInterceptor(kind) / removeAllActionInterceptors(). The old
  setPaywallActionInterceptor + onProcessAction are kept as a deprecated
  shim over the new machinery.
- iOS/Android: registerActionInterceptor / unregisterActionInterceptor /
  completeActionInterceptor, keyed by a per-invocation callbackId. This
  fixes the previous single-stashed-completion limitation — concurrent
  intercepts now resolve independently.
- Update example app, MIGRATION-v6.md, and unit tests (85/85 pass).

Cleanup (removes 4023 lines of noise):
- Remove 6 committed .idea/ files and purchasely-google/.DS_Store.
- Revert purchasely/yarn.lock to main's stub (spurious yarn install bloat;
  the project uses package-lock.json).
- Add a root .gitignore so these can't return.

Verified: Android :app:compileDebugKotlin and iOS xcodebuild both succeed
against Purchasely 6.0.0-rc.2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The JS CloseReason enum listed iOS's values {none, button,
interactive_dismiss, programmatic}, but native Android PLYCloseReason
emits back_system (button/back_system/programmatic), which was absent
from the enum — Android apps could not match it, and the two bridges
diverged.

Align with the native wire contract shared with the Flutter plugin's
PLYCloseReason:
- JS enum -> {button, backSystem:'back_system', programmatic}
- iOS resultDictionaryForOutcome: Button->button,
  InteractiveDismiss->back_system (matches the SDK's own convention,
  Purchasely-Swift.h: "interactiveDismiss stringifies to back_system"),
  Programmatic->programmatic, None-> omit key (mirrors Flutter's null
  closeReason on iOS)
- Android unchanged (already emits the canonical values, verified
  against core-6.0.0-rc.2)
- Update unit test (85/85 pass) and MIGRATION-v6.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Flutter PR #120 removed the v5 compatibility aliases outright; mirror
that clean break in the Cordova bridge instead of keeping shims.

Removed from the JS API:
- readyToOpenDeeplink            -> allowDeeplink
- isDeeplinkHandled              -> handleDeeplink
- setDefaultPresentationResultHandler -> setDefaultPresentationDismissHandler
- setPaywallActionInterceptor + onProcessAction -> interceptAction(kind, handler)
- RunningMode.paywallObserver    -> observer
- PaywallAction constant         -> PresentationAction (now the canonical name)

Also drop the legacy boolean interceptor-result normalization (handlers
now return an InterceptResult). Native bridges are unchanged — the aliases
were JS-only and delegated to the retained native actions. Updated the
example app, unit tests (77 pass), and MIGRATION-v6.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown

Greptile Summary

This PR migrates the Cordova plugin to the Purchasely 6.0 native SDKs. The main changes are:

  • Options-based SDK startup in JavaScript and native bridges.
  • New v6 presentation, deeplink, dismiss, and interceptor APIs.
  • Android and iOS native bridge updates for Purchasely 6.0.
  • New E2E workflows and Appium/WebdriverIO example tests.
  • Updated migration docs, package versions, and native dependencies.

Confidence Score: 4/5

The migrated presentation bridge needs fixes before merging.

  • Android exposes a synthetic fetched-presentation id through the public id field.
  • iOS can miss the dismiss callback for fetched presentations if v6 lifecycle handlers must be registered before preload.
  • Android still has a shared callback slot for overlapping presentation calls.

purchasely/src/android/PurchaselyPlugin.kt, purchasely/src/ios/CDVPurchasely.m

Important Files Changed

Filename Overview
purchasely/www/Purchasely.js Updates the public JavaScript API for v6 startup options, display modes, deeplink methods, synchronization callbacks, and per-action interceptors.
purchasely/src/android/PurchaselyPlugin.kt Migrates the Android bridge to v6 SDK calls, coroutine-based queries, loaded presentation display, and per-action interceptor completion.
purchasely/src/ios/CDVPurchasely.m Migrates the iOS bridge to v6 builder, display, dismiss, deeplink, and interceptor APIs.
purchasely/plugin.xml Updates native SDK versions and removes old Android presentation activity resources from plugin installation.
.github/workflows/ci.yml Updates CI triggers, concurrency, Node version, and reusable action versions.
.github/workflows/e2e-android.yml Adds Android Appium/WebdriverIO E2E coverage against an emulator and real backend.
.github/workflows/e2e-ios.yml Adds iOS Appium/WebdriverIO E2E coverage against a simulator and real backend.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
  participant App as Cordova app
  participant JS as Purchasely.js
  participant Native as Native bridge
  participant SDK as Purchasely SDK
  App->>JS: start(options)
  JS->>Native: exec start
  Native->>SDK: build/start v6 SDK
  App->>JS: fetchPresentation(...)
  JS->>Native: exec fetchPresentation
  Native->>SDK: build + preload
  SDK-->>Native: loaded presentation
  Native-->>JS: presentation object
  App->>JS: presentPresentation(presentation, mode)
  JS->>Native: exec presentPresentation
  Native->>SDK: display presentation
  SDK-->>Native: outcome on dismiss/purchase
  Native-->>JS: Cordova callback
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
  participant App as Cordova app
  participant JS as Purchasely.js
  participant Native as Native bridge
  participant SDK as Purchasely SDK
  App->>JS: start(options)
  JS->>Native: exec start
  Native->>SDK: build/start v6 SDK
  App->>JS: fetchPresentation(...)
  JS->>Native: exec fetchPresentation
  Native->>SDK: build + preload
  SDK-->>Native: loaded presentation
  Native-->>JS: presentation object
  App->>JS: presentPresentation(presentation, mode)
  JS->>Native: exec presentPresentation
  Native->>SDK: display presentation
  SDK-->>Native: outcome on dismiss/purchase
  Native-->>JS: Cordova callback
Loading

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
purchasely/src/android/PurchaselyPlugin.kt:570
**Synthetic Presentation Id Escapes**

When Android returns a fetched presentation, this replaces the native presentation id with a local `ply_fetch_...` handle. iOS returns the real id in the same `id` field, so app code that logs, stores, or passes `presentation.id` to another presentation API works on iOS but receives an Android-only handle that cannot identify the presentation.

### Issue 2 of 3
purchasely/src/ios/CDVPurchasely.m:1087
**Late Dismiss Handler Assignment**

For the `fetchPresentation` then `presentPresentation` flow, the dismiss handler is attached only after the presentation has already been built and preloaded. If the v6 presentation object captures lifecycle handlers during the builder/request phase, the paywall can display and dismiss without ever resolving the Cordova success callback.

### Issue 3 of 3
purchasely/src/android/PurchaselyPlugin.kt:594
**Shared Presentation Callback Slot**

Every Android presentation path writes to the same static `purchaseCallback`. If two presentations are started before the first outcome arrives, the later call overwrites the earlier callback, so the first caller never receives its dismiss or purchase result and the outcome can be delivered to the wrong Cordova command.

Reviews (1): Last reviewed commit: "refactor(api)!: remove deprecated v5 ali..." | Re-trigger Greptile

val handle = "ply_fetch_${System.nanoTime()}"
loadedPresentations[handle] = loaded
val map = presentationToMap(loaded).toMutableMap()
map["id"] = handle

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Synthetic Presentation Id Escapes

When Android returns a fetched presentation, this replaces the native presentation id with a local ply_fetch_... handle. iOS returns the real id in the same id field, so app code that logs, stores, or passes presentation.id to another presentation API works on iOS but receives an Android-only handle that cannot identify the presentation.

Context Used: CLAUDE.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: purchasely/src/android/PurchaselyPlugin.kt
Line: 570

Comment:
**Synthetic Presentation Id Escapes**

When Android returns a fetched presentation, this replaces the native presentation id with a local `ply_fetch_...` handle. iOS returns the real id in the same `id` field, so app code that logs, stores, or passes `presentation.id` to another presentation API works on iOS but receives an Android-only handle that cannot identify the presentation.

**Context Used:** CLAUDE.md ([source](https://app.greptile.com/purchasely/github/purchasely/purchasely-cordova/-/custom-context?memory=5b6f5482-f899-484f-8104-ac25efc0799e))

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

presentationLoaded.controller.modalPresentationStyle = UIModalPresentationFullScreen;
}
// Deliver the dismiss outcome to this command through onDismissed.
presentationLoaded.onDismissed = ^(PLYPresentationOutcome * _Nonnull outcome) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Late Dismiss Handler Assignment

For the fetchPresentation then presentPresentation flow, the dismiss handler is attached only after the presentation has already been built and preloaded. If the v6 presentation object captures lifecycle handlers during the builder/request phase, the paywall can display and dismiss without ever resolving the Cordova success callback.

Context Used: CLAUDE.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: purchasely/src/ios/CDVPurchasely.m
Line: 1087

Comment:
**Late Dismiss Handler Assignment**

For the `fetchPresentation` then `presentPresentation` flow, the dismiss handler is attached only after the presentation has already been built and preloaded. If the v6 presentation object captures lifecycle handlers during the builder/request phase, the paywall can display and dismiss without ever resolving the Cordova success callback.

**Context Used:** CLAUDE.md ([source](https://app.greptile.com/purchasely/github/purchasely/purchasely-cordova/-/custom-context?memory=5b6f5482-f899-484f-8104-ac25efc0799e))

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

return
}

purchaseCallback = callbackContext

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Shared Presentation Callback Slot

Every Android presentation path writes to the same static purchaseCallback. If two presentations are started before the first outcome arrives, the later call overwrites the earlier callback, so the first caller never receives its dismiss or purchase result and the outcome can be delivered to the wrong Cordova command.

Context Used: CLAUDE.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: purchasely/src/android/PurchaselyPlugin.kt
Line: 594

Comment:
**Shared Presentation Callback Slot**

Every Android presentation path writes to the same static `purchaseCallback`. If two presentations are started before the first outcome arrives, the later call overwrites the earlier callback, so the first caller never receives its dismiss or purchase result and the outcome can be delivered to the wrong Cordova command.

**Context Used:** CLAUDE.md ([source](https://app.greptile.com/purchasely/github/purchasely/purchasely-cordova/-/custom-context?memory=5b6f5482-f899-484f-8104-ac25efc0799e))

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

…backs, purchaseResult

Extend the Cordova presentation API to match Flutter PR #120 (verified against
head fdd5adbe). All additive / backward-compatible.

- Transition object on present*/displayMode: { type, dismissible, width, height:
  {type,value}, backgroundColor } — a mode string and legacy boolean still work.
  Native builds PLYDisplayMode (iOS) / PLYTransition (Android). iOS ObjC exposes
  percentage height + dismissible + colors only (pixel sizing and popin width are
  Swift-only in the SDK); Android honors the full set.
- Default (audience-targeted) presentation: presentPresentationForDefault() and
  fetchPresentationForDefault() (native default builder — no placement/screen id).
- Per-request lifecycle: present* stream onPresented / onCloseRequested via an
  optional final callbacks arg; the dismiss outcome stays the final success
  callback (backward-compatible dispatcher). iOS covers direct + re-display;
  Android covers the direct present* methods.
- removeDefaultPresentationDismissHandler().
- Dismiss outcome adds purchaseResult ('purchased'|'cancelled'|'restored')
  alongside the legacy result int, matching the Flutter outcome contract.

Updates JS + iOS + Android bridges, unit tests (82 pass), MIGRATION-v6.md, and
the example app (exercises transitions, lifecycle callbacks, purchaseResult).
Both platforms build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant