Skip to content

Conversation

@ucswift
Copy link
Member

@ucswift ucswift commented Sep 12, 2025

Summary by CodeRabbit

  • New Features

    • Improved analytics reliability with session management and graceful startup when analytics is unavailable.
  • Chores

    • Switched analytics backend; update environment variables to RESPOND_COUNTLY_APP_KEY and RESPOND_COUNTLY_URL.
    • Updated dependencies to align with the new analytics provider.
  • Documentation

    • Added an analytics migration guide with configuration and rollout steps.
  • Tests

    • Expanded analytics test coverage and updated asynchronous map tests for more reliable assertions.
    • Removed obsolete analytics mocks and aligned tests with the new analytics flow.

@coderabbitai
Copy link

coderabbitai bot commented Sep 12, 2025

Walkthrough

Migrates analytics from Aptabase to Countly. Updates env keys and client env schema, replaces the Aptabase service with a new Countly-based analytics service, integrates analytics initialization into app startup, removes the Aptabase provider, adjusts mocks and tests accordingly, and adds a migration document.

Changes

Cohort / File(s) Summary
Env and configuration
./.env.development, env.js, package.json
Replaced RESPOND_APTABASE_* with RESPOND_COUNTLY_* in env files; updated client env schema to COUNTLY_*; swapped dependency from @aptabase/react-native to countly-sdk-react-native-bridge.
Analytics service migration
src/services/analytics.service.ts, src/services/aptabase.service.ts
Added Countly-based analyticsService with init, event/user properties, sessions, retries/disable/recovery; removed Aptabase service implementation.
App initialization flow
src/services/app-initialization.service.ts, src/app/_layout.tsx, src/components/common/aptabase-provider.tsx
Wired analytics initialization into startup sequence; removed AptabaseProvider wrapper and related conditional rendering.
Mocks for Countly
__mocks__/countly-sdk-react-native-bridge/index.ts, __mocks__/countly-sdk-react-native-bridge/CountlyConfig.ts, __mocks__/@aptabase/react-native.ts
Added Countly SDK and config Jest mocks; removed Aptabase mock.
Analytics hook update
src/hooks/use-analytics.ts, src/hooks/__tests__/use-analytics.test.ts
Switched hook to use new analytics service; updated tests to mock and assert against analyticsService.
Tests adjustments (maps and calendar)
src/app/(app)/__tests__/map.test.tsx, src/components/calendar/__tests__/calendar-item-details-sheet-minimal.test.tsx, src/components/calendar/__tests__/calendar-item-details-sheet.security.test.tsx, src/components/calendar/__tests__/compact-calendar-item.test.tsx
Made tests async with waits, expanded analytics assertions, removed header and Aptabase mocks, aligned expectations with component structure.
Service tests overhaul
src/services/__tests__/analytics.service.test.ts, src/services/__tests__/aptabase.service.test.ts
Added comprehensive tests for new analytics service; removed Aptabase service tests.
Documentation
ANALYTICS_MIGRATION.md
Added migration guide for switching from Aptabase to Countly, covering dependencies, initialization, env vars, tests, and rollback.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant App as App Startup
  participant Init as AppInitializationService
  participant Analyt as analyticsService
  participant Countly as Countly SDK

  App->>Init: initialize()
  Note over Init: Idempotent startup
  Init->>Analyt: initializeWithEnv()
  Analyt->>Countly: initWithConfig(CountlyConfig)
  Countly-->>Analyt: Promise resolved/rejected
  alt init success
    Analyt-->>Init: initialized
    Init-->>App: continue startup
  else init failure
    Analyt-->>Init: error thrown
    Note over Init: Log error, do not block startup
    Init-->>App: continue startup
  end
Loading
sequenceDiagram
  autonumber
  actor UI as Component/Hook
  participant Hook as useAnalytics
  participant Analyt as analyticsService
  participant Countly as Countly SDK

  UI->>Hook: track(eventName, props)
  Hook->>Analyt: trackEvent(eventName, props)
  alt service initialized and enabled
    Analyt->>Countly: events.recordEvent({ key, segmentation })
    Countly-->>Analyt: ack/error
    alt record success
      Analyt-->>Hook: resolve
    else record error
      Analyt->>Analyt: increment retryCount<br/>maybe disable temporarily
      Analyt-->>Hook: reject
    end
  else not initialized or disabled
    Analyt-->>Hook: no-op / warn
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "CU-868ex18rd Migrating to countly" succinctly identifies the pull request's primary purpose (migration to Countly analytics) and includes the tracking ticket, so it is directly related to the changeset and concise enough for a quick scan.

