ChemPal Documentation - v1.7.0
    Preparing search index...

    Function useReviewPrompt

    • Nudges engaged users to leave a Chrome Web Store review once they've clearly gotten value from the extension.

      The prompt appears only for a Web Store install that has been present at least reviewPrompt.minDaysInstalled days and run at least reviewPrompt.minSearches searches (both from config.json). The first dismissal snoozes it ~30 days for one final showing; a second dismissal — or opening the reviews page — silences it for good. Install date and counters come from getReviewPromptState; a pre-existing user with no recorded date is backfilled with "now" on this first check.

      Returns UseReviewPrompt

      The pending ReviewNotice plus onReview / onDismiss actions.

      const { notice, onReview, onDismiss } = useReviewPrompt();
      // after 15 days and 6 searches returning 42 products:
      // notice → { days: 15, searches: 6, products: 42 }
      export function useReviewPrompt(): UseReviewPrompt {
      const [notice, setNotice] = useState<ReviewNotice | undefined>(undefined);

      useEffect(() => {
      let cancelled = false;

      const check = async () => {
      try {
      // Only Web Store (Chrome) installs can leave a store review.
      if (isFirefoxRuntime() || getInstallSource() !== 'webstore') return;

      await ensureInstallDate();
      const state = await getReviewPromptState();
      if (cancelled || state.reviewed || state.installedAt <= 0) return;

      const days = Math.floor((Date.now() - state.installedAt) / DAY_MS);
      if (days < MIN_DAYS_INSTALLED || state.searchCount < MIN_SEARCHES) return;

      // Dismissal window: silenced after MAX_DISMISSALS; the first dismissal
      // snoozes until `snoozedUntil` before the one final showing.
      if (state.dismissCount >= MAX_DISMISSALS) return;
      if (state.dismissCount === 1) {
      if (state.snoozedUntil === undefined || Date.now() < state.snoozedUntil) return;
      }

      setNotice({ days, searches: state.searchCount, products: state.totalResults });
      } catch (error) {
      console.error('Failed to evaluate the review prompt:', { error });
      }
      };

      void check();
      return () => {
      cancelled = true;
      };
      }, []);

      const onReview = useCallback(() => {
      setNotice(undefined);
      chrome.tabs.create({ url: CHROME_WEBSTORE_REVIEWS_URL, active: true });
      void markReviewed();
      }, []);

      const onDismiss = useCallback(() => {
      setNotice(undefined);
      void snoozeReviewPrompt();
      }, []);

      return { notice, onReview, onDismiss };
      }