Skip to content

Add sponsors and a one-time support prompt - #122

Draft
SunkenInTime wants to merge 3 commits into
mainfrom
agent/add-project-support
Draft

Add sponsors and a one-time support prompt#122
SunkenInTime wants to merge 3 commits into
mainfrom
agent/add-project-support

Conversation

@SunkenInTime

@SunkenInTime SunkenInTime commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • credit Greptile's Open Source Program and OpenAI's Codex for Open Source program in the README and app settings
  • add a permanent, low-key Buy Me a Coffee entry to Settings
  • show a non-modal support toast only once, on the third app launch, after a six-second delay
  • persist the one-time flag before displaying the toast so navigation or an early exit cannot make it recur

Validation

  • flutter test --concurrency=4 — 398 tests passed, 1 skipped
  • flutter analyze --no-fatal-infos — no new findings; one existing deprecation info remains in lib/widgets/pages_bar.dart

Summary by CodeRabbit

  • New Features

    • Added a Sponsors and Support section in Settings with links to Buy Me a Coffee, Greptile, and OpenAI Codex for Open Source.
    • Added a one-time “Buy me a coffee” prompt after repeated app launches.
    • Added launch tracking to support the prompt experience.
    • Toast notifications can now use customized display durations.
  • Documentation

    • Updated the README with support and sponsor acknowledgments.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@SunkenInTime, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 82bfd948-4724-4620-9682-0f0ad1a21b55

📥 Commits

Reviewing files that changed from the base of the PR and between a1b8838 and 4a7e5e8.

📒 Files selected for processing (1)
  • README.md
📝 Walkthrough

Walkthrough

The app now tracks launch counts in Hive, shows a one-time delayed support toast after three launches, and adds sponsor links to global settings and the README. Support links use shared URI settings and URL launching.

Changes

Support and sponsor features

Layer / File(s) Summary
Support settings and launch state
lib/const/hive_boxes.dart, lib/const/settings.dart, lib/main.dart
Adds Hive keys and support URIs. Startup increments the persisted launch count. Toast auto-close duration is configurable.
Launch-triggered support prompt
lib/widgets/folder_navigator.dart
Checks launch flags after initialization and shows a one-time delayed Buy Me a Coffee toast on supported platforms.
Sponsor links in settings and documentation
lib/widgets/settings_tab.dart, README.md
Adds an About & sponsors settings section with external-link actions and documents the sponsor links in the README.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant Hive
  participant FolderNavigator
  participant Toast
  participant URLLauncher

  App->>Hive: Open app-flags box
  App->>Hive: Increment launch count
  FolderNavigator->>Hive: Check prompt state
  FolderNavigator->>Toast: Show delayed support prompt
  Toast->>URLLauncher: Open Buy Me a Coffee link
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: adding sponsor acknowledgments and a one-time support prompt.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/add-project-support

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.

@greptile-apps

greptile-apps Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This change adds sponsor and support links and introduces a one-time support invitation based on application launch count. The Windows support-prompt flow was verified to leave its persisted prompt state unclaimed when WebView2 is unavailable on the third launch, then show the invitation on a later launch after WebView2 becomes available. This should be corrected before merge so the invitation follows the intended third-launch policy.

Confidence Score: 4/5

The support invitation does not consistently follow its intended one-time third-launch behavior on Windows systems where WebView2 is initially unavailable.

An executable reproduction verified the persisted-state transition: the third launch leaves the invitation unclaimed, while a later launch with WebView2 initialized claims and schedules it. Native Flutter rendering was unavailable, but the relevant launch-state and prompt-claim behavior was exercised directly.

Files Needing Attention: lib/widgets/folder_navigator.dart needs the WebView2 availability check and persisted support-prompt eligibility handling to be separated.

T-Rex T-Rex Logs

What T-Rex did

  • Executed the support-prompt reproduction script to reproduce the P1 scenario and verified the reproduction syntax.
  • Observed the third launch with unavailable WebView2 leaving the prompt unclaimed.
  • Observed the later initialized launch claims and schedules the prompt.
  • Validated the contract-level flow by mapping the before/after WebView2 launch outcomes and the runtime blocker log, confirming the overall sequence.

View all artifacts

T-Rex Ran code and verified through T-Rex

