{"id":20340910,"url":"https://github.com/leancodepl/app-rating","last_synced_at":"2025-10-05T01:39:53.242Z","repository":{"id":209663594,"uuid":"718999680","full_name":"leancodepl/app-rating","owner":"leancodepl","description":null,"archived":false,"fork":false,"pushed_at":"2025-08-26T14:35:27.000Z","size":161,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-26T19:48:02.841Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leancodepl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-11-15T08:29:23.000Z","updated_at":"2025-08-26T14:32:55.000Z","dependencies_parsed_at":"2023-12-01T14:01:22.607Z","dependency_job_id":"6a5bec1b-5699-44de-8a65-e2a60486e3c7","html_url":"https://github.com/leancodepl/app-rating","commit_stats":null,"previous_names":["leancodepl/app-rating"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/leancodepl/app-rating","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leancodepl%2Fapp-rating","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leancodepl%2Fapp-rating/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leancodepl%2Fapp-rating/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leancodepl%2Fapp-rating/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leancodepl","download_url":"https://codeload.github.com/leancodepl/app-rating/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leancodepl%2Fapp-rating/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278399608,"owners_count":25980329,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T21:24:37.469Z","updated_at":"2025-10-05T01:39:53.228Z","avatar_url":"https://github.com/leancodepl.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# app_rating\n\n\u003cimg width=\"300\" alt=\"image\" src=\"https://github.com/user-attachments/assets/f5d48083-7f18-4a1d-9a5d-82d3f862f3cd\"\u003e\n\u003cimg width=\"300\" alt=\"image\" src=\"https://github.com/user-attachments/assets/0ba3321e-39fb-4f9b-94ee-6e7cbca1fe98\"\u003e\n\u003cimg width=\"300\" alt=\"image\" src=\"https://github.com/user-attachments/assets/b7d40e4e-cf22-4199-9240-2f690357e5f5\"\u003e\n\n## Description\n\nThis feature provides out-of-the-box support for collecting direct feedback from users. Feedback can be gathered through two methods:\n\n- **Five star rating with optional comment:** Users can provide a rating on a 1-5 star scale, accompanied by an optional comment.\n- **Yes/No Satisfaction:** Users can answer a simple yes/no question regarding their overall satisfaction with the application.\n\nIf the user provides a positive review, they will be kindly asked to rate the application on the app store. In addition to the feedback, OS and mobile device parameters are automatically stored in the backend for analytics purposes.\n\n## Usage\n\n### Mobile\n\n#### Setup\n\nAdd `AppRatingLocalizations` to your localizations delegates in `MaterialApp`:\n```dart\n  localizationsDelegates: const [\n    (...)\n    ...AppRatingLocalizations.localizationsDelegates,\n  ],\n```\n\nInitialize `AppRating` instance with:\n- your `cqrs`\n- your app's AppStore ID (ex. `apps.apple.com/us/app/example/id000000000`)\n- app version tag (`CFBundleShortVersionString` on iOS, `versionName` on Android). Feel free to use the [package_info_plus](https://pub.dev/packages/package_info_plus) for getting this value.\n\n```dart\nfinal rateApp = AppRating(\n  cqrs: cqrs,\n  appleStoreId: '111111',\n  appVersion: packageInfo.version,\n);\n```\n\nthen provide it globally with `Provider` and use wherever you want to.\n\n#### Yes/No dialog\n\n```dart\nvoid showSingleAnswerDialog(\n  BuildContext context, {\n  String? singleAnswerDialogHeader,\n  String? singleAnswerDialogPositiveButton,\n  String? singleAnswerDialogNegativeButton,\n  String? singleAnswerDialogCancelButton,\n  String? singleAnswerDialogMoreInfoHeader,\n  String? singleAnswerDialogMoreInfoPrimaryButton,\n  String? singleAnswerDialogMoreInfoSecondaryButton,\n})\n```\n\nA dialog box is displayed, presenting the user with a `Yes` or `No` option to answer whether they like the application. The user's response is mapped as follows:\n- Selecting `Yes` sends a value of 5 to the backend.\n- Selecting `No` sends a value of 0 to the backend.\n\nUpon selecting Yes, the `requestReview` function from the [in_app_review](https://pub.dev/packages/in_app_review) package is invoked in the background. This function attempts to display the app review dialog from the app store.\n\n**WARNING:** The review dialog is not guaranteed to appear, as its display is controlled by the OS.\n\n#### Five star rating dialog\n\n```dart\nvoid showStarDialog(\n  BuildContext context, {\n  String? starDialogHeader,\n  String? starDialogSubtitle,\n  String? starDialogPrimaryButton,\n  String? starDialogSecondaryButton,\n  String? starDialogRateUsHeader,\n  String? starDialogRateUsSubtitle,\n  String? starDialogOpenStoreButton,\n  String? starDialogOpenStoreCloseButton,\n})\n```\n\nThe showStarDialog function displays a dialog box allowing the user to provide a star rating. If the user rates the app with fewer than 5 stars, the dialog expands to include a text field for additional comments. For high ratings (5 stars), the dialog changes its appearance by showing a button that directs the user to the app store to submit a review.\n\n#### Customization\n\nIn the current version of this package, you're not able to have a strong impact on how the dialogs look and how the flow works. You can apply your own texts and labels into `showStarDialog` and `showSingleAnswerDialog` methods. But at this moment, that's it.\n\n## Backend\n\nThe backend is responsible for storing the feedback data and sending it to the analytics system. The feedback data includes the following parameters:\n- `Rating` (double): The user's rating on a scale defined by mobile app.\n- `AdditionalComment` (string): The user's optional comment.\n- `Platform` (PlatformDTO): Android or iOS.\n- `SystemVersion` (string): The user's operating system version.\n- `AppVersion` (string): The user's application version.\n- `Metadata` (Dictionary\u003cstring, object\u003e): Additional metadata containing any other data important (tenant information, feature flag configuration, etc.).\n\n### Setup\n\nReference `LeanCode.AppRating.Contracts` in your contracts project.\n\n```\n  \u003cItemGroup\u003e\n    (...)\n    \u003cPackageReference Include=\"LeanCode.AppRating.Contracts\" /\u003e\n    (...)\n  \u003c/ItemGroup\u003e\n```\nReference `LeanCode.AppRating` in your service.\n\n```\n  \u003cItemGroup\u003e\n    (...)\n    \u003cPackageReference Include=\"LeanCode.AppRating\" /\u003e\n    (...)\n  \u003c/ItemGroup\u003e\n```\n\nConfigure `YourDbContext` so that it implements `IAppRatingStore\u003cTUserId\u003e` interface.\n\n```csharp\npublic class YourDbContext : DbContext, IAppRatingStore\u003cTUserId\u003e\n{\n    (...)\n\n    public DbSet\u003cAppRating\u003cTUserId\u003e\u003e AppRatings =\u003e Set\u003cAppRating\u003cTUserId\u003e\u003e();\n\n    (...)\n\n    protected override void OnModelCreating(ModelBuilder modelBuilder)\n    {\n        (...)\n\n        builder.ConfigureAppRatingEntity\u003cTUserId\u003e(SqlDbType.PostgreSql);\n\n        (...)\n    }\n}\n```\n\nRegister AppRating module in your `Startup` class - you need to define:\n- `TUserId` - type of user id (Can be `Guid`, `int`, `string`, or [strongly typed id](https://leancode-corelibrary.readthedocs.io/domain/id/)).\n- `TUserIdExtractor` - class that implements `IUserIdExtractor\u003cTUserId\u003e` interface. It is responsible for extracting user id from the request.\n- `YourDbContext` - db context where feedback will be stored. Remember to point DbContext configured in previous step.\n\n```csharp\npublic class Startup : LeanStartup\n{\n    (...)\n\n    public void ConfigureServices(IServiceCollection services)\n    {\n        services\n            (...)\n            .AddAppRating\u003cTUserId, YourDbContext, TUserIdExtractor\u003e()\n            (...);\n    }\n\n    (...)\n}\n\npublic sealed class TUserIdExtractor : IUserIdExtractor\u003cTUserId\u003e\n{\n    public TUserId Extract(HttpContext httpContext)\n    {\n        // implement your own logic to extract user id, eg. from claims\n\n        var claim = context.User.FindFirstValue(Auth.KnownClaims.UserId);\n\n        ArgumentException.ThrowIfNullOrEmpty(claim);\n\n        return TUserId.Parse(claim);\n    }\n}\n```\n\nConfigure user role to have `RateApp` permission, e.g.:\n\n```csharp\ninternal class AppRoles : IRoleRegistration\n{\n    public IEnumerable\u003cRole\u003e Roles { get; } =\n        [\n            new Role(R.User, R.User, LeanCode.AppRating.Contracts.RatingPermissions.RateApp),\n        ];\n}\n```\n\n### Email configuration\n\nYou need to configure `AppRatingReportsConfiguration` in order to define administrative email details. We assume that you have already configured email sending and localization in your application. Ensure that `LowRatingEmailSubjectKey` is defined in your localization files.\n\n```csharp\n    public void ConfigureServices(IServiceCollection services)\n    {\n        (...)\n\n        services.AddSingleton(\n            new AppRatingReportsConfiguration(\n                LowRatingUpperBoundInclusive: 2.0,\n                LowRatingEmailCulture: \"en\",\n                LowRatingEmailSubjectKey: \"emails.low-rate-submitted.subject\",\n                FromEmail: \"test+from@leancode.pl\",\n                ToEmails: [\"test+to@leancode.pl\", \"support@example.app\"]\n            )\n        );\n\n        (...)\n    }\n```\n\nYou need to configure email templates for low rating reports named `LowRateSubmittedEmail`. You can use those templates as a starting point:\n\n```html\n\u003ch4\u003eOne of your users send a low rate. Please log in to your admin panel to check details\u003c/h4\u003e\n\n\u003cul\u003e\n    \u003cli\u003e\n        UserId: @{\n            WriteLiteral(Model.UserId);\n        }\n    \u003c/li\u003e\n\n    \u003cli\u003e\n        Rating: @{\n            WriteLiteral(Model.Rating.ToString(\"F1\"));\n        }\n    \u003c/li\u003e\n\n    \u003cli\u003e\n        AdditionalComment: @{\n            WriteLiteral(Model.AdditionalComment);\n        }\n    \u003c/li\u003e\n\u003c/ul\u003e\n```\n\n```csharp\nOne of your users send a low rate. Please log in to your admin panel to check details\n\nUserId: @{\n    WriteLiteral(Model.UserId);\n}\n\nRating: @{\n    WriteLiteral(Model.Rating);\n}\n\nAdditionalComment: @{\n    WriteLiteral(Model.AdditionalComment);\n}\n```\n\nYou need to configure MassTransit consumer that will send emails.\n\n```csharp\n    public override void ConfigureServices(IServiceCollection services)\n    {\n        (...)\n\n        services.AddCQRSMassTransitIntegration(cfg =\u003e\n        {\n            (...)\n\n            cfg.AddAppRatingConsumers\u003cTUserId\u003e();\n\n            (...)\n        });\n\n        (...)\n    }\n```\n\n## Requirements\n**Note:** Debugging this on the emulators or the simulators will not provide the faithful experience of production environment.\n\n### Android\n- Android 5 or higher\n- Google Play Store must be installed.\n\n### iOS\nRequires iOS version 10.3\n\n## Guidelines\n\nWhile the `AppRating` instance shares the `in_app_review` interface with the `requestReview()` method, it is not recommended to use this method directly. App store guidelines are typically strict regarding when and how users are asked to leave the reviews. Also offering in-app rewards for the reviews is often prohibited.\n\nIf you want to implement a \"Give us feedback\" action button, use one of the following methods:\n- `appRating.showStarDialog()`\n- `appRating.showSimpleAnswerDialog()`\n- `appRating.inAppReview.openStoreListing()` (for a more direct approach, but use with caution).\n\nIf you choose to use the `appRating.inAppReview.requestReview()` method, ensure it is integrated into the app’s logic in a way that avoids triggering it too early or too frequently, to comply with store guidelines.\n\nCheck out these official guidelines:\n- https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/ratings-and-reviews/\n- https://developer.android.com/guide/playcore/in-app-review#when-to-request\n- https://developer.android.com/guide/playcore/in-app-review#design-guidelines\n\n## Team\n- @lukaszgarstecki\n- @denis-lncd\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleancodepl%2Fapp-rating","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleancodepl%2Fapp-rating","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleancodepl%2Fapp-rating/lists"}