Skip to content

Commit 0f80151

Browse files
committed
feat!: Prompt Request Result
1 parent fa302c0 commit 0f80151

20 files changed

+590
-84
lines changed

.editorconfig

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
root = true
2+
3+
#############################
4+
# Core EditorConfig Options #
5+
#############################
6+
7+
[*]
8+
indent_style = tab
9+
end_of_line = crlf
10+
charset = utf-8-bom
11+
trim_trailing_whitespace = true
12+
13+
# Code files
14+
[*.{cs,tt,xaml,xml,md,ps1}]
15+
indent_size = 4
16+
insert_final_newline = true
17+
18+
# XML project files
19+
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
20+
indent_size = 2
21+
22+
# XML config files
23+
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
24+
indent_size = 2
25+
26+
# YML configuration files
27+
[*.{yml,yaml}]
28+
indent_style = space
29+
indent_size = 2
30+
31+
# JSON files
32+
[*.json]
33+
indent_size = 2
34+
35+
###########################
36+
# .NET Coding Conventions #
37+
###########################
38+
39+
[*.cs]
40+
# Organize usings
41+
dotnet_sort_system_directives_first = true
42+
dotnet_separate_import_directive_groups = false
43+
44+
# Avoid the this. keyword
45+
dotnet_style_qualification_for_field = false:suggestion
46+
dotnet_style_qualification_for_property = false:suggestion
47+
dotnet_style_qualification_for_method = false:suggestion
48+
dotnet_style_qualification_for_event = false:suggestion
49+
50+
# Use language keywords instead of framework type names for type references
51+
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
52+
dotnet_style_predefined_type_for_member_access = true:suggestion
53+
54+
# Suggest more modern language features when available
55+
dotnet_style_object_initializer = true:suggestion
56+
dotnet_style_collection_initializer = true:suggestion
57+
dotnet_style_coalesce_expression = true:suggestion
58+
dotnet_style_null_propagation = true:suggestion
59+
dotnet_style_explicit_tuple_names = true:suggestion
60+
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
61+
dotnet_style_prefer_inferred_tuple_names = true:suggestion
62+
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
63+
dotnet_style_prefer_auto_properties = true:suggestion
64+
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
65+
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
66+
67+
# Parentheses preferences
68+
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion
69+
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion
70+
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion
71+
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion
72+
73+
# Modifier preferences
74+
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
75+
dotnet_style_readonly_field = true:suggestion
76+
77+
######################
78+
# Naming Conventions #
79+
######################
80+
81+
# Style Definitions
82+
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
83+
84+
# Constant fields are PascalCase
85+
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
86+
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
87+
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
88+
dotnet_naming_symbols.constant_fields.applicable_kinds = field
89+
dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
90+
dotnet_naming_symbols.constant_fields.required_modifiers = const
91+
92+
# Non-private readonly fields are PascalCase
93+
dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = suggestion
94+
dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields
95+
dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style
96+
dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field
97+
dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected internal, private protected
98+
dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly
99+
dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case
100+
101+
# Non-private static fields are PascalCase
102+
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion
103+
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields
104+
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style
105+
dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field
106+
dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected internal, private protected
107+
dotnet_naming_symbols.non_private_static_fields.required_modifiers = static
108+
dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case
109+
110+
# Private static fields are camelCase
111+
dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion
112+
dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields
113+
dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style
114+
dotnet_naming_symbols.static_fields.applicable_kinds = field
115+
dotnet_naming_symbols.static_fields.required_modifiers = static
116+
dotnet_naming_style.static_field_style.capitalization = camel_case
117+
dotnet_naming_style.static_field_style.required_prefix = _
118+
119+
# Instance fields are camelCase and start with _
120+
dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion
121+
dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields
122+
dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style
123+
dotnet_naming_symbols.instance_fields.applicable_kinds = field
124+
dotnet_naming_style.instance_field_style.capitalization = camel_case
125+
dotnet_naming_style.instance_field_style.required_prefix = _
126+
127+
# Locals and parameters are camelCase
128+
dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion
129+
dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters
130+
dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style
131+
dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local
132+
dotnet_naming_style.camel_case_style.capitalization = camel_case
133+
134+
# Local functions are PascalCase
135+
dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion
136+
dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions
137+
dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style
138+
dotnet_naming_symbols.local_functions.applicable_kinds = local_function
139+
dotnet_naming_style.local_function_style.capitalization = pascal_case
140+
141+
# By default, name items with PascalCase
142+
dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion
143+
dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members
144+
dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style
145+
dotnet_naming_symbols.all_members.applicable_kinds = *
146+
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
147+
148+
#######################
149+
# C# Code Style Rules #
150+
#######################
151+
152+
# Indentation preferences
153+
csharp_indent_block_contents = true
154+
csharp_indent_braces = false
155+
csharp_indent_case_contents = true
156+
csharp_indent_case_contents_when_block = true
157+
csharp_indent_switch_labels = true
158+
csharp_indent_labels = flush_left
159+
160+
# Prefer "var" everywhere
161+
csharp_style_var_for_built_in_types = true:suggestion
162+
csharp_style_var_when_type_is_apparent = true:suggestion
163+
csharp_style_var_elsewhere = true:suggestion
164+
165+
# Prefer property-like constructs to have an expression-body
166+
csharp_style_expression_bodied_properties = true:suggestion
167+
csharp_style_expression_bodied_indexers = true:suggestion
168+
csharp_style_expression_bodied_accessors = true:suggestion
169+
csharp_style_expression_bodied_methods = false:none
170+
csharp_style_expression_bodied_constructors = false:none
171+
csharp_style_expression_bodied_operators = false:none
172+
173+
# Newline preferences
174+
csharp_new_line_before_open_brace = all
175+
csharp_new_line_before_else = true
176+
csharp_new_line_before_catch = true
177+
csharp_new_line_before_finally = true
178+
csharp_new_line_before_members_in_object_initializers = true
179+
csharp_new_line_before_members_in_anonymous_types = true
180+
csharp_new_line_between_query_expression_clauses = true
181+
182+
# Suggest more modern language features when available
183+
csharp_style_deconstructed_variable_declaration = true:suggestion
184+
csharp_prefer_simple_default_expression = true:suggestion
185+
csharp_style_pattern_local_over_anonymous_function = true:suggestion
186+
csharp_style_inlined_variable_declaration = true:suggestion
187+
188+
# Space preferences
189+
csharp_space_after_cast = false
190+
csharp_space_after_keywords_in_control_flow_statements = true
191+
csharp_space_between_method_call_parameter_list_parentheses = false
192+
csharp_space_between_method_declaration_parameter_list_parentheses = false
193+
csharp_space_between_parentheses = false
194+
csharp_space_before_colon_in_inheritance_clause = true
195+
csharp_space_after_colon_in_inheritance_clause = true
196+
csharp_space_around_binary_operators = before_and_after
197+
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
198+
csharp_space_between_method_call_name_and_opening_parenthesis = false
199+
csharp_space_between_method_call_empty_parameter_list_parentheses = false
200+
201+
# Blocks are allowed
202+
csharp_prefer_braces = true:suggestion
203+
csharp_preserve_single_line_blocks = true
204+
csharp_preserve_single_line_statements = true
205+
206+
# Modifier preferences
207+
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
208+
209+
# CS1591: Missing XML comment for publicly visible type or member
210+
dotnet_diagnostic.CS1591.severity = suggestion

