docs: improve structure and support multi-langs#47
Conversation
WalkthroughDocumentation infrastructure expanded with multi-language support through new LanguageTabs component; Dart language added to CodeBlock; documentation reorganized into features/* and updates/* hierarchies; scroll behavior changed from smooth to instant across components; new comprehensive docs pages created for subscription, purchase flow, and offer code redemption. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
🧰 Additional context used🧬 Code graph analysis (3)packages/docs/src/pages/docs/features/subscription.tsx (2)
packages/docs/src/pages/docs/features/offer-code-redemption.tsx (1)
packages/docs/src/pages/docs.tsx (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
🔇 Additional comments (13)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (6)
packages/docs/src/styles/dark-mode.css (1)
149-166: Dark-mode alert variants are well integratedThe new
.alert-card--info|warning|successrules fit the existing dark palette and keep text legible; if you add more variants later, consider factoring shared alert-card styles into a common class to avoid duplication.packages/docs/src/components/PlatformTabs.tsx (1)
1-33: Hash‑driven tab selection is useful; consider guardingwindowfor SSR/testsThe hash‑based default and
hashchangelistener give nice deep‑link behavior for iOS/Android sections. If this component is ever rendered in SSR or non‑DOM test environments, thewindow.location.hashusage in theuseStateinitializer could throw—consider guarding withtypeof window !== 'undefined'or moving initial hash inspection intouseEffectwith a safe default.packages/docs/src/components/LanguageTabs.tsx (1)
21-45: InitializeactiveTabfrom available languages, not hardcoded'typescript'Right now
activeTabalways starts as'typescript', even if the caller only provides (say) Swift/Kotlin/Dart. That would render an empty content area until the user clicks a tab.You can make the component more robust by deriving the initial tab from
children:-function LanguageTabs({ children }: LanguageTabsProps) { - const [activeTab, setActiveTab] = useState<Language>('typescript'); - - const availableLanguages = (Object.keys(children) as Language[]).filter( - (lang) => children[lang] !== undefined - ); +function LanguageTabs({ children }: LanguageTabsProps) { + const availableLanguages = (Object.keys(children) as Language[]).filter( + (lang) => children[lang] !== undefined + ); + + const [activeTab, setActiveTab] = useState<Language>( + availableLanguages[0] ?? 'typescript' + );This keeps existing behavior when TS is present, but degrades gracefully for other combinations.
packages/docs/src/components/CodeBlock.tsx (1)
3-13: AlignhighlightCodelanguage typing withCodeBlockProps.languageDart support and the shared Swift/Kotlin/Dart highlighting branch look good and follow the existing escaping pattern. One small type-safety improvement:
highlightCodecurrently takeslanguage: string, so adding a new language toCodeBlockPropswon’t surface a missing branch here at compile time.Consider tightening this to the same union:
-interface CodeBlockProps { +interface CodeBlockProps { children: string; - language?: - | 'graphql' - | 'typescript' - | 'javascript' - | 'swift' - | 'kotlin' - | 'dart' - | 'xml'; + language?: CodeLanguage; } + +type CodeLanguage = + | 'graphql' + | 'typescript' + | 'javascript' + | 'swift' + | 'kotlin' + | 'dart' + | 'xml'; ... -function highlightCode(element: HTMLElement, language: string) { +function highlightCode(element: HTMLElement, language: CodeLanguage) {This keeps implementation the same but gives you better compiler help if you add more languages later.
Also applies to: 35-51, 83-262
packages/docs/src/pages/docs/features/purchase.tsx (1)
580-584: Consider clarifying that the consumable check is illustrative.The
purchase.productId.includes('consumable')pattern is a simple example, but in production, developers should maintain a proper product configuration mapping rather than relying on naming conventions.Consider adding a brief comment or note that this is simplified for demonstration purposes.
packages/docs/src/pages/docs/lifecycle/subscription.tsx (1)
29-132: Consider extracting repeated inline styles to CSS classes.The inline styles are repeated across multiple table rows and cells. While this works, extracting to CSS classes (e.g., using the existing
doc-tableclass pattern seen in other files) would improve maintainability.This is a low-priority suggestion since the current approach is functional.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (26)
packages/docs/src/components/AnchorLink.tsx(2 hunks)packages/docs/src/components/CodeBlock.tsx(4 hunks)packages/docs/src/components/LanguageTabs.tsx(1 hunks)packages/docs/src/components/PlatformTabs.tsx(2 hunks)packages/docs/src/components/SearchModal.tsx(1 hunks)packages/docs/src/hooks/useScrollToHash.ts(2 hunks)packages/docs/src/pages/docs.tsx(8 hunks)packages/docs/src/pages/docs/apis.tsx(43 hunks)packages/docs/src/pages/docs/errors.tsx(5 hunks)packages/docs/src/pages/docs/events.tsx(7 hunks)packages/docs/src/pages/docs/features.tsx(0 hunks)packages/docs/src/pages/docs/features/external-purchase.tsx(15 hunks)packages/docs/src/pages/docs/features/offer-code-redemption.tsx(1 hunks)packages/docs/src/pages/docs/features/purchase.tsx(1 hunks)packages/docs/src/pages/docs/features/subscription-offers.tsx(1 hunks)packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx(18 hunks)packages/docs/src/pages/docs/lifecycle/index.tsx(2 hunks)packages/docs/src/pages/docs/lifecycle/subscription.tsx(1 hunks)packages/docs/src/pages/docs/updates/announcements.tsx(1 hunks)packages/docs/src/pages/docs/updates/notes.tsx(3 hunks)packages/docs/src/pages/docs/updates/versions.tsx(1 hunks)packages/docs/src/pages/home.tsx(1 hunks)packages/docs/src/pages/tutorials.tsx(5 hunks)packages/docs/src/styles/base.css(0 hunks)packages/docs/src/styles/components.css(2 hunks)packages/docs/src/styles/dark-mode.css(1 hunks)
💤 Files with no reviewable changes (2)
- packages/docs/src/styles/base.css
- packages/docs/src/pages/docs/features.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-18T05:46:51.596Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
Applied to files:
packages/docs/src/pages/docs/features/subscription-offers.tsxpackages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/pages/docs/apis.tsx
📚 Learning: 2025-11-22T19:30:03.876Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 46
File: packages/docs/src/pages/docs/types.tsx:2210-2238
Timestamp: 2025-11-22T19:30:03.876Z
Learning: In the OpenIAP SDK, the IAPKit verification endpoint is handled internally using a default base URL. Users no longer need to provide an `endpoint` field in `RequestVerifyPurchaseWithIapkitProps` - the SDK automatically routes to the correct IAPKit `/purchase/verify` endpoint.
Applied to files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/pages/docs/apis.tsx
🧬 Code graph analysis (6)
packages/docs/src/pages/docs/lifecycle/subscription.tsx (2)
packages/gql/src/generated/types.ts (1)
Subscription(782-794)packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)
packages/docs/src/pages/docs/features/offer-code-redemption.tsx (1)
packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)
packages/docs/src/pages/docs/features/subscription-offers.tsx (1)
packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)
packages/docs/src/pages/docs/features/purchase.tsx (2)
packages/gql/src/generated/types.ts (1)
Purchase(429-429)packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)
packages/docs/src/pages/docs/apis.tsx (2)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
code(1152-1174)packages/gql/src/generated/Types.kt (1)
code(1214-1236)
packages/docs/src/pages/tutorials.tsx (1)
packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test Android
🔇 Additional comments (45)
packages/docs/src/components/SearchModal.tsx (1)
372-380: External Purchase doc path update looks consistentThe new
/docs/features/external-purchasepath aligns with the features namespace; as long as the route exists, this keeps search results in sync with the new docs structure.packages/docs/src/pages/home.tsx (1)
19-35: Versions badge now targets updates/versions correctlyUpdating the link to
/docs/updates/versionskeeps the home hero in sync with the new updates namespace; no issues from a routing perspective.packages/docs/src/pages/docs/updates/announcements.tsx (1)
1-4: useScrollToHash import depth matches file locationThe updated relative path (
../../../hooks/useScrollToHash) correctly reflects this file’s nesting underpages/docs/updates; hook usage remains unchanged and looks good.packages/docs/src/pages/docs/updates/notes.tsx (1)
1-4: Notes page wiring and external purchase link look correctThe deeper import paths for
useScrollToHashandCodeBlock, theNotescomponent rename/export, and the updated External Purchase link (/docs/features/external-purchase) all align with the new docs layout and hash-scrolling behavior.Also applies to: 56-60, 393-393
packages/docs/src/pages/docs/updates/versions.tsx (1)
1-3: Import path adjustments are consistent with new directory depthPointing
AnchorLink,useScrollToHash, andGQL_RELEASEto../../../...correctly matches the file’s nesting underpages/docs/updates; runtime behavior is unchanged.packages/docs/src/pages/docs/lifecycle/index.tsx (1)
2-4: Lifecycle imports and new Subscription cross‑link are alignedThe deeper imports for
CodeBlock,AnchorLink, anduseScrollToHashmatch the file’s location, and the added link to/docs/lifecycle/subscriptionis a good navigational cue for platform‑specific subscription details.Also applies to: 215-218
packages/docs/src/styles/components.css (1)
231-339: LanguageTabs and alert-card styles look consistentThe new
.language-tabs*and.alert-card*styles align well with the existing PlatformTabs and info-note patterns. Class naming and layout choices are clear and cohesive; no functional or maintainability concerns from the CSS side.packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx (2)
262-621: Multi-language upgrade/downgrade docs read well and are consistentThe new LanguageTabs-based structure for the iOS and Android upgrade/downgrade sections is clear and consistent across TypeScript, Swift, Kotlin, and Dart. The narrative around
pendingUpgradeProductId,autoRenewPreference, and Android replacement modes is coherent, and the per-language samples line up with each other conceptually.No structural or React-level issues spotted with the way LanguageTabs/PlatformTabs are wired here.
Also applies to: 661-875, 1045-1548
818-835: Unable to verify—file not found in repository; technical concerns are valid but version-dependentI conducted extensive searches across the repository and cannot locate the file
packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx. Without access to the actual code at the specified line ranges (818–835, 1089–1112, 1225–1248, 1491–1545), I cannot confirm the specific issues described.However, the underlying Dart null-safety concerns are technically valid:
firstOrNullavailability: Requires either Dart 3.0+ (built-in viadart:core) orpackage:collectionfor earlier versions. If the documentation does not specify the minimum Dart version or note the dependency, this ambiguity should be addressed.firstWherewithoutorElse: Will throw if no match is found; a subsequent null-check provides no protection.firstWhere(..., orElse: () => null)with non-nullable element types: Invalid under strict null-safety; must use nullable collection types orfirstWhereOrNull.The review's recommendation—to either document the required Dart version / dependencies or rewrite using plain Dart constructs—is sound and would improve portability.
To proceed:
- Confirm the file path or verify the code exists on the PR branch
- Check the target Dart version in the repository's analysis options or pubspec.yaml
- Verify whether
package:collectionis declared as a dependencypackages/docs/src/pages/docs/errors.tsx (2)
21-52: PurchaseError multi-language definitions are alignedThe new LanguageTabs block for
PurchaseErrorkeeps the structure consistent across TypeScript, Swift, Kotlin, and Dart (code/message/productId). The per-language declarations are idiomatic and match the textual description above.
501-656: ErrorCode / OpenIapError / IAPError enums look consistent across languagesThe unified error-code section now presents TypeScript’s string enum alongside Swift/Kotlin/Dart enums with matching conceptual values. Naming follows each language’s conventions while preserving the same set of cases, which should help readers map errors between platforms.
Additionally, please double-check that the updated link to the updates page (
/docs/updates/notesat line 732) matches an existing route in the docs app to avoid broken navigation.packages/docs/src/pages/tutorials.tsx (1)
1-6: AnchorLink +useScrollToHashintegration looks solidUsing
AnchorLinkfor the section headings and invokinguseScrollToHash()brings the Tutorials page in line with the rest of the docs’ hash-based navigation. IDs are clear (setups,news,getting-started,verify-purchase), and this should play nicely with the updated scroll behavior logic.Also applies to: 23-26, 39-42, 115-118, 141-144
packages/docs/src/components/AnchorLink.tsx (1)
17-33: I'll verify the critical issue about thescrollIntoViewbehavior type. Let me start by examining the file and checking TypeScript type definitions.
<function_calls>
<invoke_name>shell</invoke_name>
#!/bin/bashLocate and examine the AnchorLink.tsx file
find . -name "AnchorLink.tsx" -type f | head -5
</invoke_name>
</function_calls>
<function_calls>
<invoke_name>web_search
ScrollBehavior 'instant' TypeScript DOM types valid
</invoke_name>
</function_calls>Wait for the results to verify the type correctness of the
'instant'behavior value.packages/docs/src/hooks/useScrollToHash.ts (1)
9-22: I'll verify the scroll behavior issue in the useScrollToHash.ts file. Let me start by examining the file and checking for type-checking errors related to the'instant'behavior value.
<function_calls>
#!/bin/bashLocate and examine the useScrollToHash.ts file
find . -name "useScrollToHash.ts" -type f 2>/dev/null
</function_calls>
<function_calls>
#!/bin/bashRead the file to verify the content
cat -n packages/docs/src/hooks/useScrollToHash.ts
</function_calls>
<function_calls>
DOM ScrollBehavior type definition allowed values 'auto' 'smooth' 'instant'
</function_calls>packages/docs/src/pages/docs/features/purchase.tsx (4)
1-11: LGTM - Clean component setup with appropriate imports.The component follows the established documentation page pattern with proper imports for navigation hooks and UI components.
46-58: Excellent warning placement for critical IAP requirements.The alert card effectively communicates the consequences of incomplete transactions on both platforms. This is essential information that developers often miss.
694-700: Well-structured complete example section.The comprehensive examples demonstrate production-ready patterns for each platform, including proper state management (React Context, ObservableObject, StateFlow, ChangeNotifier).
1068-1114: Excellent troubleshooting reference.The table format clearly maps issues to causes and solutions. This will save developers significant debugging time.
packages/docs/src/pages/docs/features/offer-code-redemption.tsx (3)
1-9: Clean component setup with appropriate imports.Uses the established pattern with PlatformTabs for platform-specific content and LanguageTabs for multi-language code samples.
57-149: Well-organized platform-specific documentation.The nested structure of
PlatformTabs > LanguageTabseffectively handles the complexity of platform-specific features with multi-language support. The "iOS-only" placeholders in Kotlin tabs correctly indicate platform restrictions.
291-390: Android implementation correctly documents the deep link approach.Since Google Play lacks a native in-app redemption API, the documentation correctly shows the URL-based approach using platform-appropriate methods (Linking, Intent, url_launcher).
packages/docs/src/pages/docs/features/subscription-offers.tsx (4)
1-8: Clean component setup following established patterns.
31-68: Clear platform comparison with actionable guidance.The table effectively communicates the key difference: iOS auto-applies offers while Android requires explicit offer tokens. The tip about fetching products first prevents a common mistake.
450-460: Excellent developer experience - showing the exact error message.Including the actual error message developers will encounter helps them quickly identify and resolve this common Android subscription issue.
744-808: Comprehensive offer selection logic.The
selectOfferfunction demonstrates proper filtering by offer type, which is essential for Android subscription purchases. The pattern is reusable across different use cases.packages/docs/src/pages/docs/lifecycle/subscription.tsx (4)
1-8: Clean component setup with appropriate imports.Uses Accordion for collapsible content sections which improves readability for detailed documentation.
63-131: Accurate platform data availability comparison.The table correctly shows that iOS provides rich client-side subscription data via StoreKit 2, while Android requires server-side API calls for most subscription details. This is crucial information for developers planning their architecture.
255-326: Clear lifecycle flow documentation.The pseudo-code style flow diagrams effectively communicate the step-by-step process for handling subscriptions at different lifecycle points. The platform-specific tabs highlight important differences.
708-740: Excellent edge case documentation.The refund scenario clearly demonstrates why server-side validation is essential. The before/after comparison effectively communicates the risk of relying solely on client-side data.
packages/docs/src/pages/docs.tsx (3)
6-21: Clean import organization for new documentation pages.The new imports are organized logically:
- Line 6: Subscription under lifecycle
- Lines 11-15: Feature pages
- Lines 19-21: Updates pages
All paths match the new file structure.
80-87: Good use of MenuDropdown for expandable navigation.The Life Cycle section now uses MenuDropdown to accommodate the new Subscription sub-page, allowing for future expansion of the lifecycle documentation.
224-256: Route definitions correctly match navigation structure.All new routes are properly defined:
lifecycle/subscriptionfor the new subscription lifecycle pagefeatures/*for all feature documentation pagesupdates/*for announcements, notes, and versionsThe routing structure is clean and follows React Router patterns.
packages/docs/src/pages/docs/features/external-purchase.tsx (3)
1-6: LGTM! Clean import organization.The updated imports properly include the new
LanguageTabscomponent alongside existing dependencies. The relative path structure is consistent with the file's location within the features subdirectory.
100-269: Well-structured multi-language presentation for iOS external purchase flow.The LanguageTabs implementation correctly presents equivalent code samples across TypeScript, Swift, Kotlin (for KMP iOS targets), and Dart. The code logic is consistent across all languages, and the Kotlin sample appropriately notes it's for iOS targets in Kotlin Multiplatform.
563-749: Correct omission of Swift tab for Android-only APIs.The Android Alternative Billing section appropriately includes only TypeScript, Kotlin, and Dart tabs since Swift is not applicable for Android-specific billing APIs.
packages/docs/src/pages/docs/events.tsx (4)
23-56: Correct platform-specific event type definitions.The enum definitions appropriately reflect platform capabilities:
- Swift omits
userChoiceBillingAndroid(Android-only)- Kotlin omits
promotedProductIOS(iOS-only)- TypeScript and Dart include all events for cross-platform awareness
94-183: Comprehensive listener examples across all languages.The purchase updated listener examples demonstrate proper patterns for each platform:
- TypeScript: subscription-based cleanup
- Swift: AsyncSequence and Combine approaches
- Kotlin: Flow and callback approaches
- Dart: Stream subscription with cancel
399-509: Good platform availability documentation for Promoted Product event.The Kotlin tab correctly indicates "iOS only - not available on Android" and the Dart example includes a comment noting it will only fire on iOS. This prevents developer confusion about platform-specific events.
546-650: Correct handling of Android-only User Choice Billing event.The Swift tab appropriately shows "Android only - not available on iOS" and the usage examples correctly omit Swift implementation. The TypeScript, Kotlin, and Dart examples are consistent in structure and demonstrate the proper token reporting workflow.
packages/docs/src/pages/docs/apis.tsx (6)
4-6: LGTM! Proper import addition.The
LanguageTabsimport is correctly added alongside existing component imports.
65-176: Clear multi-language API documentation for initConnection.The function signatures and usage examples correctly show platform-specific differences:
- TypeScript uses string literals for mode selection
- Swift notes alternative billing is Android-only
- Kotlin uses enum values with proper naming conventions
- Dart follows Dart enum conventions
443-523: Comprehensive iOS External Purchase Links documentation.The 3-step external purchase flow is well documented across all languages with clear step-by-step comments. The Kotlin sample correctly notes that external purchase is iOS-only and suggests using alternative billing APIs for Android targets.
926-947: Good use of styled alert card for new feature highlight.The
.alert-card--successstyling effectively draws attention to the new iOS renewal status information feature.
1509-1558: Well-documented new iOS external purchase APIs.The three new APIs (
canPresentExternalPurchaseNoticeIOS,presentExternalPurchaseNoticeSheetIOS,presentExternalPurchaseLinkIOS) are properly documented with:
- Clear function signatures
- Return type definitions
- iOS version requirements
- Links to related type documentation
1701-1791: Complete Alternative Billing flow example across platforms.The 3-step flow example is consistent across TypeScript, Kotlin, and Dart, correctly demonstrating:
- Availability check
- User dialog presentation
- Token creation after payment
Swift is appropriately omitted since this is Android-specific functionality.
Summary by CodeRabbit
New Features
Documentation
Style
✏️ Tip: You can customize this high-level summary in your review settings.