Added SessionState Components
|
@ -1,7 +1,48 @@
|
||||||
{
|
{
|
||||||
"Version": 1,
|
"Version": 1,
|
||||||
"WorkspaceRootPath": "C:\\Repos\\SummerBestWebForm2\\",
|
"WorkspaceRootPath": "C:\\Repos\\SummerBestWebForm2\\",
|
||||||
"Documents": [],
|
"Documents": [
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\components\\app.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
||||||
|
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:components\\app.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\components\\_imports.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
||||||
|
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:components\\_imports.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\sessionstate\\sessionmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
|
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:sessionstate\\sessionmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\sessionstate\\sessionidmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
|
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:sessionstate\\sessionidmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\classobj\\mdlsessioninfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
|
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:classobj\\mdlsessioninfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\sessionstate\\mdlsession.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
|
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:sessionstate\\mdlsession.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\appstate\\mdlappstate.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
|
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:appstate\\mdlappstate.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\appstate\\cascadingappstate.razor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
|
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:appstate\\cascadingappstate.razor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\appstate\\iappstate.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
|
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:appstate\\iappstate.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
|
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
}
|
||||||
|
],
|
||||||
"DocumentGroupContainers": [
|
"DocumentGroupContainers": [
|
||||||
{
|
{
|
||||||
"Orientation": 0,
|
"Orientation": 0,
|
||||||
|
@ -9,7 +50,7 @@
|
||||||
"DocumentGroups": [
|
"DocumentGroups": [
|
||||||
{
|
{
|
||||||
"DockedWidth": 200,
|
"DockedWidth": 200,
|
||||||
"SelectedChildIndex": -1,
|
"SelectedChildIndex": 11,
|
||||||
"Children": [
|
"Children": [
|
||||||
{
|
{
|
||||||
"$type": "Bookmark",
|
"$type": "Bookmark",
|
||||||
|
@ -54,6 +95,136 @@
|
||||||
{
|
{
|
||||||
"$type": "Bookmark",
|
"$type": "Bookmark",
|
||||||
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
|
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 0,
|
||||||
|
"Title": "App.razor",
|
||||||
|
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\Components\\App.razor",
|
||||||
|
"RelativeDocumentMoniker": "Components\\App.razor",
|
||||||
|
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\Components\\App.razor",
|
||||||
|
"RelativeToolTip": "Components\\App.razor",
|
||||||
|
"ViewState": "AgIAACQAAAAAAAAAAAAYwFwAAAAAAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
||||||
|
"WhenOpened": "2025-05-15T09:23:12.142Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 9,
|
||||||
|
"Title": "Program.cs",
|
||||||
|
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\Program.cs",
|
||||||
|
"RelativeDocumentMoniker": "Program.cs",
|
||||||
|
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\Program.cs",
|
||||||
|
"RelativeToolTip": "Program.cs",
|
||||||
|
"ViewState": "AgIAABMAAAAAAAAAAAAAACsAAAAAAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2025-05-15T10:01:38.124Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 8,
|
||||||
|
"Title": "IAppState.cs",
|
||||||
|
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\AppState\\IAppState.cs",
|
||||||
|
"RelativeDocumentMoniker": "AppState\\IAppState.cs",
|
||||||
|
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\AppState\\IAppState.cs",
|
||||||
|
"RelativeToolTip": "AppState\\IAppState.cs",
|
||||||
|
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2025-05-15T10:03:35.861Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 7,
|
||||||
|
"Title": "CascadingAppState.razor.cs",
|
||||||
|
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\AppState\\CascadingAppState.razor.cs",
|
||||||
|
"RelativeDocumentMoniker": "AppState\\CascadingAppState.razor.cs",
|
||||||
|
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\AppState\\CascadingAppState.razor.cs",
|
||||||
|
"RelativeToolTip": "AppState\\CascadingAppState.razor.cs",
|
||||||
|
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAoAAAA6AAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2025-05-15T10:03:51.372Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 6,
|
||||||
|
"Title": "MdlAppState.cs",
|
||||||
|
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\AppState\\MdlAppState.cs",
|
||||||
|
"RelativeDocumentMoniker": "AppState\\MdlAppState.cs",
|
||||||
|
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\AppState\\MdlAppState.cs",
|
||||||
|
"RelativeToolTip": "AppState\\MdlAppState.cs",
|
||||||
|
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2025-05-15T10:04:02.184Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 5,
|
||||||
|
"Title": "MdlSession.cs",
|
||||||
|
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\SessionState\\MdlSession.cs",
|
||||||
|
"RelativeDocumentMoniker": "SessionState\\MdlSession.cs",
|
||||||
|
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\SessionState\\MdlSession.cs",
|
||||||
|
"RelativeToolTip": "SessionState\\MdlSession.cs",
|
||||||
|
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2025-05-15T10:04:06.197Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 4,
|
||||||
|
"Title": "MdlSessionInfo.cs",
|
||||||
|
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\ClassObj\\MdlSessionInfo.cs",
|
||||||
|
"RelativeDocumentMoniker": "ClassObj\\MdlSessionInfo.cs",
|
||||||
|
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\ClassObj\\MdlSessionInfo.cs",
|
||||||
|
"RelativeToolTip": "ClassObj\\MdlSessionInfo.cs",
|
||||||
|
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2025-05-15T10:04:10.974Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 3,
|
||||||
|
"Title": "SessionIdManager.cs",
|
||||||
|
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\SessionState\\SessionIdManager.cs",
|
||||||
|
"RelativeDocumentMoniker": "SessionState\\SessionIdManager.cs",
|
||||||
|
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\SessionState\\SessionIdManager.cs",
|
||||||
|
"RelativeToolTip": "SessionState\\SessionIdManager.cs",
|
||||||
|
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2025-05-15T10:04:14.473Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 2,
|
||||||
|
"Title": "SessionManager.cs",
|
||||||
|
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\SessionState\\SessionManager.cs",
|
||||||
|
"RelativeDocumentMoniker": "SessionState\\SessionManager.cs",
|
||||||
|
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\SessionState\\SessionManager.cs",
|
||||||
|
"RelativeToolTip": "SessionState\\SessionManager.cs",
|
||||||
|
"ViewState": "AgIAAAIAAAAAAAAAAAAAAAIAAAAcAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2025-05-15T10:04:17.539Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 1,
|
||||||
|
"Title": "_Imports.razor",
|
||||||
|
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\Components\\_Imports.razor",
|
||||||
|
"RelativeDocumentMoniker": "Components\\_Imports.razor",
|
||||||
|
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\Components\\_Imports.razor",
|
||||||
|
"RelativeToolTip": "Components\\_Imports.razor",
|
||||||
|
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAoAAAAiAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
||||||
|
"WhenOpened": "2025-05-15T10:07:58.031Z",
|
||||||
|
"EditorCaption": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
@* @using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
*@
|
||||||
|
<CascadingValue Value="this">
|
||||||
|
@ChildContent
|
||||||
|
</CascadingValue>
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace SummerBestWebForm2.AppState;
|
||||||
|
|
||||||
|
public partial class CascadingAppState : ComponentBase, IAppState
|
||||||
|
{
|
||||||
|
private readonly string StorageKey = "SummerBestWebForm2-MyCarHQ";
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? ChildContent { get; set; }
|
||||||
|
[Inject]
|
||||||
|
ProtectedLocalStorage? localStorage { get; set; }
|
||||||
|
[Inject]
|
||||||
|
IHttpContextAccessor? httpContextAccessor { get; set; }
|
||||||
|
|
||||||
|
bool isLoaded = false;
|
||||||
|
|
||||||
|
public Guid SessionId { get; set; }
|
||||||
|
|
||||||
|
public bool isInit { get; set; }
|
||||||
|
|
||||||
|
//public CascadingAppState()
|
||||||
|
//{
|
||||||
|
// Load();
|
||||||
|
//}
|
||||||
|
|
||||||
|
private string? _myIpAddress = string.Empty;
|
||||||
|
public string? myIpAddress
|
||||||
|
{
|
||||||
|
get => _myIpAddress;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_myIpAddress = value;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DateTimeOffset _DateCreated = DateTimeOffset.Now;
|
||||||
|
public DateTimeOffset DateCreated
|
||||||
|
{
|
||||||
|
get => _DateCreated;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_DateCreated = value;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private DateTimeOffset _DateExpires = DateTimeOffset.Now;
|
||||||
|
public DateTimeOffset DateExpires
|
||||||
|
{
|
||||||
|
get => _DateExpires;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_DateExpires = value;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O)
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
await LoadAsync();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Save()
|
||||||
|
{
|
||||||
|
new Task(async () =>
|
||||||
|
{
|
||||||
|
await SaveAsync();
|
||||||
|
}).Start();
|
||||||
|
} //Save
|
||||||
|
|
||||||
|
public async Task SaveAsync()
|
||||||
|
{
|
||||||
|
if (!isLoaded) return;
|
||||||
|
|
||||||
|
// serialize
|
||||||
|
var state = (IAppState)this;
|
||||||
|
var json = JsonSerializer.Serialize(state);
|
||||||
|
// save
|
||||||
|
await localStorage.SetAsync(StorageKey, json);
|
||||||
|
} //SaveAsync
|
||||||
|
|
||||||
|
private void Load()
|
||||||
|
{
|
||||||
|
new Task(async () =>
|
||||||
|
{
|
||||||
|
await LoadAsync();
|
||||||
|
}).Start();
|
||||||
|
} //Load
|
||||||
|
|
||||||
|
public async Task LoadAsync()
|
||||||
|
{
|
||||||
|
string remoteIpAddr=string.Empty;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
remoteIpAddr = httpContextAccessor.HttpContext.Connection.RemoteIpAddress?.ToString() ?? "";
|
||||||
|
var data = await localStorage.GetAsync<string>(StorageKey);
|
||||||
|
var state = JsonSerializer.Deserialize<MdlAppState>(data.Value);
|
||||||
|
|
||||||
|
if (state != null)
|
||||||
|
{
|
||||||
|
if (DateTimeOffset.Now < state.DateExpires)
|
||||||
|
{
|
||||||
|
// decide whether to set properties manually or with reflection
|
||||||
|
|
||||||
|
// comment to set properties manually
|
||||||
|
//this.Message = state.Message;
|
||||||
|
//this.Count = state.Count;
|
||||||
|
|
||||||
|
// set properties using Reflaction
|
||||||
|
var t = typeof(IAppState);
|
||||||
|
//var props = t.GetProperties();
|
||||||
|
PropertyInfo[] props = t.GetProperties();
|
||||||
|
foreach (var prop in props)
|
||||||
|
{
|
||||||
|
//if (!Regex.IsMatch(prop.Name, "isInit|SessionId|"))
|
||||||
|
{
|
||||||
|
var value = prop.GetValue(state, null);
|
||||||
|
prop.SetValue(this, value, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// do something
|
||||||
|
}
|
||||||
|
isLoaded = true;
|
||||||
|
DateExpires = DateTimeOffset.Now.AddHours(36);
|
||||||
|
myIpAddress = remoteIpAddr;
|
||||||
|
if (!isInit)
|
||||||
|
{
|
||||||
|
DateCreated = DateTimeOffset.Now;
|
||||||
|
SessionId = Guid.NewGuid();
|
||||||
|
isInit = true;
|
||||||
|
await SaveAsync();
|
||||||
|
}
|
||||||
|
await InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
});
|
||||||
|
} //LoadAsync
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace SummerBestWebForm2.AppState;
|
||||||
|
|
||||||
|
public interface IAppState
|
||||||
|
{
|
||||||
|
bool isInit { get; set; }
|
||||||
|
string? myIpAddress { get; set; }
|
||||||
|
Guid SessionId { get; set; }
|
||||||
|
DateTimeOffset DateCreated { get; set; }
|
||||||
|
DateTimeOffset DateExpires { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace SummerBestWebForm2.AppState;
|
||||||
|
|
||||||
|
public class MdlAppState : IAppState
|
||||||
|
{
|
||||||
|
public bool isInit { get; set; }
|
||||||
|
public string? myIpAddress { get; set; }
|
||||||
|
public Guid SessionId { get; set; }
|
||||||
|
public DateTimeOffset DateCreated { get; set; }
|
||||||
|
public DateTimeOffset DateExpires { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace SummerBestWebForm2;
|
||||||
|
|
||||||
|
public class MdlSessionInfo
|
||||||
|
{
|
||||||
|
public const string KEY_IPADDRESS = "IPAddress";
|
||||||
|
public string IPAddress { get; set; } = null!;
|
||||||
|
public bool isInit { get; set; } = false;
|
||||||
|
public DateTimeOffset DateCreated { get; set; } = DateTimeOffset.MinValue;
|
||||||
|
|
||||||
|
public MdlSessionInfo()
|
||||||
|
{
|
||||||
|
// Have to have a blank ctor here for ProtectedSessionStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,20 +1,92 @@
|
||||||
<!DOCTYPE html>
|
@inject IHttpContextAccessor httpContextAccessor
|
||||||
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<base href="/" />
|
<base href="/" />
|
||||||
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
|
<meta name="description" content="KeyMotive's Summer Best Customer Program Enrollment" />
|
||||||
<link rel="stylesheet" href="app.css" />
|
<meta name="author" content="KeyMotive LLC" />
|
||||||
<link rel="stylesheet" href="SummerBestWebForm2.styles.css" />
|
<meta name="Charset" content="US-ASCII" />
|
||||||
<link rel="icon" type="image/png" href="favicon.png" />
|
<meta name="Distribution" content="Global" />
|
||||||
<HeadOutlet @rendermode="InteractiveServer" />
|
<meta name="Rating" content="General" />
|
||||||
|
<meta name="Robots" content="INDEX,FOLLOW" />
|
||||||
|
<meta name="Revisit-after" content="14 Days" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin />
|
||||||
|
<link href='https://fonts.googleapis.com/css?family=Roboto|Handlee|Candal|Exo 2|Armata|Poppins|Kalam:wght@300&display=swap' rel='stylesheet' type='text/css' />
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" integrity="sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" integrity="sha512-SnH5WK+bZxgPHs44uWIX+LLJAJ9/2PkPKZ5QiAj6Ta86w+fsb2TkcmfRyVX3pBnMFcV7oQPJkl9QevSCWr3W6A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
|
<link rel="stylesheet" href="app.css" />
|
||||||
|
<link rel="stylesheet" href="SummerBestWebForm2.styles.css" />
|
||||||
|
<link rel="icon" type="image/png" sizes="512x512" href="/img/favico-512.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="256x256" href="/img/favico-256.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="128x128" href="/img/favico-128.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="64x64" href="/img/favico-64.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/img/favico-32.png">
|
||||||
|
<style type="text/css">
|
||||||
|
div.container {
|
||||||
|
background-color: #fff;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<HeadOutlet @rendermode="InteractiveServer" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<Routes @rendermode="InteractiveServer" />
|
<Routes @rendermode="InteractiveServer" />
|
||||||
<script src="_framework/blazor.web.js"></script>
|
<script src="_framework/blazor.web.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js" integrity="sha512-7Pi/otdlbbCR+LnW+F7PwFcSDJOuUJB3OxtEHbg4vSMvzvJjde4Po1v4BR9Gdc9aXNUNFVUY+SK51wWT8WF0Gg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/js/all.min.js" integrity="sha512-u3fPA7V8qQmhBPNT5quvaXVa1mnnLSXUep5PS1qo5NRzHwG19aHmNJnj1Q8hpA/nBWZtZD4r4AX6YOt5ynLN2g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
<script>
|
||||||
|
window.WriteCookie = {
|
||||||
|
WriteCookie: function (name, value, days) {
|
||||||
|
var expires;
|
||||||
|
if (days) {
|
||||||
|
var date = new Date();
|
||||||
|
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||||
|
expires = "; expires=" + date.toGMTString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
expires = "";
|
||||||
|
}
|
||||||
|
document.cookie = name + "=" + value + expires + "; path=/";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.ReadCookie = {
|
||||||
|
ReadCookie: function (cname) {
|
||||||
|
var name = cname + "=";
|
||||||
|
var decodedCookie = decodeURIComponent(document.cookie);
|
||||||
|
var ca = decodedCookie.split(';');
|
||||||
|
for (var i = 0; i < ca.length; i++) {
|
||||||
|
var c = ca[i];
|
||||||
|
while (c.charAt(0) == ' ') {
|
||||||
|
c = c.substring(1);
|
||||||
|
}
|
||||||
|
if (c.indexOf(name) == 0) {
|
||||||
|
return c.substring(name.length, c.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<pre>@errMsg</pre>
|
||||||
|
@if (appState != null)
|
||||||
|
{
|
||||||
|
<pre style="font-size:.8em">
|
||||||
|
SessionId: @appState.SessionId
|
||||||
|
DateCreated: @appState.DateCreated
|
||||||
|
DateExpires: @appState.DateExpires
|
||||||
|
IPAddress: @appState.myIpAddress
|
||||||
|
</pre>
|
||||||
|
}
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@code {
|
||||||
|
[CascadingParameter]
|
||||||
|
public CascadingAppState appState { get; set; }
|
||||||
|
|
||||||
|
private string errMsg = string.Empty;
|
||||||
|
}
|
||||||
|
|
|
@ -8,3 +8,4 @@
|
||||||
@using Microsoft.JSInterop
|
@using Microsoft.JSInterop
|
||||||
@using SummerBestWebForm2
|
@using SummerBestWebForm2
|
||||||
@using SummerBestWebForm2.Components
|
@using SummerBestWebForm2.Components
|
||||||
|
@using SummerBestWebForm2.AppState
|
||||||
|
|
73
Program.cs
|
@ -1,36 +1,65 @@
|
||||||
|
using Microsoft.AspNetCore.ResponseCompression;
|
||||||
using SummerBestWebForm2.Components;
|
using SummerBestWebForm2.Components;
|
||||||
|
using System.IO.Compression;
|
||||||
|
|
||||||
namespace SummerBestWebForm2
|
namespace SummerBestWebForm2
|
||||||
{
|
{
|
||||||
public class Program
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
{
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddRazorComponents()
|
builder.Services.AddRazorComponents()
|
||||||
.AddInteractiveServerComponents();
|
.AddInteractiveServerComponents();
|
||||||
|
|
||||||
var app = builder.Build();
|
builder.Services.AddResponseCompression(options =>
|
||||||
|
{
|
||||||
|
options.EnableForHttps = true;
|
||||||
|
options.Providers.Add<BrotliCompressionProvider>();
|
||||||
|
options.Providers.Add<GzipCompressionProvider>();
|
||||||
|
});
|
||||||
|
builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
|
||||||
|
{
|
||||||
|
options.Level = CompressionLevel.Fastest;
|
||||||
|
});
|
||||||
|
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
|
||||||
|
{
|
||||||
|
options.Level = CompressionLevel.SmallestSize;
|
||||||
|
});
|
||||||
|
builder.Services.AddDataProtection();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
builder.Services.AddHttpContextAccessor();
|
||||||
if (!app.Environment.IsDevelopment())
|
builder.Services.AddScoped<HttpContextAccessor>();
|
||||||
{
|
|
||||||
app.UseExceptionHandler("/Error");
|
|
||||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
|
||||||
app.UseHsts();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.UseStaticFiles();
|
// Configure the HTTP request pipeline.
|
||||||
app.UseAntiforgery();
|
if (!app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseExceptionHandler("/Error");
|
||||||
|
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||||
|
app.UseHsts();
|
||||||
|
}
|
||||||
|
|
||||||
app.MapRazorComponents<App>()
|
var webSocketOptions = new WebSocketOptions()
|
||||||
.AddInteractiveServerRenderMode();
|
{
|
||||||
|
KeepAliveInterval = TimeSpan.FromSeconds(15)
|
||||||
|
};
|
||||||
|
app.UseWebSockets(webSocketOptions);
|
||||||
|
app.UseResponseCompression();
|
||||||
|
app.UseResponseCaching();
|
||||||
|
|
||||||
app.Run();
|
app.UseHttpsRedirection();
|
||||||
}
|
|
||||||
|
app.UseStaticFiles();
|
||||||
|
app.UseAntiforgery();
|
||||||
|
|
||||||
|
app.MapRazorComponents<App>()
|
||||||
|
.AddInteractiveServerRenderMode();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
namespace SummerBestWebForm2.SessionState;
|
||||||
|
|
||||||
|
public class MdlSession : Dictionary<string, string>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Session Id value.
|
||||||
|
/// </summary>
|
||||||
|
public string SessionId { get; set; } = string.Empty;
|
||||||
|
public string IPAddress { get; set; } = string.Empty;
|
||||||
|
public bool IsCheckedOut { get; set; } = false;
|
||||||
|
public DateTimeOffset dtExpires { get; set; } = DateTimeOffset.Now.AddHours(36);
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
namespace SummerBestWebForm2.SessionState;
|
||||||
|
|
||||||
|
// INTERFACE
|
||||||
|
|
||||||
|
public interface ISessionIdManager
|
||||||
|
{
|
||||||
|
Task<string?> GetSessionIdAsync();
|
||||||
|
Task<string?> GetIPAddressAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// CODE
|
||||||
|
|
||||||
|
public class SessionIdManager(IHttpContextAccessor httpContextAccessor) : ISessionIdManager
|
||||||
|
{
|
||||||
|
private readonly IHttpContextAccessor HttpContextAccessor = httpContextAccessor;
|
||||||
|
|
||||||
|
public Task<string?> GetSessionIdAsync()
|
||||||
|
{
|
||||||
|
var httpContext = HttpContextAccessor.HttpContext;
|
||||||
|
string? result;
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
{
|
||||||
|
if (httpContext.Request.Cookies.ContainsKey("sessionId"))
|
||||||
|
{
|
||||||
|
result = httpContext.Request.Cookies["sessionId"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = Guid.NewGuid().ToString();
|
||||||
|
httpContext.Response.Cookies.Append("sessionId", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("No HttpContext available");
|
||||||
|
}
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string?> GetIPAddressAsync()
|
||||||
|
{
|
||||||
|
var httpContext = HttpContextAccessor.HttpContext;
|
||||||
|
string? result;
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
{
|
||||||
|
result = httpContext.Connection.RemoteIpAddress?.ToString();// ?? "Not Set";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("No HttpContext available");
|
||||||
|
}
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace SummerBestWebForm2.SessionState;
|
||||||
|
|
||||||
|
// INTERFACE
|
||||||
|
|
||||||
|
public interface ISessionManager
|
||||||
|
{
|
||||||
|
Task<MdlSession> GetSession();
|
||||||
|
Task UpdateSession(MdlSession session);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CODE
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dictionary containing per-user session objects, keyed
|
||||||
|
/// by sessionId.
|
||||||
|
/// </summary>
|
||||||
|
public class SessionManager : ISessionManager
|
||||||
|
{
|
||||||
|
private Dictionary<Guid, MdlSession> _sessions = new Dictionary<Guid, MdlSession>();
|
||||||
|
private readonly ISessionIdManager _sessionIdManager;
|
||||||
|
private object syncLock1 = new();
|
||||||
|
|
||||||
|
public SessionManager(ISessionIdManager sessionIdManager)
|
||||||
|
{
|
||||||
|
_sessionIdManager = sessionIdManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MdlSession> GetSession()
|
||||||
|
{
|
||||||
|
string key = await _sessionIdManager.GetSessionIdAsync() ?? "";
|
||||||
|
string ipAddress = await _sessionIdManager.GetIPAddressAsync() ?? "";
|
||||||
|
Guid theSessionId;
|
||||||
|
|
||||||
|
if (!Guid.TryParse(key, out theSessionId))
|
||||||
|
theSessionId = Guid.Empty;//Guid.Parse(key);
|
||||||
|
|
||||||
|
MdlSession session;
|
||||||
|
bool needToCreateNew = false;
|
||||||
|
|
||||||
|
if (!_sessions.ContainsKey(theSessionId))
|
||||||
|
{
|
||||||
|
needToCreateNew = true;
|
||||||
|
}
|
||||||
|
else if (_sessions[theSessionId].dtExpires < DateTimeOffset.Now)
|
||||||
|
needToCreateNew = true;
|
||||||
|
|
||||||
|
if (needToCreateNew)
|
||||||
|
{
|
||||||
|
session = new MdlSession() { SessionId = key, IPAddress = ipAddress };
|
||||||
|
_sessions.Add(theSessionId, new MdlSession());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
session = _sessions[theSessionId];
|
||||||
|
|
||||||
|
// ensure session isn't checked out by wasm
|
||||||
|
//while (session.IsCheckedOut)
|
||||||
|
// await Task.Delay(5);
|
||||||
|
var endTime = DateTime.UtcNow.AddSeconds(10);
|
||||||
|
while (session.IsCheckedOut)
|
||||||
|
{
|
||||||
|
if (DateTime.UtcNow > endTime)
|
||||||
|
throw new TimeoutException();
|
||||||
|
await Task.Delay(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateSession(MdlSession session)
|
||||||
|
{
|
||||||
|
if (session != null)
|
||||||
|
{
|
||||||
|
string key = await _sessionIdManager.GetSessionIdAsync() ?? "";
|
||||||
|
string ipAddress = await _sessionIdManager.GetIPAddressAsync() ?? "";
|
||||||
|
Guid theSessionId;
|
||||||
|
|
||||||
|
if (!Guid.TryParse(key, out theSessionId))
|
||||||
|
theSessionId = Guid.Empty;//Guid.Parse(key);
|
||||||
|
|
||||||
|
if (_sessions.ContainsKey(theSessionId))
|
||||||
|
{
|
||||||
|
//session = new MdlSession() { SessionId = key, IPAddress = ipAddress };
|
||||||
|
session.SessionId = key;
|
||||||
|
session.dtExpires = DateTimeOffset.Now.AddHours(36); // Rolling expiration date
|
||||||
|
_sessions[theSessionId] = session;
|
||||||
|
_sessions[theSessionId].IsCheckedOut = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var sess = new MdlSession() { SessionId = key, IPAddress = ipAddress };
|
||||||
|
_sessions[theSessionId] = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove stale sessions
|
||||||
|
lock (syncLock1)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var lstRemove = new HashSet<Guid>();
|
||||||
|
foreach (var kvp in _sessions)
|
||||||
|
{
|
||||||
|
if (kvp.Value.dtExpires < DateTimeOffset.Now && !lstRemove.Contains(kvp.Key))
|
||||||
|
lstRemove.Add(kvp.Key);
|
||||||
|
}
|
||||||
|
foreach (var rmv in lstRemove)
|
||||||
|
_sessions.Remove(rmv);
|
||||||
|
lstRemove.Clear();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// do something
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //UpdateSession
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replace the contents of oldSession with the items
|
||||||
|
/// in newSession.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="newSession"></param>
|
||||||
|
/// <param name="oldSession"></param>
|
||||||
|
private void Replace(MdlSession newSession, MdlSession oldSession)
|
||||||
|
{
|
||||||
|
oldSession.Clear();
|
||||||
|
foreach (var key in newSession.Keys)
|
||||||
|
oldSession.Add(key, newSession[key]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ using System.Reflection;
|
||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("SummerBestWebForm2")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("SummerBestWebForm2")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+92bc4657300f745b2cfa9a7b2055bd3fd4591564")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("SummerBestWebForm2")]
|
[assembly: System.Reflection.AssemblyProductAttribute("SummerBestWebForm2")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("SummerBestWebForm2")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("SummerBestWebForm2")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
98a44021c4ce1eb513aa177c5c56797f58eab237be1d5eb9823389d6970b0f70
|
17e471ad6ef47e2a4bd7241cad398f501e58c3d66500f6f4af29608363c718b3
|
||||||
|
|
|
@ -20,6 +20,10 @@ build_property._RazorSourceGeneratorDebug =
|
||||||
build_property.EffectiveAnalysisLevelStyle = 8.0
|
build_property.EffectiveAnalysisLevelStyle = 8.0
|
||||||
build_property.EnableCodeStyleSeverity =
|
build_property.EnableCodeStyleSeverity =
|
||||||
|
|
||||||
|
[C:/Repos/SummerBestWebForm2/AppState/CascadingAppState.razor]
|
||||||
|
build_metadata.AdditionalFiles.TargetPath = QXBwU3RhdGVcQ2FzY2FkaW5nQXBwU3RhdGUucmF6b3I=
|
||||||
|
build_metadata.AdditionalFiles.CssScope =
|
||||||
|
|
||||||
[C:/Repos/SummerBestWebForm2/Components/App.razor]
|
[C:/Repos/SummerBestWebForm2/Components/App.razor]
|
||||||
build_metadata.AdditionalFiles.TargetPath = Q29tcG9uZW50c1xBcHAucmF6b3I=
|
build_metadata.AdditionalFiles.TargetPath = Q29tcG9uZW50c1xBcHAucmF6b3I=
|
||||||
build_metadata.AdditionalFiles.CssScope =
|
build_metadata.AdditionalFiles.CssScope =
|
||||||
|
|
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 50 KiB |