Poem

A hop, a skip, I swap my trails,
From Aptabase to Countly sails.
New keys rustle in env’s breeze,
Tests nibble mocks with practiced ease.
Startup hums—no pause, no pout—
Events now bounce, then burrow out.
Thump-thump: shipped! 🐇✨

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/(app)/__tests__/map.test.tsx (1)

316-323: Reset window-dimension mock between tests to avoid orientation bleed

mockReturnValue persists across tests; set the default every beforeEach.

   beforeEach(() => {
     jest.clearAllMocks();
+    const mockUseWindowDimensions = (jest.requireMock('react-native') as any).useWindowDimensions;
+    mockUseWindowDimensions.mockReturnValue({ width: 375, height: 812 });
🧹 Nitpick comments (17)
src/components/calendar/__tests__/compact-calendar-item.test.tsx (1)

148-162: Stabilize time-range assertion to avoid locale/timezone flakiness.

The regex is permissive, but output can still vary with device locale/TZ. Consider pinning TZ in tests (e.g., set process.env.TZ = 'UTC' in jest.setup) or mocking your date formatter to a fixed locale/timezone so this never flakes on CI.

.env.development (1)

24-25: Fix dotenv lints (ordering and trailing newline).

Place COUNTLY keys before SENTRY_DSN and add a newline at EOF.

-RESPOND_SENTRY_DSN=
-RESPOND_COUNTLY_APP_KEY=
-RESPOND_COUNTLY_URL=
+RESPOND_COUNTLY_APP_KEY=
+RESPOND_COUNTLY_URL=
+RESPOND_SENTRY_DSN=
+
env.js (1)

95-97: Tighten validation for Countly env vars (URL format, prod requirements).

Validate https URLs and require non-empty values in production/internal while allowing empty in dev/staging.

-  COUNTLY_URL: z.string(),
-  COUNTLY_APP_KEY: z.string(),
+  COUNTLY_URL: z
+    .string()
+    .refine(
+      (v) => v === '' || /^https:\/\/.+/i.test(v),
+      'COUNTLY_URL must be an https URL (or empty outside production).'
+    ),
+  COUNTLY_APP_KEY: z
+    .string()
+    .refine(
+      (v) => !(APP_ENV === 'production' || APP_ENV === 'internal') || v.trim().length > 0,
+      'COUNTLY_APP_KEY is required in production/internal.'
+    ),
__mocks__/countly-sdk-react-native-bridge/CountlyConfig.ts (1)

1-8: Add common fluent setters to future-proof the mock.

If initialization starts calling additional setters (e.g., setServerURL/setAppKey), tests will fail. Add them returning this.

 export default jest.fn().mockImplementation(() => ({
   setLoggingEnabled: jest.fn().mockReturnThis(),
   enableCrashReporting: jest.fn().mockReturnThis(),
   setRequiresConsent: jest.fn().mockReturnThis(),
   giveConsent: jest.fn().mockReturnThis(),
   setLocation: jest.fn().mockReturnThis(),
   enableParameterTamperingProtection: jest.fn().mockReturnThis(),
+  setServerURL: jest.fn().mockReturnThis(),
+  setAppKey: jest.fn().mockReturnThis(),
 }));
src/services/app-initialization.service.ts (2)

81-82: Nit: Clarify or remove placeholder comment.

Either expand this comment with concrete next steps (e.g., background services you plan to init) or drop it.


84-99: End analytics session during cleanup to avoid dangling sessions.

Since init is handled here, consider ending the session in cleanup.

@@
   async cleanup(): Promise<void> {
     try {
+      // End analytics session
+      try {
+        analyticsService.endSession?.();
+      } catch (_) {
+        // no-op
+      }

If you prefer not to modify this block here, I can follow up with a small PR to wire it in.

src/services/analytics.service.ts (4)

121-123: Pass explicit count to recordEvent for SDK parity

The RN SDK examples pass an event count arg; make the call explicit to avoid signature drift across SDK versions. (support.countly.com)

-      Countly.events.recordEvent(eventName, segmentation);
+      Countly.events.recordEvent(eventName, segmentation, 1);

11-16: Remove or use retryDelay; currently unused

retryDelay is configurable but never applied. Either implement actual backoff or drop the option/field to avoid confusion (docs/text mention “retry/backoff” but code only disables after strikes).

 interface AnalyticsServiceOptions {
   maxRetries?: number;
-  retryDelay?: number;
   enableLogging?: boolean;
   disableTimeout?: number;
 }
@@
   private retryCount = 0;
   private maxRetries = 2;
-  private retryDelay = 2000;
   private enableLogging = true;
@@
   constructor(options: AnalyticsServiceOptions = {}) {
     this.maxRetries = options.maxRetries ?? 2;
-    this.retryDelay = options.retryDelay ?? 2000;
     this.enableLogging = options.enableLogging ?? true;
     this.disableTimeout = options.disableTimeout ?? 10 * 60 * 1000;
   }

Also applies to: 19-35


53-55: Avoid hard-coding consent off; expose a toggle

Consent requirement is disabled by default; explicitly setting setRequiresConsent(false) is redundant and makes future privacy toggling harder. Consider reading a boolean from Env or config to enable consents when needed (GDPR/CCPA). (support.countly.com)


208-211: Clear pending re-enable timer on reset

A scheduled re-enable can fire after reset and flip state unexpectedly during tests or fast app restarts. Store the timer id and clear it in reset.

   private errorThrottleMs = 30000;
   private isInitialized = false;
+  private reenableTimer: ReturnType<typeof setTimeout> | null = null;
@@
-    setTimeout(() => {
-      this.enableAnalytics();
-    }, this.disableTimeout);
+    this.reenableTimer = setTimeout(() => {
+      this.enableAnalytics();
+      this.reenableTimer = null;
+    }, this.disableTimeout);
@@
   public reset(): void {
+    if (this.reenableTimer) {
+      clearTimeout(this.reenableTimer);
+      this.reenableTimer = null;
+    }
     this.retryCount = 0;
     this.isDisabled = false;
     this.lastErrorTime = 0;
     this.isInitialized = false;
   }

Also applies to: 248-254, 19-27

src/services/__tests__/analytics.service.test.ts (2)

129-136: Don’t reach into private state directly

Accessing private fields risks TS errors and brittle tests. Cast to any at the point of mutation or introduce a tiny test helper on the service.

-      analyticsService['isDisabled'] = true;
+      (analyticsService as any).isDisabled = true;

329-339: Avoid hard-coding the disable timeout in tests

Read it from the service to keep tests resilient to config changes.

-      // Fast-forward time to trigger re-enable
-      jest.advanceTimersByTime(10 * 60 * 1000);
+      const minutes = analyticsService.getStatus().disableTimeoutMinutes;
+      jest.advanceTimersByTime(minutes * 60_000);
ANALYTICS_MIGRATION.md (2)

1-1: Fix markdownlint: no trailing punctuation in heading

Remove the colon to satisfy MD026.

-# Analytics Migration: Aptabase to Countly
+# Analytics Migration – Aptabase to Countly

12-27: Docs vs code drift: backoff and session APIs

Doc claims “exponential backoff” and “start, end, extend session,” but code implements strike-based disable and only endSession. Either update docs to match or extend the service.

src/app/(app)/__tests__/map.test.tsx (3)

2-2: Remove unused act import

Keeps the test lean.

-import { fireEvent, render, screen, waitFor, act } from '@testing-library/react-native';
+import { fireEvent, render, screen, waitFor } from '@testing-library/react-native';

112-128: Fire onCameraChanged in MapView mock to make recenter path testable

Trigger a single user-interaction event on mount so the recenter button appears when unlocked. This removes the conditional no-op in the recenter test.

-    MapView: ({ children, testID }: any) => {
-      const { View } = require('react-native');
-      return <View testID={testID}>{children}</View>;
-    },
+    MapView: ({ children, testID, onCameraChanged }: any) => {
+      const React = require('react');
+      const { View } = require('react-native');
+      React.useEffect(() => {
+        onCameraChanged?.({ properties: { isUserInteraction: true } });
+      }, []);
+      return <View testID={testID}>{children}</View>;
+    },

585-621: Make the recenter test assertive (not conditional)

With the MapView mock emitting a user interaction, the button should exist; remove the guard and assert the analytics call deterministically.

-      const recenterButton = screen.queryByTestId('recenter-button');
-      if (recenterButton) {
-        fireEvent.press(recenterButton);
-
-        // Check analytics tracking for recenter action
-        expect(mockTrackEvent).toHaveBeenCalledWith('map_recentered', {
-          timestamp: expect.any(String),
-          isMapLocked: false,
-          zoomLevel: 12,
-        });
-      }
+      fireEvent.press(screen.getByTestId('recenter-button'));
+      expect(mockTrackEvent).toHaveBeenCalledWith('map_recentered', {
+        timestamp: expect.any(String),
+        isMapLocked: false,
+        zoomLevel: 12,
+      });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ecbf22 and 5c45718.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (20)
  • .env.development (1 hunks)
  • ANALYTICS_MIGRATION.md (1 hunks)
  • __mocks__/@aptabase/react-native.ts (0 hunks)
  • __mocks__/countly-sdk-react-native-bridge/CountlyConfig.ts (1 hunks)
  • __mocks__/countly-sdk-react-native-bridge/index.ts (1 hunks)
  • env.js (2 hunks)
  • package.json (1 hunks)
  • src/app/(app)/__tests__/map.test.tsx (14 hunks)
  • src/app/_layout.tsx (1 hunks)
  • src/components/calendar/__tests__/calendar-item-details-sheet-minimal.test.tsx (0 hunks)
  • src/components/calendar/__tests__/calendar-item-details-sheet.security.test.tsx (0 hunks)
  • src/components/calendar/__tests__/compact-calendar-item.test.tsx (1 hunks)
  • src/components/common/aptabase-provider.tsx (0 hunks)
  • src/hooks/__tests__/use-analytics.test.ts (2 hunks)
  • src/hooks/use-analytics.ts (1 hunks)
  • src/services/__tests__/analytics.service.test.ts (1 hunks)
  • src/services/__tests__/aptabase.service.test.ts (0 hunks)
  • src/services/analytics.service.ts (1 hunks)
  • src/services/app-initialization.service.ts (2 hunks)
  • src/services/aptabase.service.ts (0 hunks)
💤 Files with no reviewable changes (6)
  • src/services/tests/aptabase.service.test.ts
  • src/components/calendar/tests/calendar-item-details-sheet-minimal.test.tsx
  • src/components/calendar/tests/calendar-item-details-sheet.security.test.tsx
  • mocks/@aptabase/react-native.ts
  • src/components/common/aptabase-provider.tsx
  • src/services/aptabase.service.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names
Use TypeScript for all components and favor interfaces for props and state
Avoid using any; use precise types
Use React Navigation for navigation and deep linking following best practices
Handle errors gracefully and provide user feedback
Implement proper offline support (caching, queueing, retries)
Use Expo SecureStore for sensitive data storage
Use zustand for state management
Use react-hook-form for form handling
Use react-query for data fetching and caching
Use react-native-mmkv for local storage
Use axios for API requests

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names
Use TypeScript for all components, favoring interfaces for props and state
Avoid using any; strive for precise types
Ensure support for dark mode and light mode
Handle errors gracefully and provide user feedback
Use react-query for data fetching
Use react-i18next for internationalization
Use react-native-mmkv for local storage
Use axios for API requests

Files:

  • src/services/analytics.service.ts
  • __mocks__/countly-sdk-react-native-bridge/index.ts
  • src/components/calendar/__tests__/compact-calendar-item.test.tsx
  • src/services/__tests__/analytics.service.test.ts
  • src/services/app-initialization.service.ts
  • src/hooks/use-analytics.ts
  • src/app/_layout.tsx
  • __mocks__/countly-sdk-react-native-bridge/CountlyConfig.ts
  • src/hooks/__tests__/use-analytics.test.ts
  • src/app/(app)/__tests__/map.test.tsx
src/**

📄 CodeRabbit inference engine (.cursorrules)

src/**: Organize files by feature, grouping related components, hooks, and styles
Directory and file names should be lowercase and hyphenated (e.g., user-profile)

Files:

  • src/services/analytics.service.ts
  • src/components/calendar/__tests__/compact-calendar-item.test.tsx
  • src/services/__tests__/analytics.service.test.ts
  • src/services/app-initialization.service.ts
  • src/hooks/use-analytics.ts
  • src/app/_layout.tsx
  • src/hooks/__tests__/use-analytics.test.ts
  • src/app/(app)/__tests__/map.test.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: Use functional components and React hooks instead of class components
Use PascalCase for React component names
Use React.FC for defining functional components with props
Minimize useEffect/useState usage and avoid heavy computations during render
Use React.memo for components with static props to prevent unnecessary re-renders
Optimize FlatList with removeClippedSubviews, maxToRenderPerBatch, and windowSize
Provide getItemLayout to FlatList when items have consistent size
Avoid anonymous functions in renderItem or event handlers; define callbacks with useCallback or outside render
Use gluestack-ui for styling where available from components/ui; otherwise, style via StyleSheet.create or styled-components
Ensure responsive design across screen sizes and orientations
Use react-native-fast-image for image handling instead of the default Image where appropriate
Wrap all user-facing text in t() from react-i18next for translations
Support dark mode and light mode in UI components
Use @rnmapbox/maps for maps or navigation features
Use lucide-react-native for icons directly; do not use the gluestack-ui icon component
Use conditional rendering with the ternary operator (?:) instead of &&

**/*.tsx: Use functional components and hooks over class components
Ensure components are modular, reusable, and maintainable
Ensure all components are mobile-friendly, responsive, and support both iOS and Android
Use PascalCase for component names
Utilize React.FC for defining functional components with props
Minimize useEffect, useState, and heavy computations inside render
Use React.memo for components with static props to prevent unnecessary re-renders
Optimize FlatList with removeClippedSubviews, maxToRenderPerBatch, and windowSize
Use getItemLayout for FlatList when items have consistent size
Avoid anonymous functions in renderItem or event handlers to prevent re-renders
Ensure responsive design for different screen sizes and orientations
Optimize image handling using rea...

Files:

  • src/components/calendar/__tests__/compact-calendar-item.test.tsx
  • src/app/_layout.tsx
  • src/app/(app)/__tests__/map.test.tsx
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{test,spec}.{ts,tsx}: Create Jest tests for all generated components, services, and logic
Ensure tests run without errors and fix failing tests

Files:

  • src/components/calendar/__tests__/compact-calendar-item.test.tsx
  • src/services/__tests__/analytics.service.test.ts
  • src/hooks/__tests__/use-analytics.test.ts
  • src/app/(app)/__tests__/map.test.tsx
src/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.test.{ts,tsx}: Create and use Jest tests to validate all generated components
Generate tests for all components, services, and logic; ensure tests run without errors

Files:

  • src/components/calendar/__tests__/compact-calendar-item.test.tsx
  • src/services/__tests__/analytics.service.test.ts
  • src/hooks/__tests__/use-analytics.test.ts
  • src/app/(app)/__tests__/map.test.tsx
🧠 Learnings (3)
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Use rnmapbox/maps for maps, mapping, or vehicle navigation

Applied to files:

  • src/app/(app)/__tests__/map.test.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Use rnmapbox/maps for maps or navigation features

Applied to files:

  • src/app/(app)/__tests__/map.test.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to src/**/*.test.{ts,tsx} : Create and use Jest tests to validate all generated components

Applied to files:

  • src/app/(app)/__tests__/map.test.tsx
🧬 Code graph analysis (7)
src/services/analytics.service.ts (1)
env.js (1)
  • Env (170-170)
__mocks__/countly-sdk-react-native-bridge/index.ts (1)
src/services/analytics.service.ts (1)
  • endSession (320-339)
src/services/__tests__/analytics.service.test.ts (1)
src/services/analytics.service.ts (1)
  • analyticsService (342-342)
src/services/app-initialization.service.ts (1)
src/services/analytics.service.ts (1)
  • analyticsService (342-342)
src/hooks/use-analytics.ts (1)
src/services/analytics.service.ts (2)
  • trackEvent (97-134)
  • analyticsService (342-342)
src/hooks/__tests__/use-analytics.test.ts (2)
src/services/analytics.service.ts (1)
  • analyticsService (342-342)
src/hooks/use-analytics.ts (1)
  • useAnalytics (14-22)
src/app/(app)/__tests__/map.test.tsx (1)
src/app/(app)/map.tsx (1)
  • HomeMap (26-382)
🪛 dotenv-linter (3.3.0)
.env.development

[warning] 24-24: [UnorderedKey] The RESPOND_COUNTLY_APP_KEY key should go before the RESPOND_SENTRY_DSN key

(UnorderedKey)


[warning] 25-25: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)


[warning] 25-25: [UnorderedKey] The RESPOND_COUNTLY_URL key should go before the RESPOND_SENTRY_DSN key

(UnorderedKey)

🪛 markdownlint-cli2 (0.17.2)
ANALYTICS_MIGRATION.md

20-20: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)

⏰ 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
🔇 Additional comments (10)
src/components/calendar/__tests__/compact-calendar-item.test.tsx (1)

351-353: LGTM: assertion now targets Card directly.

This matches the component change (Card without CardContent) and keeps the test intent clear.

package.json (1)

96-96: Countly RN bridge added — verify native build & remove Aptabase leftovers

  • Findings: @aptabase/react-native only appears in ANALYTICS_MIGRATION.md (ANALYTICS_MIGRATION.md:9) — no runtime/package references found.
  • Findings: countly-sdk-react-native-bridge is added to package.json (line ~96); repo search returned no other references and no app.config.* plugin hooks mentioning countly.
  • Action required: Run EAS prebuild and iOS Pod install for Expo SDK 53 / RN 0.79.5 on both platforms and confirm both builds succeed; run a package check (yarn why / npm ls or rg) to ensure no Aptabase runtime deps remain and remove the doc “Reinstall: yarn add @aptabase/react-native” if Aptabase is intentionally removed.
src/app/_layout.tsx (1)

199-199: LGTM: Removed Aptabase wrapper; render tree is simplified.

KeyboardProvider directly wraps content; analytics is now initialized via appInitializationService, which is appropriate.

env.js (1)

131-133: Ensure RESPOND_COUNTLY_APP_KEY and RESPOND_COUNTLY_URL are present and non-empty in all .env. files and CI secrets.*

  • .env.development — both keys exist but are empty (lines 24–25).
  • Scan found no other .env.* files; confirm production/.env.ci and CI secret injections contain non-empty values to avoid runtime "initialized=false".
__mocks__/countly-sdk-react-native-bridge/index.ts (1)

1-20: Solid Jest mock surface for Countly.

Covers init, events, session, and init state; default export matches named API. Good for tests.

src/hooks/use-analytics.ts (1)

3-17: Migration to analyticsService looks good.

The hook remains stable and memoized; passthrough to analyticsService is correct.

src/services/app-initialization.service.ts (1)

74-76: Good: Initialize analytics early in the startup sequence.

Running analytics init before other services is appropriate and non-blocking given your try/catch.

src/hooks/__tests__/use-analytics.test.ts (1)

3-11: LGTM: hook wiring and mocks are clean

Mocks the singleton correctly and exercises the public surface with and without props. Types are fine for a test context.

Also applies to: 14-15, 27-37, 39-48

src/services/__tests__/analytics.service.test.ts (1)

55-68: Initialization expectations align with SDK

Asserting CountlyConfig(serverUrl, appKey) and initWithConfig once is correct for 25.x RN bridge. (support.countly.com)

ANALYTICS_MIGRATION.md (1)

8-11: Don't hard‑pin the SDK version in docs — reference package.json or enforce in CI

package.json shows countly-sdk-react-native-bridge: ^25.4.0; ANALYTICS_MIGRATION.md (lines 8–11) is pinned to 25.4.0. Update the doc to '^25.4.0' or derive/inject the version from package.json (or add a CI check to fail on mismatch).

Comment on lines +34 to +38
- **Updated** `env.js` to support Countly variables:
- `RESPOND_COUNTLY_APP_KEY` (replaces `RESPOND_APTABASE_APP_KEY`)
- `RESPOND_COUNTLY_URL` (replaces `RESPOND_APTABASE_URL`)
- **Updated** `.env.development` with new variable names

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Align env var names with implementation

Docs list RESPOND_COUNTLY_* keys, but the service currently reads COUNTLY_*; reconcile both sides and document the final names.

-  - `RESPOND_COUNTLY_APP_KEY` (replaces `RESPOND_APTABASE_APP_KEY`)
-  - `RESPOND_COUNTLY_URL` (replaces `RESPOND_APTABASE_URL`)
+  - `RESPOND_COUNTLY_APP_KEY` (or `COUNTLY_APP_KEY` for backward compat)
+  - `RESPOND_COUNTLY_URL` (or `COUNTLY_URL` for backward compat)
@@
-RESPOND_COUNTLY_APP_KEY=your_countly_app_key_here
-RESPOND_COUNTLY_URL=https://your-countly-server.com
+RESPOND_COUNTLY_APP_KEY=your_countly_app_key_here
+RESPOND_COUNTLY_URL=https://your-countly-server.com
+# Optionally support legacy names if present:
+# COUNTLY_APP_KEY=your_countly_app_key_here
+# COUNTLY_URL=https://your-countly-server.com

Also applies to: 79-83

🤖 Prompt for AI Agents
In ANALYTICS_MIGRATION.md around lines 34–38 and 79–83 the docs reference
RESPOND_COUNTLY_* env vars but the service reads COUNTLY_*; update the
documentation and any example .env entries to use the actual runtime names
(COUNTLY_APP_KEY and COUNTLY_URL) so names match implementation (or
alternatively change the service to read RESPOND_COUNTLY_* if you prefer that
convention) — make the change consistently in both places and update any mention
of replacing RESPOND_APTABASE_* to reflect the chosen final names.

Comment on lines +77 to +92
public async initializeWithEnv(): Promise<void> {
const appKey = Env.COUNTLY_APP_KEY;
const serverUrl = Env.COUNTLY_URL;

if (!appKey || !serverUrl) {
if (this.enableLogging) {
logger.warn({
message: 'Analytics environment variables not configured, skipping initialization',
context: { hasAppKey: Boolean(appKey), hasServerUrl: Boolean(serverUrl) },
});
}
return;
}

return this.initialize(appKey, serverUrl);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix env key mismatch to avoid skipping initialization

Service reads COUNTLY_APP_KEY/COUNTLY_URL, while the migration docs instruct RESPOND_COUNTLY_APP_KEY/RESPOND_COUNTLY_URL. This will silently skip init in non-test environments. Support both (new first, fallback to old) or align names across code and docs.

Apply:

-  public async initializeWithEnv(): Promise<void> {
-    const appKey = Env.COUNTLY_APP_KEY;
-    const serverUrl = Env.COUNTLY_URL;
+  public async initializeWithEnv(): Promise<void> {
+    const appKey = Env.RESPOND_COUNTLY_APP_KEY ?? Env.COUNTLY_APP_KEY;
+    const serverUrl = Env.RESPOND_COUNTLY_URL ?? Env.COUNTLY_URL;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public async initializeWithEnv(): Promise<void> {
const appKey = Env.COUNTLY_APP_KEY;
const serverUrl = Env.COUNTLY_URL;
if (!appKey || !serverUrl) {
if (this.enableLogging) {
logger.warn({
message: 'Analytics environment variables not configured, skipping initialization',
context: { hasAppKey: Boolean(appKey), hasServerUrl: Boolean(serverUrl) },
});
}
return;
}
return this.initialize(appKey, serverUrl);
}
public async initializeWithEnv(): Promise<void> {
const appKey = Env.RESPOND_COUNTLY_APP_KEY ?? Env.COUNTLY_APP_KEY;
const serverUrl = Env.RESPOND_COUNTLY_URL ?? Env.COUNTLY_URL;
if (!appKey || !serverUrl) {
if (this.enableLogging) {
logger.warn({
message: 'Analytics environment variables not configured, skipping initialization',
context: { hasAppKey: Boolean(appKey), hasServerUrl: Boolean(serverUrl) },
});
}
return;
}
return this.initialize(appKey, serverUrl);
}
🤖 Prompt for AI Agents
In src/services/analytics.service.ts around lines 77 to 92, the service
currently reads Env.COUNTLY_APP_KEY and Env.COUNTLY_URL but the migration docs
and environment use RESPOND_COUNTLY_APP_KEY / RESPOND_COUNTLY_URL; update the
code to prefer the new RESPOND_* keys and fall back to the old COUNTLY_* keys
(e.g., appKey = Env.RESPOND_COUNTLY_APP_KEY ?? Env.COUNTLY_APP_KEY; serverUrl =
Env.RESPOND_COUNTLY_URL ?? Env.COUNTLY_URL), keep the existing logging behavior
and context but ensure the hasAppKey/hasServerUrl booleans reflect the final
values checked, and then call initialize(appKey, serverUrl) as before when both
are present.

@ucswift
Copy link
Member Author

ucswift commented Sep 12, 2025

Approve

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

This PR is approved.

@ucswift ucswift merged commit 45ff70c into master Sep 12, 2025
11 of 12 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Sep 29, 2025
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.

2 participants