Skip to content

Commit 7d85539

Browse files
committed
Introduce IsOffline
1 parent 7d142d2 commit 7d85539

4 files changed

Lines changed: 61 additions & 22 deletions

File tree

README.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,20 +74,27 @@ example
7474
### CachedRequest
7575
You can give optional settings to the CachedRequest object.
7676

77-
* OneCallPerSession - If true, the result will be returned from sessionstorage if it is not expired. Default: false
78-
* OneCallPerCache - If true, the result will be returned from localstorage if it is not expired. Default: false
79-
* IgnoreCache - If true, never return a cached result. Default: false
80-
* ExpireLocalStorage - The time to expire the result in localstorage. Default: 7 days.
81-
* ExpireSessionStorage - The time to expire the result in sessionstorage. Default: 15 minutes.
82-
* CachedAndReplace - If true, The cached result will be returned and the cache will be refreshed for the next call. Default: false
83-
* RetryOnJsonException - If true, If a JSON exception occurs, the cache will be cleared and the request will be retried. Default: true
77+
* **OneCallPerSession** - If true, the result will be returned from sessionstorage if it is not expired. *Default: false*
78+
* **OneCallPerCache** - If true, the result will be returned from localstorage if it is not expired. *Default: false*
79+
* **IgnoreCache** - If true, never return a cached result. *Default: false*
80+
* **ExpireLocalStorage** - The time to expire the result in localstorage. *Default: 7 days.*
81+
* **ExpireSessionStorage** - The time to expire the result in sessionstorage. *Default: 15 minutes.*
82+
* **CachedAndReplace** - If true, The cached result will be returned and the cache will be refreshed for the next call. *Default: false*
83+
* **AlwaysCacheWhenOffline** - If true, the cached result will be returned when offline, except when IgnoreCache is true. *Default: false*
84+
* **RetryOnJsonException** - If true, If a JSON exception occurs, the cache will be cleared and the request will be retried. *Default: true*
8485

8586
### Global settings
8687

8788
On, for example, MainLayout.razor.cs, you can set the Postfix to be used for all requests. This is useful if you have multiple users using the same app from the same browser.
8889

8990
`ExpireStorageService.Postfix = userId.ToString();`
9091

92+
ExpireStorageService knows two properties to monitor if the app is offline.
93+
94+
IsOffline is true when the last request had an `HttpRequestException`.
95+
96+
`ExpireStorageService.IsOffline` and `ExpireStorageService.IsOfflineChanged`
97+
9198
### ICacheableResponse
9299

93100
If a response object implements ICacheableResponse, the FromCache property will be set to true if the result was retrieved from cache.

Src/Drogecode.Blazor.ExpireStorage/Drogecode.Blazor.ExpireStorage.csproj

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
<Project Sdk="Microsoft.NET.Sdk.Razor">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net9.0;net10.0</TargetFrameworks>
4+
<TargetFramework>net10.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<RootNamespace>Drogecode.Blazor.ExpireStorage</RootNamespace>
8-
<Version>0.1.2</Version>
8+
<Version>0.2.0</Version>
99
<Title>Drogecode.Blazor.ExpireStorage</Title>
1010
<Authors>Taco Droogers</Authors>
1111
<PackageProjectUrl>https://github.com/Drogecode/Drogecode.Blazor.ExpireStorage</PackageProjectUrl>
1212
<RepositoryUrl>https://github.com/Drogecode/Drogecode.Blazor.ExpireStorage</RepositoryUrl>
1313
<Description>Adds a wrapper on top of Blazored.LocalStorage and Blazored.SessionStorage to expire items from localstorage and sessionstorage after a specified time.</Description>
1414
<PackageReadmeFile>README.md</PackageReadmeFile>
1515
<PackageLicenseFile>LICENSE</PackageLicenseFile>
16+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
17+
<NoWarn>$(NoWarn);1591</NoWarn>
18+
</PropertyGroup>
19+
20+
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
21+
<DocumentationFile>bin\Debug\Drogecode.Blazor.ExpireStorage.xml</DocumentationFile>
22+
</PropertyGroup>
23+
24+
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
25+
<DocumentationFile>bin\Release\Drogecode.Blazor.ExpireStorage.xml</DocumentationFile>
1626
</PropertyGroup>
1727

1828
<ItemGroup>
@@ -28,11 +38,6 @@
2838
<PrivateAssets>all</PrivateAssets>
2939
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3040
</PackageReference>
31-
</ItemGroup>
32-
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
33-
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="9.0.10" />
34-
</ItemGroup>
35-
<ItemGroup Condition=" '$(TargetFramework)' == 'net10.0' ">
3641
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="10.0.0" />
3742
</ItemGroup>
3843

Src/Drogecode.Blazor.ExpireStorage/Models/CachedRequest.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ public class CachedRequest
3131
/// Return cached but also call for update
3232
/// </summary>
3333
public bool CachedAndReplace { get; set; }
34+
35+
/// <summary>
36+
/// Always return the cached response when offline, except when IgnoreCache is true.
37+
/// </summary>
38+
public bool AlwaysCacheWhenOffline { get; set; }
3439

3540
/// <summary>
3641
/// Retry once on JsonException.