BREAKING_CHANGES.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
# Breaking Changes
1+
# Breaking Changes
22

3-
<!-- Add a header (##) with the version and list the breaking changes.
3+
<!--
4+
Add a header (##) with the version and list the breaking changes.
45
56
Example:
67
78
## 1.0.0
89
- `Something` was removed.
910
- `This` was renamed to `That`.
10-
1111
-->
1212

13+
## 3.0.0
14+
- The 'IReviewService.TryRequestReview' now returns a result.
15+
1316
## 2.0.0
1417
- Update .NET Target From 6 To 8.

README.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Review Service
1+
# Review Service
22

33
This repository introduces abstractions around native review capabilities to ease code sharing and testability.
44
It also introduces business logic to quickly configure conditions and state tracking to prompt for reviews at the right moment.
@@ -55,7 +55,16 @@ Before getting started, please read the [Android](https://developer.android.com/
5555
// Do Meaningful Task.
5656
5757
// Check if all conditions are satisfied and prompt for review if they are.
58-
await _reviewService.TryRequestReview(ct);
58+
var result = await _reviewService.TryRequestReview(ct);
59+
60+
if (result.IsSuccessful)
61+
{
62+
// Review prompt was successfully shown
63+
}
64+
else
65+
{
66+
Console.WriteLine( $ "Review request status: {result.Status}.");
67+
}
5968
}
6069
```
6170

@@ -154,7 +163,11 @@ var host = new HostBuilder()
154163
>
155164
> public Task<bool> GetAreConditionsSatisfied(CancellationToken ct) => _reviewService.GetAreConditionsSatisfied(ct);
156165
>
157-
> public Task TryRequestReview(CancellationToken ct) => _reviewService.TryRequestReview(ct);
166+
> public async Task<ReviewRequestResult> TryRequestReview(CancellationToken ct)
167+
> {
168+
> var status = await _reviewService.TryRequestReview(ct);
169+
> return new ReviewRequestResult(status);
170+
> }
158171
>
159172
> public Task UpdateReviewSettings(CancellationToken ct, Func<ReviewSettings, ReviewSettings> updateFunction) => _reviewService.UpdateReviewSettings(ct, updateFunction);
160173
> }
@@ -344,4 +357,4 @@ This project is licensed under the Apache 2.0 license - see the
344357
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on the process for
345358
contributing to this project.
346359

347-
Be mindful of our [Code of Conduct](CODE_OF_CONDUCT.md).
360+
Be mindful of our [Code of Conduct](CODE_OF_CONDUCT.md).

src/ReviewService.Abstractions/IReviewPrompter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ public interface IReviewPrompter
1818
/// <summary>
1919
/// Prompts the user to rate the current application using the platform's default application store.
2020
/// </summary>
21-
/// <returns><see cref="Task"/>.</returns>
22-
Task TryPrompt();
21+
/// <returns>The review prompt status.</returns>
22+
Task<ReviewPromptStatus> TryPrompt();
2323
}

src/ReviewService.Abstractions/IReviewService.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ public interface IReviewService<TReviewSettings>
1616
/// Checks if all review prompt conditions are satisfied and then prompt user to review the current application.
1717
/// </summary>
1818
/// <param name="ct"><see cref="CancellationToken"/>.</param>
19-
/// <returns><see cref="Task"/>.</returns>
20-
Task TryRequestReview(CancellationToken ct);
19+
/// <returns>A <see cref="ReviewRequestResult"/> containing the status and success information.</returns>
20+
Task<ReviewRequestResult> TryRequestReview(CancellationToken ct);
2121

2222
/// <summary>
2323
/// Gets if all conditions are satisfied which means that we can prompt user to review the current application.
@@ -31,6 +31,5 @@ public interface IReviewService<TReviewSettings>
3131
/// </summary>
3232
/// <param name="ct"><see cref="CancellationToken"/>.</param>
3333
/// <param name="updateFunction">Function that returns updated <typeparamref name="TReviewSettings"/>.</param>
34-
/// <returns><see cref="Task"/>.</returns>
3534
Task UpdateReviewSettings(CancellationToken ct, Func<TReviewSettings, TReviewSettings> updateFunction);
3635
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
namespace ReviewService;
2+
3+
/// <summary>
4+
/// The status of a review prompt attempt.
5+
/// </summary>
6+
public enum ReviewPromptStatus
7+
{
8+
/// <summary>
9+
/// The review prompt was successfully shown to the user, and the user has completed the review process.
10+
/// </summary>
11+
Success,
12+
13+
/// <summary>
14+
/// The review prompt was shown to the user, but the user did not complete the review process.
15+
/// </summary>
16+
Canceled,
17+
18+
/// <summary>
19+
/// The review prompt was attempted but failed, typically due to an exception or an error in the prompting process.
20+
/// </summary>
21+
Failed,
22+
23+
/// <summary>
24+
/// The review prompt was not attempted because the conditions were not satisfied.
25+
/// </summary>
26+
NotAttempted,
27+
28+
/// <summary>
29+
/// The review prompt status is unknown, typically used when the platform does not support prompting or the implementation is not available.
30+
/// </summary>
31+
Unknown,
32+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
namespace ReviewService;
2+
3+
/// <summary>
4+
/// Represents the result of a review request operation.
5+
/// </summary>
6+
public sealed class ReviewRequestResult
7+
{
8+
/// <summary>
9+
/// Initializes a new instance of the <see cref="ReviewRequestResult"/> class.
10+
/// </summary>
11+
/// <param name="status">The status of the review request.</param>
12+
public ReviewRequestResult(ReviewPromptStatus status)
13+
{
14+
Status = status;
15+
}
16+
17+
/// <summary>
18+
/// Gets the status of the review request.
19+
/// </summary>
20+
public ReviewPromptStatus Status { get; }
21+
22+
/// <summary>
23+
/// Gets a value indicating whether the review request was successful.
24+
/// </summary>
25+
public bool IsSuccessful => Status is ReviewPromptStatus.Success;
26+
}

src/ReviewService.Abstractions/ReviewService.Abstractions.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@
3333
</PropertyGroup>
3434

3535
<ItemGroup>
36-
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
36+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
3737
</ItemGroup>
3838
</Project>

src/ReviewService.NativePrompters/ReviewPrompter.Android.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,21 @@ public ReviewPrompter(ILogger<ReviewPrompter>? logger)
2626
}
2727

2828
/// <inheritdoc/>
29-
public Task TryPrompt()
29+
public Task<ReviewPromptStatus> TryPrompt()
3030
{
31-
var tcs = new TaskCompletionSource();
31+
var tcs = new TaskCompletionSource<ReviewPromptStatus>();
3232

3333
_handler.Post(async () =>
3434
{
3535
try
3636
{
3737
_logger.LogDebug("Trying to prompt the user for a review.");
3838

39-
await CrossStoreReview.Current.RequestReview(false);
39+
var reviewStatus = await CrossStoreReview.Current.RequestReview(false);
4040

4141
_logger.LogInformation("Prompted the user for a review.");
4242

43-
tcs.SetResult();
43+
tcs.SetResult(reviewStatus.ToReviewPromptStatus());
4444
}
4545
catch (Exception e)
4646
{

0 commit comments

Comments
 (0)