Skip to content

Commit edc3bf8

Browse files
Add project files.
1 parent 7815f15 commit edc3bf8

16 files changed

+651
-0
lines changed

Blazor.PWA.MSBuild-NuGet-Icon.pdn

41.4 KB
Binary file not shown.

Blazor.PWA.MSBuild-NuGet-Icon.png

12.8 KB
Loading
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard1.6</TargetFramework>
5+
<!-- Suppresses the warnings about the package not having assemblies in lib/*/.dll.-->
6+
<NoPackageAnalysis>true</NoPackageAnalysis>
7+
<!-- Change the default location where NuGet will put the build output -->
8+
<BuildOutputTargetFolder>tasks</BuildOutputTargetFolder>
9+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
10+
<Authors>Mister Magoo</Authors>
11+
<Copyright>2019 SQL-MisterMagoo</Copyright>
12+
<DevelopmentDependency>true</DevelopmentDependency>
13+
<Description>The easiest way to turn your Client Side Blazor application into a PWA with offline capabilities.
14+
Add this package to your build process and it will generate the files you need to become PWA compatible.
15+
This does not make your application ready for distribution through "App Stores" - it just provides basic PWA functionality.</Description>
16+
<PackageId>Blazor.PWA.MSBuild</PackageId>
17+
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
18+
<PackageLicenseExpression></PackageLicenseExpression>
19+
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
20+
<PackageProjectUrl>https://github.com/SQL-MisterMagoo/Blazor.PWA.MSBuild/src/Blazor.PWA.MSBuild</PackageProjectUrl>
21+
<PackageTags>Blazor,Build,MSBuild,PWA,Manifest,ServiceWorker,C#,DotNET,Web,Client</PackageTags>
22+
<Product>Blazor.PWA.MSBuild</Product>
23+
<RepositoryUrl>https://github.com/SQL-MisterMagoo/Blazor.PWA.MSBuild</RepositoryUrl>
24+
<PackageIconUrl>https://github.com/SQL-MisterMagoo/Blazor.PWA.MSBuild/src/Blazor.PWA.MSBuild/Blazor.PWA.MSBuild-NuGet-Icon.png</PackageIconUrl>
25+
<RepositoryType>git</RepositoryType>
26+
<PackageReleaseNotes>This is a beta release of a very basic PWA build target.
27+
Just by including this in the build, it can generate the basic requirements for an installable PWA for client side Blazor.
28+
The result is not an App Store package, it is simply the basic requirements for PWA.
29+
I will add more network caching strategies, but for now it has just one - cache all local assets.</PackageReleaseNotes>
30+
</PropertyGroup>
31+
32+
<ItemGroup>
33+
<PackageReference Update="@(PackageReference)" PrivateAssets="All" />
34+
</ItemGroup>
35+
36+
<ItemGroup>
37+
<None Include="..\LICENSE.txt;..\README.md;">
38+
<Pack>True</Pack>
39+
<PackagePath></PackagePath>
40+
</None>
41+
</ItemGroup>
42+
43+
<ItemGroup>
44+
<Content Include="build\Blazor.PWA.MSBuild.Manifest.targets">
45+
<PackagePath>build\</PackagePath>
46+
<Pack>True</Pack>
47+
</Content>
48+
<Content Include="build\Blazor.PWA.MSBuild.ServiceWorker.targets">
49+
<PackagePath>build\</PackagePath>
50+
<Pack>True</Pack>
51+
</Content>
52+
<Content Include="build\Blazor.PWA.MSBuild.ServiceWorkerRegister.targets">
53+
<PackagePath>build\</PackagePath>
54+
<Pack>True</Pack>
55+
</Content>
56+
<Content Include="build\Blazor.PWA.MSBuild.targets">
57+
<PackagePath>build\</PackagePath>
58+
<Pack>True</Pack>
59+
</Content>
60+
<Content Include="Templates\**\*.js">
61+
<Pack>True</Pack>
62+
<PackagePath>Templates\</PackagePath>
63+
</Content>
64+
<Content Include="Templates\Images\**\*.*">
65+
<Pack>True</Pack>
66+
<PackagePath>Templates\</PackagePath>
67+
</Content>
68+
69+
</ItemGroup>
70+
71+
<ItemGroup>
72+
<Folder Include="Templates\Images\" />
73+
</ItemGroup>
74+
75+
</Project>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<Project>
2+
<PropertyGroup>
3+
<VersionPrefix>0.0.1</VersionPrefix>
4+
<VersionSuffix>beta$([System.DateTime]::Now.ToString("yyyyMMdd-HHmmss"))</VersionSuffix>
5+
<VersionSuffix Condition="'$(Configuration)' == 'Release'">beta$([System.DateTime]::Now.ToString("yyyyMMdd-HH"))</VersionSuffix>
6+
</PropertyGroup>
7+
</Project>
Loading
Loading
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
self.addEventListener(networkFetchEvent, event => {
2+
const requestUrl = new URL(event.request.url);
3+
if (requestUrl.origin === location.origin) {
4+
if (requestUrl.pathname === baseURL) {
5+
event.respondWith(caches.match(indexURL));
6+
return;
7+
}
8+
}
9+
event.respondWith(
10+
caches.match(event.request)
11+
.then(response => {
12+
if (response) {
13+
return response;
14+
}
15+
return fetch(event.request)
16+
.then(response => {
17+
if (response.ok) {
18+
if (requestUrl.origin === location.origin) {
19+
caches.open(staticCacheName).then(cache => {
20+
cache.put(event.request.url, response);
21+
});
22+
}
23+
}
24+
return response.clone();
25+
});
26+
}).catch(error => {
27+
console.error(error);
28+
})
29+
);
30+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// * listen for the install event and pre-cache anything in filesToCache * //
2+
self.addEventListener(swInstallEvent, event => {
3+
self.skipWaiting();
4+
event.waitUntil(
5+
caches.open(staticCacheName)
6+
.then(cache => {
7+
return cache.addAll(requiredFiles);
8+
})
9+
);
10+
});
11+
self.addEventListener(swActivateEvent, function (event) {
12+
event.waitUntil(
13+
caches.keys().then(function (cacheNames) {
14+
return Promise.all(
15+
cacheNames.map(function (cacheName) {
16+
if (staticCacheName !== cacheName && cacheName.startsWith(staticCachePrefix)) {
17+
return caches.delete(cacheName);
18+
}
19+
})
20+
);
21+
})
22+
);
23+
});
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
window.updateAvailable = new Promise(function (resolve, reject) {
2+
if ('serviceWorker' in navigator) {
3+
navigator.serviceWorker.register(serviceWorkerFileName)
4+
.then(function (registration) {
5+
console.log('Registration successful, scope is:', registration.scope);
6+
registration.onupdatefound = () => {
7+
const installingWorker = registration.installing;
8+
installingWorker.onstatechange = () => {
9+
switch (installingWorker.state) {
10+
case swInstalledEvent:
11+
if (navigator.serviceWorker.controller) {
12+
resolve(true);
13+
} else {
14+
resolve(false);
15+
}
16+
break;
17+
default:
18+
}
19+
};
20+
};
21+
})
22+
.catch(error =>
23+
console.log('Service worker registration failed, error:', error));
24+
}
25+
});
26+
window['updateAvailable']
27+
.then(isAvailable => {
28+
if (isAvailable) {
29+
alert("Update available. Reload the page when convenient.");
30+
}
31+
});
32+
33+
window.addEventListener('beforeinstallprompt', function (e) {
34+
// Prevent Chrome 67 and earlier from automatically showing the prompt
35+
e.preventDefault();
36+
// Stash the event so it can be triggered later.
37+
window.PWADeferredPrompt = e;
38+
39+
showAddToHomeScreen();
40+
41+
});
42+
43+
function showAddToHomeScreen() {
44+
var pwaInstallPrompt = document.createElement('div');
45+
var pwaInstallButton = document.createElement('button');
46+
var pwaCancelButton = document.createElement('button');
47+
48+
pwaInstallPrompt.id = 'pwa-install-prompt';
49+
pwaInstallPrompt.style.position = 'absolute';
50+
pwaInstallPrompt.style.bottom = '0';
51+
pwaInstallPrompt.style.display = 'flex';
52+
pwaInstallPrompt.style.width = '100vw';
53+
pwaInstallPrompt.style.backgroundColor='darkslategrey';
54+
pwaInstallPrompt.style.color='white';
55+
pwaInstallPrompt.style.fontSize='2rem';
56+
57+
pwaInstallButton.style.marginLeft='auto';
58+
pwaInstallButton.style.width='4em';
59+
pwaInstallButton.style.backgroundColor='green';
60+
pwaInstallButton.style.color='white';
61+
62+
pwaCancelButton.style.marginLeft='0.3rem';
63+
pwaCancelButton.style.backgroundColor='darkslategray';
64+
pwaCancelButton.style.color='white';
65+
66+
pwaInstallPrompt.innerText = 'Add to your homescreen!';
67+
pwaInstallButton.innerText = 'OK';
68+
pwaCancelButton.innerText = 'Ignore';
69+
70+
pwaInstallPrompt.appendChild(pwaInstallButton);
71+
pwaInstallPrompt.appendChild(pwaCancelButton);
72+
document.body.appendChild(pwaInstallPrompt);
73+
74+
pwaInstallButton.addEventListener('click', addToHomeScreen);
75+
pwaCancelButton.addEventListener('click', hideAddToHomeScreen);
76+
setTimeout(hideAddToHomeScreen, 10000);
77+
}
78+
79+
function hideAddToHomeScreen() {
80+
var pwa = document.getElementById('pwa-install-prompt');
81+
if (pwa) document.body.removeChild(pwa);
82+
}
83+
84+
function addToHomeScreen(s, e) {
85+
hideAddToHomeScreen();
86+
if (window.PWADeferredPrompt) {
87+
window.PWADeferredPrompt.prompt();
88+
window.PWADeferredPrompt.userChoice
89+
.then(function (choiceResult) {
90+
window.PWADeferredPrompt = null;
91+
});
92+
}
93+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<Project>
2+
<Target Name="BuildServiceWorkerManifest" Condition="!Exists('$(WWWRoot)$(ManifestFileName)') Or '$(ManifestForce)'=='true'">
3+
<!-- bit of info in the build output -->
4+
<Message Importance="high" Text="Building Manifest $(ManifestFileName)" Condition="!Exists('$(WWWRoot)$(ManifestFileName)')"/>
5+
<Message Importance="high" Text="Re-Building Manifest $(ManifestFileName)" Condition="Exists('$(WWWRoot)$(ManifestFileName)')"/>
6+
7+
<PropertyGroup Label="Manifest">
8+
9+
<!-- A manifest needs names - if none were provided, use the Solution/Project names -->
10+
<ManifestShortName Condition="'$(ManifestShortName)' == ''">$(SolutionName)</ManifestShortName>
11+
<ManifestLongName Condition="'$(ManifestLongName)' == ''">$(ProjectName)</ManifestLongName>
12+
13+
<!-- Base URL for the app -->
14+
<ManifestBaseUrl Condition="'$(ManifestBaseUrl)' == ''">/</ManifestBaseUrl>
15+
16+
<!-- Please see https://developers.google.com/web/fundamentals/web-app-manifest/#display for options -->
17+
<ManifestDisplay Condition="'$(ManifestDisplay)' == ''">standalone</ManifestDisplay>
18+
19+
<!-- Icons to use if none are supplierd - manifest requires 192x192 and 512x512 -->
20+
<ManifestTemplatePath Condition="'$(ManifestTemplatePath)' == ''">$(MSBuildThisFileDirectory)..\Templates\</ManifestTemplatePath>
21+
<ManifestDefaultIcon192 Condition="'$(ManifestDefaultIcon192)' == ''">$(ManifestTemplatePath)default-icon-192x192.png</ManifestDefaultIcon192>
22+
<ManifestDefaultIcon512 Condition="'$(ManifestDefaultIcon512)' == ''">$(ManifestTemplatePath)default-icon-512x512.png</ManifestDefaultIcon512>
23+
</PropertyGroup>
24+
25+
26+
27+
<!-- Find required icons @ 192x192 and 512x512 -->
28+
<ItemGroup>
29+
<ImageFiles Include="$(ServiceWorkerPreCacheImageFiles);" />
30+
<IconFiles192 Include="$(WWWRoot)**\*icon*192*.*;">
31+
<Size>192x192</Size>
32+
</IconFiles192>
33+
<IconFiles512 Include="$(WWWRoot)**\*icon*512*.*;">
34+
<Size>512x512</Size>
35+
</IconFiles512>
36+
</ItemGroup>
37+
38+
<!-- Use defualt icons if none were provided as they are required for PWA -->
39+
<Copy Condition="'@(IconFiles192)' == ''"
40+
SourceFiles="$(ManifestDefaultIcon192)"
41+
DestinationFolder="$(WWWRoot)"/>
42+
<Copy Condition="'@(IconFiles512)' == ''"
43+
SourceFiles="$(ManifestDefaultIcon512)"
44+
DestinationFolder="$(WWWRoot)"/>
45+
46+
<!-- Now we should definitely have the icons we need -->
47+
<ItemGroup>
48+
<IconFiles Include="$(WWWRoot)**\*icon*192*.*;">
49+
<Size>192x192</Size>
50+
</IconFiles>
51+
<IconFiles Include="$(WWWRoot)**\*icon*512*.*;">
52+
<Size>512x512</Size>
53+
</IconFiles>
54+
<ManifestText Include="{
55+
&quot;short_name&quot;: &quot;$(ManifestShortName)&quot;,
56+
&quot;name&quot;: &quot;$(ManifestLongName)&quot;,
57+
&quot;start_url&quot;: &quot;$(ManifestBaseUrl)&quot;,
58+
&quot;display&quot;: &quot;$(ManifestDisplay)&quot;,
59+
"/>
60+
<ManifestText Include="%20%20&quot;icons&quot;: ["/>
61+
<ManifestText Include="@(IconFiles-> ' {
62+
&quot;src&quot;:&quot;$(ManifestBaseUrl)%(RecursiveDir)%(Filename)%(Extension)&quot;,
63+
&quot;type&quot;:&quot;image/%(Extension)&quot;,
64+
&quot;sizes&quot;:&quot;%(Size)&quot;
65+
}'->Replace('/.','/')->Replace('\','/'),',%0D%0A');%20%20];}"/>
66+
</ItemGroup>
67+
68+
<!-- Write the manifest -->
69+
<WriteLinesToFile File="$(WWWRoot)$(ManifestFileName)" Overwrite="true" Lines="@(ManifestText);" />
70+
71+
<!-- Add manifest to index.html -->
72+
<PropertyGroup>
73+
<IndexFile>$(WWWRoot)$(ServiceWorkerIndexUrl)</IndexFile>
74+
<IndexLines>$([System.IO.File]::ReadAllText($(IndexFile)))</IndexLines>
75+
</PropertyGroup>
76+
77+
<Message Importance="high" Text="Adding manifest to $(IndexFile)"
78+
Condition="'$(IndexLines.Contains(rel=&quot;manifest&quot;))'=='false'"/>
79+
80+
<WriteLinesToFile
81+
File="$(IndexFile)"
82+
Overwrite="true"
83+
Lines="$(IndexLines.Replace('&lt;/head&gt;',' &lt;link href=&quot;/$(ManifestFileName)&quot; rel=&quot;manifest&quot;/&gt;%0D%0A&lt;/head&gt;'))"
84+
Condition="'$(IndexLines.Contains(rel=&quot;manifest&quot;))'=='false'"/>
85+
</Target>
86+
</Project>

0 commit comments

Comments
 (0)