Skip to content

Commit

Permalink
See CHANGELOG for details - added Blazor interop for install notifica…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
SQL-MisterMagoo committed Aug 15, 2019
1 parent 5632f4d commit babddbf
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 19 deletions.
2 changes: 2 additions & 0 deletions Blazor.PWA.MSBuild.Tasks/Blazor.PWA.MSBuild.Tasks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ I will add more network caching strategies, but for now it has just one - cache
</ItemGroup>

<ItemGroup>
<None Remove="Templates\ServiceWorker\sw_register-beforeinstallprompt.template.js" />
<None Remove="Templates\ServiceWorker\sw_register-installable-banner.template.js" />
<None Remove="Templates\ServiceWorker\sw_register-installable-blazor.template.js" />
<None Remove="Templates\ServiceWorker\sw_register-update-alert.template.js" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
window.addEventListener('beforeinstallprompt', function (e) {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
window.PWADeferredPrompt = e;

showAddToHomeScreen();

});
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ function showAddToHomeScreen() {

pwaInstallPrompt.id = 'pwa-install-prompt';
pwaInstallPrompt.style.position = 'absolute';
pwaInstallPrompt.style.bottom = '1rem';
pwaInstallPrompt.style.left = '1rem';
pwaInstallPrompt.style.right = '1rem';
pwaInstallPrompt.style.padding = '0.3rem';
pwaInstallPrompt.style.bottom = '0.1rem';
pwaInstallPrompt.style.left = '0.1rem';
pwaInstallPrompt.style.right = '0.1rem';
pwaInstallPrompt.style.padding = '0.5rem';
pwaInstallPrompt.style.display = 'flex';
pwaInstallPrompt.style.backgroundColor = 'lightslategray';
pwaInstallPrompt.style.color = 'white';
pwaInstallPrompt.style.fontFamily = 'sans-serif';
pwaInstallPrompt.style.fontSize = '1.2rem';
pwaInstallPrompt.style.fontSize = '1.3rem';
pwaInstallPrompt.style.borderRadius = '4px';

pwaInstallButton.style.marginLeft = 'auto';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

function showAddToHomeScreen() {
DotNet.invokeMethodAsync(blazorAssembly, blazorInstallMethod)
.then(function () { }, function (er) { setTimeout(showAddToHomeScreen, 1000); });
}

window.BlazorPWA = {
installPWA: function () {
if (window.PWADeferredPrompt) {
window.PWADeferredPrompt.prompt();
window.PWADeferredPrompt.userChoice
.then(function (choiceResult) {
window.PWADeferredPrompt = null;
});
}
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,3 @@
}
});