Src/Drogecode.Blazor.ExpireStorage/Services/ExpireStorageService.cs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,36 @@
11
using System.Diagnostics.CodeAnalysis;
22
using System.Text.Json;
33
using Drogecode.Blazor.ExpireStorage.Helpers;
4+
using Microsoft.AspNetCore.Components;
45

56
namespace Drogecode.Blazor.ExpireStorage;
67

78
public class ExpireStorageService : IExpireStorageService
89
{
9-
private readonly ILocalStorageExpireService _localStorageExpireService;
10+
private readonly ILocalStorageExpireService _localStorageExpireService;
1011
private readonly ISessionExpireService _sessionStorageExpireService;
1112

1213
/// <summary>
1314
/// String to postfix to the key in case multiple users can use the app from the same browser.
1415
/// </summary>
1516
public static string? Postfix { get; set; }
1617

18+
public static EventCallback<bool> IsOfflineChanged { get; set; }
19+
20+
/// <summary>
21+
/// True if the last request failed.
22+
/// </summary>
23+
public static bool IsOffline
24+
{
25+
get => field;
26+
private set
27+
{
28+
if (field == value) return;
29+
field = value;
30+
IsOfflineChanged.InvokeAsync(field);
31+
}
32+
}
33+
1734
public static bool LogToConsole
1835
{
1936
get => ConsoleHelper.LogToConsole;
@@ -29,9 +46,9 @@ public ExpireStorageService(
2946
}
3047

3148
public async Task<TRes?> CachedRequestAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TRes>(
32-
string cacheKey,
33-
Func<Task<TRes>> function,
34-
CachedRequest? request = null,
49+
string cacheKey,
50+
Func<Task<TRes>> function,
51+
CachedRequest? request = null,
3552
CancellationToken clt = default)
3653
{
3754
request ??= new CachedRequest();
@@ -41,14 +58,15 @@ public ExpireStorageService(
4158
{
4259
cacheKey += $"__{Postfix}";
4360
}
61+
4462
if (clt.IsCancellationRequested) return default;
45-
if (request.CachedAndReplace)
63+
if (request.CachedAndReplace && !(IsOffline && request.AlwaysCacheWhenOffline))
4664
{
4765
var requestCopy = request;
4866
_ = Task.Run(async () => await RunSaveAndReturn(cacheKey, function, requestCopy, clt), clt);
4967
}
5068

51-
if ((request.CachedAndReplace || request.OneCallPerSession) && !request.IgnoreCache)
69+
if ((request.CachedAndReplace || request.OneCallPerSession || (IsOffline && request.AlwaysCacheWhenOffline)) && !request.IgnoreCache)
5270
{
5371
var sessionResult = await _sessionStorageExpireService.GetItemAsync<TRes>(cacheKey, clt);
5472
if (sessionResult is not null)
@@ -57,11 +75,12 @@ public ExpireStorageService(
5775
{
5876
response.FromCache = true;
5977
}
78+
6079
return sessionResult;
6180
}
6281
}
6382

64-
if ((request.CachedAndReplace || request.OneCallPerCache) && !request.IgnoreCache)
83+
if ((request.CachedAndReplace || request.OneCallPerCache || (IsOffline && request.AlwaysCacheWhenOffline)) && !request.IgnoreCache)
6584
{
6685
var cacheResult = await _localStorageExpireService.GetItemAsync<TRes?>(cacheKey, clt);
6786
if (cacheResult is not null)
@@ -70,6 +89,7 @@ public ExpireStorageService(
7089
{
7190
response.FromCache = true;
7291
}
92+
7393
return cacheResult;
7494
}
7595
}
@@ -82,6 +102,7 @@ public ExpireStorageService(
82102
catch (HttpRequestException)
83103
{
84104
ConsoleHelper.WriteLine("a HttpRequestException");
105+
IsOffline = true;
85106
}
86107
catch (TaskCanceledException)
87108
{
@@ -110,6 +131,7 @@ public ExpireStorageService(
110131
catch (HttpRequestException)
111132
{
112133
ConsoleHelper.WriteLine("b HttpRequestException");
134+
IsOffline = true;
113135
}
114136
catch (TaskCanceledException)
115137
{
@@ -119,7 +141,6 @@ public ExpireStorageService(
119141
// The object definition could be changed with an update. Deleting the old version and retrying again to get the latest version.
120142
ConsoleHelper.WriteLine($"JsonException for {cacheKey}, Deleting");
121143
await _localStorageExpireService.DeleteItemAsync(cacheKey, clt);
122-
request ??= new CachedRequest();
123144
if (request.RetryOnJsonException) // Only retry once
124145
{
125146
ConsoleHelper.WriteLine($"Retry calling {cacheKey}");
@@ -143,6 +164,7 @@ private async Task<TRes> RunSaveAndReturn<TRes>(string cacheKey, Func<Task<TRes>
143164
await _localStorageExpireService.SetItemAsync(cacheKey, result, request.ExpireLocalStorage, clt);
144165
if (request.OneCallPerSession)
145166
await _sessionStorageExpireService.SetItemAsync(cacheKey, result, request.ExpireSession, clt);
167+
IsOffline = false;
146168
return result;
147169
}
148170
}

0 commit comments

Comments
 (0)