Skip to content

Commit 9f20d4c

Browse files
authored
feat(templates): improve bit Boilerplate docs #11591 (#11592)
1 parent 83cbee2 commit 9f20d4c

File tree

18 files changed

+107
-17
lines changed

18 files changed

+107
-17
lines changed

src/Templates/Boilerplate/Bit.Boilerplate/.docs/22- Messaging.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,27 @@ private async Task ShowNotification()
546546

547547
---
548548

549-
## 7. Dashboard Data Changed Example
549+
## 7. Testing Push Notifications - Understanding the Four Scenarios
550+
551+
When testing push notifications, it's critical to understand that there are **four distinct scenarios** based on the app state when the notification is sent and when the user taps on it. The Boilerplate project handles all four scenarios across all platforms.
552+
553+
**Scenario 1**: Close the app completely → Send push notification → Tap the notification → Verify the app opens to the correct page
554+
555+
**Scenario 2**: Close the app → Send push notification → Open the app manually (without tapping notification) → Now tap the notification → Verify navigation works
556+
557+
**Scenario 3**: Keep the app open → Send push notification → Close the app → Tap the notification → Verify the app opens to the correct page
558+
559+
**Scenario 4**: Keep the app open → Send push notification → Tap the notification immediately → Verify navigation works without restarting the app
560+
561+
### Key Takeaways
562+
563+
- The codebase includes specialized handling for all four push notification scenarios
564+
- Different entry points are used depending on the app state (e.g., `OnCreate` vs `OnNewIntent` on Android)
565+
- Service workers on the web platform handle scenario detection automatically using `clients.matchAll()`
566+
567+
---
568+
569+
## 8. Dashboard Data Changed Example
550570

551571
Another common scenario is notifying all authenticated clients when data changes.
552572

src/Templates/Boilerplate/Bit.Boilerplate/.github/copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ After applying changes, you **MUST** verify the integrity of the application.
128128
Example 1: `OnClick="WrapHandled(MyMethod)"` instead of `OnClick="MyMethod"`.
129129
Example 2: `OnClick="WrapHandled(async () => await MyMethod())"` instead of `OnClick="async () => await MyMethod()"`.
130130
16. **Use OData Query Options**: Leverage `[EnableQuery]` and `ODataQueryOptions` for efficient data filtering and pagination.
131-
17. **Follow Mapperly Conventions**: Use partial static classes and extensions methods with Mapperly for high-performance object mapping.
131+
17. **Follow Mapperly Conventions**: Use **partial static classes and extensions methods** with Mapperly for high-performance object mapping.
132132
18. **Handle Concurrency**: Always use `ConcurrencyStamp` for optimistic concurrency control in update and delete operations.
133133

134134
## Instructions for adding new model/entity to ef-core DbContext / Database

src/Templates/Boilerplate/Bit.Boilerplate/.github/prompts/getting-started.prompt.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,12 @@ At the end of Stage 21, ask: **"Do you have any questions about .NET MAUI, nativ
10831083
- Find `PushNotificationService` implementation
10841084
- Show examples of sending push notifications with deep links
10851085
- Explain how to handle notification clicks on the client side
1086+
- **Explain** that in order to test push notifications, the following scenarios must be considered:
1087+
1. The time that push notification was sent, the app was closed already, and when the user tapped on the notification to open the app, the app was still closed.
1088+
2. The time that push notification was sent, the app was closed already, but when the user tapped on the notification, the app was already open.
1089+
3. The time that push notification was sent, the app was open, but the time that user tapped on the notification, the app was closed.
1090+
4. The time that push notification was sent, the app was open, and the time that user tapped on the notification, the app was still open.
1091+
Explain that if some similar codes exists in the codebase, they are used to handle these 4 different scenarios across platforms.
10861092
<!--#endif-->
10871093

10881094
---

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Common/Prompt.razor

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
@inherits AppComponentBase
22