window.addEventListener('beforeinstallprompt', function (e) {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
window.PWADeferredPrompt = e;

showAddToHomeScreen();

});
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,30 @@
<ServiceWorkerRegisterInstallableType Condition="'$(ServiceWorkerRegisterInstallableType)'==''">installable-banner</ServiceWorkerRegisterInstallableType>
<!-- The file that contains template code for the service worker "update available" -->
<ServiceWorkerRegisterInstallableTemplate Condition="'$(ServiceWorkerRegisterInstallableTemplate)' == ''">$(ServiceWorkerRegisterTemplatePath)sw_register-$(ServiceWorkerRegisterInstallableType).template.js</ServiceWorkerRegisterInstallableTemplate>
<!-- Before Install Prompt handler type -->
<ServiceWorkerRegisterBeforeInstallPromptType Condition="'$(ServiceWorkerRegisterBeforeInstallPromptType)'==''">beforeinstallprompt</ServiceWorkerRegisterBeforeInstallPromptType>
<!-- The file that contains template code for the service worker register "beforeinstallprompt" -->
<ServiceWorkerRegisterBeforeInstallPromptTemplate Condition="'$(ServiceWorkerRegisterBeforeInstallPromptTemplate)' == ''">$(ServiceWorkerRegisterTemplatePath)sw_register-$(ServiceWorkerRegisterBeforeInstallPromptType).template.js</ServiceWorkerRegisterBeforeInstallPromptTemplate>
<!-- event fired by browser when the service worker has installed ** probably never change **-->
<ServiceWorkerInstalledEvent Condition="'$(ServiceWorkerInstalledEvent)'==''">installed</ServiceWorkerInstalledEvent>
<!-- Text to display when an update is available -->
<ServiceWorkerUpdateAlertText Condition="'$(ServiceWorkerUpdateAlertText)'==''">Update available. Reload the page when convenient.</ServiceWorkerUpdateAlertText>
<!-- The Blazor namespace for callbacks -->
<ServiceWorkerBlazorAssembly Condition="'$(ServiceWorkerBlazorAssembly)'==''">$(ProjectName)</ServiceWorkerBlazorAssembly>
<!-- The Blazor method to call when a PWA is installable -->
<ServiceWorkerBlazorInstallMethod Condition="'$(ServiceWorkerBlazorInstallMethod)'==''">PWAInstallable</ServiceWorkerBlazorInstallMethod>
<!-- Setup the declarations for the Service Worker Register -->
<ServiceWorkerRegisterConstants Condition="'$(ServiceWorkerConstants)' == ''">
<ServiceWorkerRegisterConstants Condition="'$(ServiceWorkerRegisterConstants)' == ''">
const serviceWorkerFileName = '$(ServiceWorkerBaseURL)$(ServiceWorkerFileName)'%3B;
const swInstalledEvent = '$(ServiceWorkerInstalledEvent)'%3B;
const staticCachePrefix = '$(ServiceWorkerCacheName)-v'%3B;
const updateAlertMessage = '$(ServiceWorkerUpdateAlertText)'%3B;
</ServiceWorkerRegisterConstants>
<ServiceWorkerRegisterConstants Condition="'$(ServiceWorkerRegisterInstallableType)' == 'installable-blazor'">
$(ServiceWorkerRegisterConstants);
const blazorAssembly = '$(ServiceWorkerBlazorAssembly)'%3B;
const blazorInstallMethod = '$(ServiceWorkerBlazorInstallMethod)'%3B;
</ServiceWorkerRegisterConstants>
</PropertyGroup>
<ItemGroup>
<!-- Read the Service Worker Register template-->
Expand All @@ -45,7 +58,24 @@
<ServiceWorkerRegisterTemplateLines
Condition="Exists('$(ServiceWorkerRegisterInstallableTemplate)')"
Include="$([System.IO.File]::ReadAllText($(ServiceWorkerRegisterInstallableTemplate)))"/>
<!-- Read the Service Worker Register Before Install Prompt template-->
<ServiceWorkerRegisterTemplateLines
Condition="Exists('$(ServiceWorkerRegisterBeforeInstallPromptTemplate)')"
Include="$([System.IO.File]::ReadAllText($(ServiceWorkerRegisterBeforeInstallPromptTemplate)))"/>
</ItemGroup>
<!-- Debugging -->
<Message
Importance="high"
Text="Service Worker Register Template: $(ServiceWorkerRegisterTemplate)"/>
<Message
Importance="high"
Text="Service Worker Update Template: $(ServiceWorkerRegisterUpdateTemplate)"/>
<Message
Importance="high"
Text="Service Worker Installable Template: $(ServiceWorkerRegisterInstallableTemplate)"/>
<Message
Importance="high"
Text="Service Worker Before Install Prompt Template: $(ServiceWorkerRegisterBeforeInstallPromptTemplate)"/>
<!-- (Re)Create the ServiceWorkerRegister.js file -->
<WriteLinesToFile
Condition="'$(ServiceWorkerRegisterConstants)@(ServiceWorkerRegisterTemplateLines)' != ''"
Expand Down
12 changes: 9 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
#### 10/08/2019
#### 10/08/2019 - 13/08/2019

