Skip to content

Move billing fields from settings.json to store.json (TinybaseValues)#3458

Closed
devin-ai-integration[bot] wants to merge 1 commit intomainfrom
devin/1769665716-move-billing-to-store
Closed

Move billing fields from settings.json to store.json (TinybaseValues)#3458
devin-ai-integration[bot] wants to merge 1 commit intomainfrom
devin/1769665716-move-billing-to-store

Conversation

@devin-ai-integration
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot commented Jan 29, 2026

Summary

Moves billing-related UI state fields from settings.json to store.json using the store2 plugin (TinybaseValues). The billing fields (trial_expired_modal_dismissed_at, trial_begin_modal_pending, pro_subscription_modal_shown_at) are not settings concerns and belong in the local store.

Additionally removes trial_start_checked_at entirely as it's redundant - this information is already encoded in the JWT and canStartTrial from useBillingAccess() handles this check.

Changes:

  • Remove billing fields from SETTINGS_MAPPING in settings.ts
  • Add billing fields to valueSchemaForTinybase in packages/store
  • Update 4 files to use main store instead of settings store for billing values
  • Remove redundant trial_start_checked_at logic in useTrialStartOnFirstLaunch

Review & Testing Checklist for Human

  • Verify canStartTrial is sufficient: Confirm that removing trial_start_checked_at check doesn't cause duplicate trial start attempts - the canStartTrial from useBillingAccess() should handle this via JWT
  • Data migration for existing users: Existing users have billing data in settings.json that won't automatically migrate to store.json - this means modals may re-appear once. Verify this is acceptable behavior
  • Test billing modal flows: Launch app, trigger trial expired modal, dismiss it, restart app - verify it doesn't show again within a week

Notes

Link to Devin run: https://app.devin.ai/sessions/670ae6c5cdf64ce2bdb6a7c4fa86973e
Requested by: @yujonglee


Open with Devin

- Remove billing fields from settings.ts SETTINGS_MAPPING (no longer stored in settings.json)
- Add billing fields to valueSchemaForTinybase in packages/store (stored in store.json via TinybaseValues)
- Remove redundant trial_start_checked_at (already encoded in JWT)
- Update hooks to use main store instead of settings store for billing values

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
@devin-ai-integration
Copy link
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@netlify
Copy link

netlify bot commented Jan 29, 2026

Deploy Preview for hyprnote-storybook canceled.

Name Link
🔨 Latest commit dfa3e78
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/697af4eb34322f0008a0d982

@netlify
Copy link

netlify bot commented Jan 29, 2026

Deploy Preview for hyprnote canceled.

Name Link
🔨 Latest commit dfa3e78
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/697af4eb1b7f5c00072ffc01

Copy link
Contributor Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View issue and 3 additional flags in Devin Review.

Open in Devin Review

Comment on lines 73 to 75
hasCheckedRef.current = true;

if (canStartTrial) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🔴 Removing trial_start_checked_at causes trial start API to be called on every app launch

The removal of the trial_start_checked_at persistence check causes startTrial() to be called on every app launch before the actual canStartTrial value is fetched from the server.

Click to expand

Root Cause

In billing.tsx:71, canStartTrial defaults to true while the query is loading:

const canStartTrial = isPro ? false : (canTrialQuery.data ?? true);

The effect in useTrialStartOnFirstLaunch.ts runs immediately when isAuthenticated becomes true. Since canStartTrial is true (the default value before the API response), startTrial() is called:

useEffect(() => {
  if (hasCheckedRef.current || !store || !isAuthenticated) {
    return;
  }
  hasCheckedRef.current = true;
  if (canStartTrial) {
    startTrial();  // Called with canStartTrial = true (default)
  }
}, [isAuthenticated, canStartTrial, store, startTrial]);

Previous Behavior

The old code persisted trial_start_checked_at in the store to prevent this across app restarts:

const checkedAt = store.getValue("trial_start_checked_at");
if (checkedAt) {
  hasCheckedRef.current = true;
  return;
}
store.setValue("trial_start_checked_at", Date.now());

Impact

  • Unnecessary postBillingStartTrial API calls on every app launch
  • The server will reject duplicate trial starts, but this wastes bandwidth and server resources
  • Could cause race conditions or unexpected behavior in the billing system

(Refers to lines 73-77)

Recommendation: Either keep the trial_start_checked_at persistence check, or change canStartTrial to default to false while loading (e.g., canTrialQuery.data ?? false), or wait for the query to complete before running the effect by checking canTrialQuery.isSuccess.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@yujonglee yujonglee closed this Feb 8, 2026
@yujonglee yujonglee deleted the devin/1769665716-move-billing-to-store branch February 8, 2026 13:20
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