3+
@* This component gets shown by the PromptService to prompt the user for input. *@
4+
35
<section>
46
<BitStack Gap="0">
57
<BitStack Horizontal AutoHeight Class="header-stack">

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/AppAiChatPanel.razor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ private async Task StartChannel()
158158
{
159159
channel = Channel.CreateUnbounded<string>(new() { SingleReader = true, SingleWriter = true });
160160

161+
// The following code streams user's input messages to the server and processes the streamed responses.
162+
// It keeps the chat ongoing until CurrentCancellationToken is cancelled.
161163
await foreach (var response in hubConnection.StreamAsync<string>("Chatbot",
162164
new StartChatbotRequest()
163165
{

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ private async Task SocialSignIn(string provider)
211211
try
212212
{
213213
pubSubUnsubscribe?.Invoke();
214-
pubSubUnsubscribe = PubSubService.Subscribe(ClientPubSubMessages.SOCIAL_SIGN_IN, async (uriString) =>
214+
pubSubUnsubscribe = PubSubService.Subscribe(ClientPubSubMessages.SOCIAL_SIGN_IN_CALLBACK, async (uriString) =>
215215
{
216216
// Check out SignInModalService for more details
217217
var uri = uriString!.ToString();

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ private async Task SocialSignUp(string provider)
9090
{
9191
try
9292
{
93-
pubSubUnsubscribe = PubSubService.Subscribe(ClientPubSubMessages.SOCIAL_SIGN_IN, async (uriString) =>
93+
pubSubUnsubscribe = PubSubService.Subscribe(ClientPubSubMessages.SOCIAL_SIGN_IN_CALLBACK, async (uriString) =>
9494
{
9595
// Social sign-in creates a new user automatically, so we only need to navigate to the sign-in page to automatically sign-in the user by provided OTP.
9696
NavigationManager.NavigateTo(uriString!.ToString()!, replace: true);

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientPubSubMessages.cs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,94 @@ public partial class ClientPubSubMessages
88
: SharedPubSubMessages
99
//#endif
1010
{
11+
/// <summary>
12+
/// A publisher that publishes this message wants a snack bar to be shown.
13+
/// </summary>
1114
public const string SHOW_SNACK = nameof(SHOW_SNACK);
15+
16+
/// <summary>
17+
/// A publisher that publishes this message wants a modal to be shown.
18+
/// </summary>
1219
public const string SHOW_MODAL = nameof(SHOW_MODAL);
20+
21+
/// <summary>
22+
/// A publisher that publishes this message wants a modal to be closed.
23+
/// </summary>
1324
public const string CLOSE_MODAL = nameof(CLOSE_MODAL);
1425

26+
/// <summary>
27+
/// This message gets published when the app theme is changed.
28+
/// </summary>
1529
public const string THEME_CHANGED = nameof(THEME_CHANGED);
30+
31+
/// <summary>
32+
/// A publisher that publishes this message wants the navigation panel to be opened.
33+
/// </summary>
1634
public const string OPEN_NAV_PANEL = nameof(OPEN_NAV_PANEL);
35+
36+
/// <summary>
37+
/// A publisher that publishes this message wants the navigation panel to be closed.
38+
/// </summary>
1739
public const string CLOSE_NAV_PANEL = nameof(CLOSE_NAV_PANEL);
40+
41+
/// <summary>
42+
/// When the culture is changed, this message is published.
43+
/// </summary>
1844
public const string CULTURE_CHANGED = nameof(CULTURE_CHANGED);
45+
1946
/// <summary>
2047
/// <inheritdoc cref="Parameters.IsOnline"/>
2148
/// </summary>
2249
public const string IS_ONLINE_CHANGED = nameof(IS_ONLINE_CHANGED);
50+
51+
/// <summary>
52+
/// When the data of the current page is changed, this message is published.
53+
/// </summary>
2354
public const string PAGE_DATA_CHANGED = nameof(PAGE_DATA_CHANGED);
55+
56+
/// <summary>
57+
/// When the route data is updated, this message is published.
58+
/// </summary>
2459
public const string ROUTE_DATA_UPDATED = nameof(ROUTE_DATA_UPDATED);
2560

2661
/// <summary>
2762
/// Supposed to be called using JavaScript to navigate between pages without reloading the app.
2863
/// </summary>
2964
public const string NAVIGATE_TO = nameof(NAVIGATE_TO);
65+
66+
/// <summary>
67+
/// A publisher that publishes this message wants the diagnostic modal to be shown.
68+
/// </summary>
3069
public const string SHOW_DIAGNOSTIC_MODAL = nameof(SHOW_DIAGNOSTIC_MODAL);
3170

3271
//#if (signalR != true)
72+
/// <summary>
73+
/// When the user profile is updated, this message is published.
74+
/// </summary>
3375
public const string PROFILE_UPDATED = nameof(PROFILE_UPDATED);
3476
//#endif
3577

3678
//#if(module == "Sales")
79+
/// <summary>
80+
/// When a user taps on search bar, this message is published to open the AI chat panel with product search prompt.
81+
/// </summary>
3782
public const string SEARCH_PRODUCTS = nameof(SEARCH_PRODUCTS);
3883
//#endif
3984

4085
//#if(ads == true)
86+
/// <summary>
87+
/// When a user has trouble with ads, this message is published to open the AI chat panel with ad help prompt.
88+
/// </summary>
4189
public const string AD_HAVE_TROUBLE = nameof(AD_HAVE_TROUBLE);
4290
//#endif
4391

44-
public const string SOCIAL_SIGN_IN = nameof(SOCIAL_SIGN_IN);
92+
/// <summary>
93+
/// When a user completes social sign-in in a separate window, this message is published to notify the app.
94+
/// </summary>
95+
public const string SOCIAL_SIGN_IN_CALLBACK = nameof(SOCIAL_SIGN_IN_CALLBACK);
4596

97+
/// <summary>
98+
/// A publisher that publishes this message wants to force the app to check for updates and install them.
99+
/// </summary>
46100
public const string FORCE_UPDATE = nameof(FORCE_UPDATE);
47101
}

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/ILocalHttpServer.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
namespace Boilerplate.Client.Core.Services.Contracts;
1+
namespace Boilerplate.Client.Core.Services.Contracts;
22

3+
// Checkout Client.web/wwwroot/web-interop-app.html's comments.
34
public interface ILocalHttpServer : IAsyncDisposable
45
{
56
int EnsureStarted();

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/DefaultExternalNavigationService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public async Task NavigateToAsync(string url)
3636
else
3737
{
3838
pubSubUnsubscribe?.Invoke();
39-
pubSubUnsubscribe = pubSubService.Subscribe(ClientPubSubMessages.SOCIAL_SIGN_IN, async _ =>
39+
pubSubUnsubscribe = pubSubService.Subscribe(ClientPubSubMessages.SOCIAL_SIGN_IN_CALLBACK, async _ =>
4040
{
4141
if (lastOpenedWindowId != null)
4242
{

0 commit comments

Comments
 (0)