Comments Outside Diff (1)

  1. General comment

    P1 Support prompt is deferred past the third launch when WebView2 is unavailable

    • Bug
      • On Windows, _maybeShowSupportPrompt returns at line 99 when isWebViewInitialized is false. That return occurs before the Hive box is read and before the one-time support prompt is claimed. Since main.dart increments the launch count before runApp, the third launch leaves support_prompt_shown at 0. On a later launch once WebView2 is initialized, the count is already greater than or equal to 3, so the prompt is claimed and displayed after its delay.
    • Cause
      • The WebView2 availability guard precedes both the Hive read and the launchCount < 3 eligibility check/claim.
    • Fix
      • Evaluate and claim the third-launch support-prompt eligibility independently of WebView2 availability (for example, move the Windows/WebView2 guard after the Hive claim logic, or record that the third-launch opportunity was consumed before returning).

    T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "add sponsors and one-time support prompt" | Re-trigger Greptile

Comment thread lib/widgets/folder_navigator.dart Outdated

Future<void> _maybeShowSupportPrompt() async {
if (kIsWeb) return;
if (Platform.isWindows && !isWebViewInitialized) return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 WebView gate defers the third-launch prompt

On Windows without WebView2, this return occurs before the persisted prompt state is read or claimed. The application has already recorded the third launch, but support_prompt_shown remains unset; when WebView2 becomes available on a later launch, the launchCount >= 3 condition causes the support toast to appear then instead of consuming the intended third-launch opportunity. Record or consume the third-launch eligibility independently of WebView2 availability.

Artifacts

Executable support-prompt state reproduction source

  • Node source used to assert the checked-out guard ordering and execute the two launch-state transitions, with the takeaway that it models the exact early-return and claim sequence.

Third launch with unavailable WebView2 leaves prompt unclaimed

  • Executed third-launch reproduction with Windows WebView2 unavailable; it records launch count 3, no Hive read, no claim, and persisted `support_prompt_shown: 0`, with the takeaway that the third-launch invitation was not consumed.

Later initialized launch claims and schedules prompt

  • Executed subsequent Windows launch reproduction with WebView2 initialized; it records launch count 4, a Hive read, a claim, and a scheduled toast, with the takeaway that the prompt appears later than the intended third launch.

Flutter and Chromium runtime availability check

  • Runtime availability command output shows `flutter` is not found and Chromium is absent, with the takeaway that rendered Flutter UI/video validation could not run in this environment.

Executable reproduction syntax check

  • Node syntax validation for the authored reproduction completed with exit code 0, with the takeaway that the executed test harness was syntactically valid.

View artifacts

T-Rex Ran code and verified through T-Rex

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/widgets/folder_navigator.dart`:
- Around line 108-111: In the prompt flow around the readiness check and
Settings.showToast, move the Platform.isWindows && !isWebViewInitialized early
return before writing flags.put(HiveBoxNames.supportPromptShownKey, 1). Keep the
persisted write immediately before the existing six-second delay so the flag is
saved only when the prompt can proceed.

In `@lib/widgets/settings_tab.dart`:
- Around line 581-610: Replace the direct launchUrl calls in the three
_SupportLinkTile onPressed callbacks with a shared async helper that accepts the
target URL, checks for a false launch result, catches platform exceptions, and
reports failures through the existing toast/error UI. Keep each tile’s URL
unchanged and ensure the callbacks await or return the helper’s Future.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 504a6b92-1147-4dd4-a34c-218215c77ea1

📥 Commits

Reviewing files that changed from the base of the PR and between aeefc69 and a1b8838.

📒 Files selected for processing (6)
  • README.md
  • lib/const/hive_boxes.dart
  • lib/const/settings.dart
  • lib/main.dart
  • lib/widgets/folder_navigator.dart
  • lib/widgets/settings_tab.dart

Comment on lines +108 to +111
// Claim the prompt before the delay so a route change or quick exit never
// turns a one-time invitation into a recurring interruption.
await flags.put(HiveBoxNames.supportPromptShownKey, 1);
if (Platform.isWindows && !isWebViewInitialized) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Move the persisted “shown” flag after the readiness check.

Line 110 writes supportPromptShownKey = 1. Line 111 can then return before Settings.showToast runs. On a Windows launch with an uninitialized WebView, the prompt is not shown, but later launches return at Line 106 because the flag is already set.

Move the readiness check before the write. Keep the write before the six-second delay.

Proposed fix
-    // Claim the prompt before the delay so a route change or quick exit never
-    // turns a one-time invitation into a recurring interruption.
-    await flags.put(HiveBoxNames.supportPromptShownKey, 1);
     if (Platform.isWindows && !isWebViewInitialized) return;
+    // Claim the prompt before the delay so a route change or quick exit never
+    // turns a one-time invitation into a recurring interruption.
+    await flags.put(HiveBoxNames.supportPromptShownKey, 1);

As per coding guidelines, “If a library write path is uncertain, fail loudly without saving rather than persisting potentially incorrect data.”

📝 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
// Claim the prompt before the delay so a route change or quick exit never
// turns a one-time invitation into a recurring interruption.
await flags.put(HiveBoxNames.supportPromptShownKey, 1);
if (Platform.isWindows && !isWebViewInitialized) return;
if (Platform.isWindows && !isWebViewInitialized) return;
// Claim the prompt before the delay so a route change or quick exit never
// turns a one-time invitation into a recurring interruption.
await flags.put(HiveBoxNames.supportPromptShownKey, 1);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/widgets/folder_navigator.dart` around lines 108 - 111, In the prompt flow
around the readiness check and Settings.showToast, move the Platform.isWindows
&& !isWebViewInitialized early return before writing
flags.put(HiveBoxNames.supportPromptShownKey, 1). Keep the persisted write
immediately before the existing six-second delay so the flag is saved only when
the prompt can proceed.

Source: Coding guidelines

Comment on lines +581 to +610
_SupportLinkTile(
icon: Icons.bug_report_outlined,
title: "Greptile",
description:
"AI code review through Greptile's Open Source Program.",
actionLabel: "View program",
onPressed: () {
launchUrl(Settings.greptileOpenSourceLink);
},
),
const _SettingsItemDivider(),
_SupportLinkTile(
icon: Icons.code_outlined,
title: "OpenAI",
description:
"Tooling and credits through Codex for Open Source.",
actionLabel: "View program",
onPressed: () {
launchUrl(Settings.openAICodexForOssLink);
},
),
const _SettingsItemDivider(),
_SupportLinkTile(
icon: Icons.local_cafe_outlined,
title: "Support Icarus",
description:
"If Icarus helps your team, you can help keep development going.",
actionLabel: "Buy me a coffee",
onPressed: () {
launchUrl(Settings.buyMeACoffeeLink);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Expect every launchUrl call to use result-aware handling.
if rg -nP '^\s*launchUrl\s*\(' lib/widgets/settings_tab.dart; then
  echo "Found a fire-and-forget launchUrl call"
  exit 1
fi

rg -n -C 5 'launchUrl\(' lib/widgets/settings_tab.dart

Repository: SunkenInTime/icarus

Length of output: 387


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Relevant settings_tab.dart section:"
sed -n '560,625p' lib/widgets/settings_tab.dart

echo
echo "Import/dependency declarations:"
rg -n "url_launcher|launchUrl|Toast|showDialog|ErrorMessage|error" pubspec.yaml pubspec.lock

Repository: SunkenInTime/icarus

Length of output: 3225


🌐 Web query:

url_launcher package 6.3.1 launchUrl returns Future<bool> behavior if platform handler not found

💡 Result:

In the url_launcher package version 6.3.1, the launchUrl method returns a Future<bool> that indicates whether the URL was launched successfully [1][2]. When a platform handler for the requested URL cannot be found, the standard behavior is for launchUrl to return false [1][2]. Depending on the specific platform and the nature of the failure, it may alternatively throw a PlatformException [1][2]. It is important to note the following regarding this behavior: 1. Handling Failures: Because launchUrl can return false when an operation fails to launch, the package documentation explicitly recommends checking the boolean return value to handle such scenarios [3][1]. For example: if (!await launchUrl(url)) { // Handle failure (e.g., show an error message or try a fallback) } 2. Limitations of canLaunchUrl: You should not rely solely on canLaunchUrl to predict the outcome of launchUrl [3][4]. On many platforms (especially mobile and web), canLaunchUrl may return false even if launchUrl would succeed, often due to platform-specific security configurations like LSApplicationQueriesSchemes on iOS or <queries> elements in the Android manifest [5][6][2]. 3. Web Considerations: On web platforms, launchUrl behavior can sometimes lead to false negatives (returning false despite the operation potentially succeeding in the browser) depending on how the window or tab is opened (e.g., usage of webOnlyWindowName) [7]. For mission-critical operations, it is best to attempt the launchUrl call directly and implement fallback logic within the else or if (!result) block rather than disabling functionality based on a canLaunchUrl check [3][6].

Citations:


Handle external-link failures before shipping.

launchUrl(...) returns a Future<bool> at all three support-link call sites, and these callbacks discard the result. If the platform cannot launch the URL, the action fails silently. Route these callbacks through one async helper that checks false results and catches platform errors, then reports the failure with the existing toast/error UI.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/widgets/settings_tab.dart` around lines 581 - 610, Replace the direct
launchUrl calls in the three _SupportLinkTile onPressed callbacks with a shared
async helper that accepts the target URL, checks for a false launch result,
catches platform exceptions, and reports failures through the existing
toast/error UI. Keep each tile’s URL unchanged and ensure the callbacks await or
return the helper’s Future.

Source: Coding guidelines

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