- Added new **Property** **`ServiceWorkerUpdateAlertText`** - used to change the default text in the "Update available alert".
- Added new **Property** **`ServiceWorkerRegisterUpdateType`** - used to select the "Update available alert" type.
- Added new **Property** **`ServiceWorkerRegisterUpdateTemplate`** - The name of the template file for the "update available" event.
- Moved the "Updated available alert" to it's own template so we can have alternates
- Added new **Property** **`ServiceWorkerRegisterInstallableType`** - used to select the "Installable PWA alert" type.
- Added new **Property** **`ServiceWorkerRegisterInstallableTemplate`** - The name of the template file for the "Installable PWA alert" event.
- Added new **Property** **`ServiceWorkerRegisterBeforeInstallPromptType`** - used to select the "Before Install Prompt" type.
- Added new **Property** **`ServiceWorkerRegisterBeforeInstallPromptTemplate`** - The name of the template file for the "Before Install Prompt" event.
- Moved the "Update available alert" to it's own template so we can have alternates
- Moved the "Installable PWA alert" to it's own template so we can have alternates
- Tidied up default install alert
- Moved the "Before Install Prompt" to it's own template so we can have alternates
- Tidied up default install alert a bit
- Added new Installable PWA Alert type - "installable-blazor" which will call a Blazor static method, which is defined by:
- **`ServiceWorkerBlazorAssembly`** which is used to define the Blazor Assembly namespace - defaults to the project name
- **`ServiceWorkerBlazorInstallMethod`** which is used to define the Blazor method to call - defaults to 'PWAInstallable'
- Updated README with Blazor interop example.

#### 08/08/2019 Initial Release
67 changes: 66 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,75 @@ The web manifest has properties for the application name, which are taken, by de

There are dozens of Properties in the *targets* files supplied by this package - you *could* customise them all, but you probably don't need to, so proceed with caution.

### Handing the "Installable PWA" event in Blazor

When a Chromium based browser detects an installable PWA, it fires an event that your application
can use to display a prompt to the user.

The default for this package is to simply display a bar at the bottom of the
browser window prompting the user to install your app.

You can customise the message, or you can override that and pass the event
over to your Blazor application - by setting the property
**`ServiceWorkerRegisterInstallableType`** to **`installable-blazor`** in your **.csproj**

This will generate code in your **ServiceWorkerRegister.js** to make an
interop call to Blazor when the **beforeinstallprompt** event fires in the browser.

In your Blazor application, you will need code to handle this call
- by default it will use the **ProjectName** and method name **"InstallPWA"** to
perform a `DotNet.InvokeAsync()`
- These values can be customised using the properties **`ServiceWorkerBlazorAssembly`**
and **`ServiceWorkerBlazorInstallMethod`**

Here is an example Blazor implementation which could be added to **MainLayout**

``` HTML
@if (Installable)
{
<div class="fixed-bottom w-100 alert alert-dark d-flex align-items-center" @onclick="InstallClicked">
<h3>Install this app?</h3>
<small class="ml-auto mr-1 rounded-pill"><button @onclick="@(()=>Installable=false)">X</button></small>
</div>
}
```
This will display a bar at the bottom of the browser, which can be dismissed or clicked.


The `code` section has the `JSInvokable` method **InstallPWS** that we called
earlier from the browser and some supporting code to toggle the display and
make an interop call back to the browser to trigger the app installation.
``` C#
@code
{
[Inject] IJSRuntime JSRuntime { get; set; }

static bool Installable = false;
static Action ml;
protected override void OnInitialized()
{
ml = () => InvokeAsync(StateHasChanged);
}
[JSInvokable]
public static Task InstallPWA()
{
Installable = true;
ml.Invoke();
return Task.CompletedTask;
}
Task InstallClicked(UIMouseEventArgs args)
{
Installable = false;
return JSRuntime.InvokeAsync<object>("BlazorPWA.installPWA");
}
}
```

## Roadmap

- [ ] At the moment, there is only one choice for caching strategy - Cache First/Network Fallback - I will add more (https://developers.google.com/web/ilt/pwa/introduction-to-progressive-web-app-architectures#caching_strategies_supported_by_sw-toolbox)
- [ ] The current methods for alerting the user are semi-hard coded (you can adjust them manually after generation) - this will change to allow hooks/callbacks into Blazor via project properties
- [x] The current method for alerting the user that the app is installable is semi-hard coded (you can adjust it manually after generation) - this will change to allow hooks/callbacks into Blazor via project properties
- [ ] The current method for alerting the user when an update is available is semi-hard coded (you can adjust it manually after generation) - this will change to allow hooks/callbacks into Blazor via project properties
- [ ] Document all of the configuration Properties (they all have comments in the code - so you are able to understand their purpose without documentation...)
- [ ] Bug fixes

Expand Down

0 comments on commit babddbf

Please sign in to comment.