Save
This commit is contained in:
parent
ba4a4b14c8
commit
5cba76554a
4
.gitignore
vendored
4
.gitignore
vendored
@ -360,4 +360,6 @@ MigrationBackup/
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
FodyWeavers.xsd
|
||||
/.idea/.idea.decePubClient/.idea
|
||||
/.idea/config
|
||||
|
39
App.razor
39
App.razor
@ -1,12 +1,27 @@
|
||||
<Router AppAssembly="@typeof(App).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
|
||||
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||
</Found>
|
||||
<NotFound>
|
||||
<PageTitle>Not found</PageTitle>
|
||||
<LayoutView Layout="@typeof(MainLayout)">
|
||||
<p role="alert">Sorry, there's nothing at this address.</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
<CascadingState>
|
||||
<CascadingAuthenticationState>
|
||||
<Router AppAssembly="@typeof(App).Assembly">
|
||||
<Found Context="routeData">
|
||||
<AuthorizeRouteView DefaultLayout="@typeof(MainLayout)" RouteData="@routeData">
|
||||
<NotAuthorized>
|
||||
@if (context.User.Identity?.IsAuthenticated != true)
|
||||
{
|
||||
<RedirectToLogin/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p role="alert">You are not authorized to access this resource.</p>
|
||||
}
|
||||
</NotAuthorized>
|
||||
</AuthorizeRouteView>
|
||||
<FocusOnNavigate RouteData="@routeData" Selector="section"/>
|
||||
</Found>
|
||||
<NotFound>
|
||||
<Title>Not found</Title>
|
||||
<LayoutView Layout="@typeof(MainLayout)">
|
||||
<p role="alert">Sorry, there's nothing at this address.</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
</CascadingAuthenticationState>
|
||||
</CascadingState>
|
@ -1,5 +1,106 @@
|
||||
<h3>ActionBar</h3>
|
||||
<section class="flex flex-col space-y-4">
|
||||
|
||||
@if (isPosting)
|
||||
{
|
||||
<div class="block p-3 md:p-4 neomorph rounded-xl is-nxsmall">
|
||||
<MessageUpsertForm OnMessageSubmit="OnMessageSubmit" />
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex justify-between space-x-3">
|
||||
|
||||
<EditForm Model="Filters">
|
||||
<div class="field flex flex-row space-x-3">
|
||||
@if (OnTimelineChanged.HasDelegate)
|
||||
{
|
||||
<div class="control has-icons-left">
|
||||
<span class="icon is-left">
|
||||
<i class="@Filters.TimelineType.GetTimelineTypeIcon()"></i>
|
||||
</span>
|
||||
<span class="select is-rounded neoSelect">
|
||||
<InputSelect TValue="TimelineType" Value="Filters.TimelineType"
|
||||
ValueChanged="async v => await OnTimelineChange(v)" ValueExpression="() => Filters.TimelineType">
|
||||
@foreach (var timelineType in Enum.GetValues<TimelineType>())
|
||||
{
|
||||
<option value="@timelineType">@CascadingState.Localizer[timelineType.ToString()]</option>
|
||||
}
|
||||
</InputSelect>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
@if (OnTimeSortingChanged.HasDelegate)
|
||||
{
|
||||
<div class="control has-icons-left">
|
||||
<span class="icon is-left">
|
||||
<i class="ion-md-funnel"></i>
|
||||
</span>
|
||||
<span class="select is-rounded neoSelect">
|
||||
<InputSelect TValue="TimeSortingType" Value="Filters.TimeSortingType"
|
||||
ValueChanged="async v => await OnTimeSortChange(v)" ValueExpression="() => Filters.TimeSortingType">
|
||||
@foreach (var timeSortingType in Enum.GetValues<TimeSortingType>())
|
||||
{
|
||||
<option value="@timeSortingType">@CascadingState.Localizer[timeSortingType.ToString()]</option>
|
||||
}
|
||||
</InputSelect>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</EditForm>
|
||||
|
||||
@if (OnMessageSubmit.HasDelegate)
|
||||
{
|
||||
<div>
|
||||
<button class="button is-rounded @SUtility.IfTrueThen(isPosting, "neoBtnInsetPlain", "neoBtn")"
|
||||
@onclick="OpenCloseMessageForm">
|
||||
<span class="icon is-left">
|
||||
<i class="ion-md-create"></i>
|
||||
</span>
|
||||
<span>@CascadingState.Localizer["Post"]</span>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div></div>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
@code {
|
||||
|
||||
[CascadingParameter] CascadingState CascadingState { get; set; }
|
||||
[Parameter] public EventCallback<MessageForm> OnMessageSubmit { get; set; }
|
||||
[Parameter] public EventCallback<TimelineType> OnTimelineChanged { get; set; }
|
||||
[Parameter] public EventCallback<TimeSortingType> OnTimeSortingChanged { get; set; }
|
||||
[Inject] ILocalStorageService LocalStorage { get; set; }
|
||||
bool isPosting { get; set; } = false;
|
||||
ActionBarFilter Filters { get; set; } = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var filters = await LocalStorage.GetItemAsync<ActionBarFilter>(nameof(ActionBarFilter));
|
||||
if (filters == default)
|
||||
await LocalStorage.SetItemAsync(nameof(ActionBarFilter), Filters);
|
||||
else
|
||||
Filters = filters;
|
||||
}
|
||||
|
||||
void OpenCloseMessageForm()
|
||||
{
|
||||
isPosting = !isPosting;
|
||||
}
|
||||
async Task OnTimelineChange(TimelineType timelineType)
|
||||
{
|
||||
Filters.TimelineType = timelineType;
|
||||
await LocalStorage.SetItemAsync(nameof(ActionBarFilter), Filters);
|
||||
await OnTimelineChanged.InvokeAsync(Filters.TimelineType);
|
||||
}
|
||||
async Task OnTimeSortChange(TimeSortingType timeSorting)
|
||||
{
|
||||
Filters.TimeSortingType = timeSorting;
|
||||
await LocalStorage.SetItemAsync(nameof(ActionBarFilter), Filters);
|
||||
await OnTimeSortingChanged.InvokeAsync(Filters.TimeSortingType);
|
||||
}
|
||||
}
|
248
Components/Content.razor
Normal file
248
Components/Content.razor
Normal file
@ -0,0 +1,248 @@
|
||||
<div class="flex space-x-3 w-full neomorph is-nxxsmall rounded-xl @CssContainer">
|
||||
|
||||
<div class="flex flex-col py-3 pl-3 md:py-4 md:pl-4 flex-none rounded-l-xl bg-cover bg-no-repeat bg-center"
|
||||
style="background-image:linear-gradient(to left, var(--background), transparent), url('@(Message.User?.BackgroundUrl)');">
|
||||
|
||||
<a class="block h-12 w-12 md:h-16 md:w-16" href="@Message.User.ProfileUrl" title="@Message.User.UserName">
|
||||
<img alt="@Message.User.UserName" class="h-12 w-12 md:h-16 md:w-16 object-cover rounded-full neomorph is-nxxsmall" src="@(Message.User.PictureUrl ?? "/imgs/icon-192.png")" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col space-y-3 flex-1 py-3 pr-3 md:py-4 md:pr-4 min-w-0">
|
||||
|
||||
<div class="flex flex-col space-y-1 flex-1 min-w-0">
|
||||
<p class="inline-flex flex-1 space-x-2 min-w-0 justify-between text-xs md:text-sm">
|
||||
<span class="inline-flex space-x-2 min-w-0">
|
||||
<b class="shrink truncate max-w-[80%]" title="@Message.User.DisplayName">
|
||||
@Message.User.DisplayName
|
||||
</b>
|
||||
<a class="underline flex-1 min-w-6 opacity-50 truncate self-center text-xs" href="@Message.User.ProfileUrl" title="@Message.User.UserName">
|
||||
@Message.User.UserName
|
||||
</a>
|
||||
</span>
|
||||
<span class="flex-none inline-flex space-x-1 items-center">
|
||||
<span title="@Message.CreatedAt.ToLocalTime().ToString("📅dd/MM/yy 🕒HH:mm")">
|
||||
@Message.CreatedAt.GetPassedTime(CascadingState.Localizer)
|
||||
</span>
|
||||
<i aria-hidden="true" class="@Message.MessageType.GetMessageTypeIcon() text-md"
|
||||
title="@CascadingState.Localizer[Message.MessageType.ToString()]">
|
||||
</i>
|
||||
</span>
|
||||
</p>
|
||||
@if (Message.Title is { Length: > 0 })
|
||||
{
|
||||
<p class="text-sm md:text-base font-bold break-all">@Message.Title</p>
|
||||
}
|
||||
|
||||
@if (Message.Content is { Length: > 0 })
|
||||
{
|
||||
<div class="text-sm md:text-base break-all">
|
||||
@((MarkupString)Message.Content)
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (Message.Medias.Count != 0)
|
||||
{
|
||||
<div class="grid gap-4 auto-cols-auto grid-rows-1 grid-flow-col-dense">
|
||||
@foreach (var media in Message.Medias)
|
||||
{
|
||||
if (media.ContentType.StartsWith("image"))
|
||||
{
|
||||
<a class="w-auto" href="@media.Url">
|
||||
<img alt="@media.AltText" class="w-full rounded-lg @SUtility.IfTrueThen(Message.Medias.Count > 1, "max-h-[30vh]")" src="@media.Url" title="@media.AltText">
|
||||
</a>
|
||||
}
|
||||
else if (media.ContentType.StartsWith("video"))
|
||||
{
|
||||
<video class="w-full max-h-[50vh] aspect-video rounded-lg mx-auto neomorphInset is-nxxsmall" controls="controls" playsinline="playsinline" preload="metadata" title="@media.FileName">
|
||||
<source src="@media.Url" type="@media.ContentType" />
|
||||
</video>
|
||||
}
|
||||
else if (media.ContentType.StartsWith("audio"))
|
||||
{
|
||||
<audio class="w-full max-h-8" controls="controls" preload="metadata" title="@media.FileName">
|
||||
<source src="@media.Url" type="@media.ContentType" />
|
||||
</audio>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="flex items-center space-x-3 align-center rounded-lg p-3 md:p-4 neomorph is-nxxsmall">
|
||||
<span>
|
||||
<i class="text-2xl ion-md-document"></i>
|
||||
</span>
|
||||
<div class="flex flex-col w-full space-y-1">
|
||||
<p class="text-xs md:text-sm break-all">
|
||||
<b>@media.FileName</b>
|
||||
</p>
|
||||
<p class="text-xs break-all">
|
||||
<i class="ion-md-code"></i> @media.ContentType
|
||||
</p>
|
||||
</div>
|
||||
<button class="button is-small is-rounded neoBtnSmall" @onclick="async () => await OnMessageMediaDownload.InvokeAsync(media)" type="button">
|
||||
<span class="icon">
|
||||
<i class="ion-md-download text-base"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex space-x-3 mt-3 justify-between">
|
||||
<div class="flex space-x-3">
|
||||
@if (OnMessageReply.HasDelegate)
|
||||
{
|
||||
<button class="button is-small is-rounded @(isAnswering ? "neoBtnSmallInsetPlain" : "neoBtnSmall")" @onclick="Reply"
|
||||
title="@CascadingState.Localizer[isAnswering ? "Close" : "Reply"]">
|
||||
<span class="icon">
|
||||
<i aria-hidden="true" class="@SUtility.IfTrueThen(isAnswering, "ion-md-close-circle", "ion-md-text") text-lg has-text-success"></i>
|
||||
</span>
|
||||
</button>
|
||||
}
|
||||
@if (OnMessageBoost.HasDelegate)
|
||||
{
|
||||
<button class="button is-small is-rounded @(Message.IsBoosted ? "neoBtnSmallInsetPlain" : "neoBtnSmall")" @onclick="() => OnMessageBoost.InvokeAsync(Message)"
|
||||
title="@CascadingState.Localizer["Boost"]">
|
||||
<span class="icon">
|
||||
<i aria-hidden="true" class="ion-md-repeat text-lg has-text-info"></i>
|
||||
</span>
|
||||
</button>
|
||||
}
|
||||
@if (OnMessageFavourite.HasDelegate)
|
||||
{
|
||||
<button class="button is-small is-rounded @SUtility.IfTrueThen(Message.IsFavourite, "neoBtnSmallInsetPlain", "neoBtnSmall")" @onclick="() => OnMessageFavourite.InvokeAsync(Message)"
|
||||
title="@CascadingState.Localizer["Favourite"]">
|
||||
<span class="icon">
|
||||
<i aria-hidden="true" class="@SUtility.IfTrueThen(Message.IsFavourite, "ion-md-heart-dislike", "ion-md-heart") text-lg text-pink-300"></i>
|
||||
</span>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="flex space-x-3">
|
||||
<DropdownButton IsOpen="Message.IsOptionsOpen">
|
||||
<DropdownTrigger>
|
||||
<button class="button is-small is-rounded neoBtnSmall" @onclick="() => Message.IsOptionsOpen = !Message.IsOptionsOpen"
|
||||
title="@CascadingState.Localizer["Other"]">
|
||||
<span class="icon">
|
||||
<i aria-hidden="true" class="ion-md-more text-lg"></i>
|
||||
</span>
|
||||
</button>
|
||||
</DropdownTrigger>
|
||||
<DropdownContent>
|
||||
@if (OnUserDirectMessage.HasDelegate)
|
||||
{
|
||||
<div class="dropdown-item">
|
||||
<button class="button is-small is-rounded has-icons-left neoBtnSmall" @onclick="() => OnUserDirectMessage.InvokeAsync(Message)">
|
||||
<span class="icon is-left">
|
||||
<i aria-hidden="true" class="ion-md-paper-plane text-base has-text-success"></i>
|
||||
</span>
|
||||
<span>@CascadingState.Localizer["Direct message"]</span>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
@if (OnUserSilence.HasDelegate)
|
||||
{
|
||||
<div class="dropdown-item">
|
||||
<button class="button is-small is-rounded has-icons-left neoBtnSmall" @onclick="() => OnUserSilence.InvokeAsync(Message.User)">
|
||||
<span class="icon is-left">
|
||||
<i aria-hidden="true" class="ion-md-eye-off text-base drop-shadow has-text-warning"></i>
|
||||
</span>
|
||||
<span>@CascadingState.Localizer["Mute"]</span>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
@if (OnUserBlock.HasDelegate)
|
||||
{
|
||||
<div class="dropdown-item">
|
||||
<button class="button is-small is-rounded has-icons-left neoBtnSmall" @onclick="() => OnUserBlock.InvokeAsync(Message.User)">
|
||||
<span class="icon is-left">
|
||||
<i aria-hidden="true" class="ion-md-remove-circle text-base has-text-danger"></i>
|
||||
</span>
|
||||
<span>@CascadingState.Localizer["Block"]</span>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
@if (@*Message.User.UserName == CurrentUserName &&*@ OnMessageDelete.HasDelegate)
|
||||
{
|
||||
<div class="dropdown-item">
|
||||
<button class="button is-small has-icons-left is-rounded neoBtnSmall" @onclick="DeleteMessage"
|
||||
title="@CascadingState.Localizer["Delete"]">
|
||||
<span class="icon is-left">
|
||||
<i aria-hidden="true" class="ion-md-trash text-lg has-text-danger"></i>
|
||||
</span>
|
||||
<span>@CascadingState.Localizer["Delete"]</span>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</DropdownContent>
|
||||
</DropdownButton>
|
||||
@if (IncludeExpand)
|
||||
{
|
||||
<NavLink class="button is-small is-rounded neoBtnSmall" href="@($"expand/{Message.MessageId}")"
|
||||
title="@CascadingState.Localizer["Expand"]">
|
||||
<span class="icon">
|
||||
<i aria-hidden="true" class="ion-md-expand text-lg"></i>
|
||||
</span>
|
||||
</NavLink>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (isAnswering)
|
||||
{
|
||||
<MessageUpsertForm AnsweringMessage="Message" OnMessageSubmit="SubmitReply"></MessageUpsertForm>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] Task<AuthenticationState> AuthState { get; set; }
|
||||
[CascadingParameter] CascadingState CascadingState { get; set; }
|
||||
[Parameter] public Message Message { get; set; } = new();
|
||||
[Parameter] public EventCallback<MessageForm> OnMessageReply { get; set; }
|
||||
[Parameter] public EventCallback<Message> OnMessageBoost { get; set; }
|
||||
[Parameter] public EventCallback<Message> OnMessageFavourite { get; set; }
|
||||
[Parameter] public EventCallback<Message> OnMessageDelete { get; set; }
|
||||
[Parameter] public EventCallback<Message> OnUserDirectMessage { get; set; }
|
||||
[Parameter] public EventCallback<Media> OnMessageMediaDownload { get; set; }
|
||||
[Parameter] public EventCallback<MessageUser> OnUserBlock { get; set; }
|
||||
[Parameter] public EventCallback<MessageUser> OnUserSilence { get; set; }
|
||||
[Parameter] public string CssContainer { get; set; }
|
||||
[Parameter] public bool IncludeExpand { get; set; } = true;
|
||||
|
||||
bool isAnswering { get; set; } = false;
|
||||
|
||||
string CurrentUserName
|
||||
{
|
||||
get
|
||||
{
|
||||
return AuthState.Result.User.Identity?.Name;
|
||||
}
|
||||
}
|
||||
|
||||
async Task DeleteMessage()
|
||||
{
|
||||
isAnswering = false;
|
||||
await OnMessageDelete.InvokeAsync(Message);
|
||||
}
|
||||
|
||||
async Task SubmitReply(MessageForm messageForm)
|
||||
{
|
||||
isAnswering = false;
|
||||
await OnMessageReply.InvokeAsync(messageForm);
|
||||
}
|
||||
|
||||
async Task Reply()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
});
|
||||
isAnswering = !isAnswering;
|
||||
}
|
||||
}
|
13
Components/LoadingData.razor
Normal file
13
Components/LoadingData.razor
Normal file
@ -0,0 +1,13 @@
|
||||
<p class="w-full loadAnimation text-center">
|
||||
<span>.</span>
|
||||
<span>.</span>
|
||||
<span>.</span>
|
||||
@CascadingState.Localizer["loading"]
|
||||
<span>.</span>
|
||||
<span>.</span>
|
||||
<span>.</span>
|
||||
</p>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] CascadingState CascadingState { get; set; }
|
||||
}
|
@ -1,7 +1,430 @@
|
||||
<EditForm Model="">
|
||||
|
||||
<EditForm Model="MessageForm" OnValidSubmit="OnValidSubmit">
|
||||
<DataAnnotationsValidator />
|
||||
|
||||
<div class="field mb-3">
|
||||
<div class="control">
|
||||
<InputText class="input rounded-t-[1.4rem] rounded-b-lg neoInput" maxlength="64"
|
||||
placeholder="@CascadingState.Localizer["Title (optional)"]" Value="@MessageForm.Title"
|
||||
ValueChanged="(v) => OnTitleChanged(v)"
|
||||
ValueExpression="() => MessageForm.Title" />
|
||||
</div>
|
||||
<div class="control relative mt-1">
|
||||
<textarea @bind="@MessageForm.Content" @bind:event="oninput"
|
||||
class="textarea rounded-b-[1.4rem] rounded-t-lg neoInput"
|
||||
maxlength="5000"
|
||||
placeholder="@CascadingState.Localizer["oh shit... here we go again"]" rows="3"></textarea>
|
||||
<span class="absolute text-xs opacity-50 right-2 bottom-1">@(MessageForm.Content?.Length ?? 0)/5000</span>
|
||||
</div>
|
||||
@if (showPreviewButton && isPreviewOpen && MessageForm.Content is { Length: > 0 })
|
||||
{
|
||||
<div class="control relative mt-1 px-8">
|
||||
<div class="neomorphInset rounded-t-lg rounded-b-[1.4rem] px-2 py-1 md:px-3 md:py-2 text-xs md:text-sm">
|
||||
@switch (MessageForm.ContentType)
|
||||
{
|
||||
case ContentType.Markdown:
|
||||
@((MarkupString)Markdown.ToHtml(MessageForm.Content))
|
||||
break;
|
||||
case ContentType.HTML:
|
||||
<p>@((MarkupString)MessageForm.Content)</p>
|
||||
break;
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="help is-danger">
|
||||
<ValidationMessage For="() => MessageForm.Content" />
|
||||
@contentError
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col space-y-3 mb-3 @SUtility.IfTrueThen(MessageForm.Media.Count == 0, "hidden")">
|
||||
@foreach (var media in MessageForm.Media)
|
||||
{
|
||||
switch (MessageForm.MediaType)
|
||||
{
|
||||
case MediaType.Images:
|
||||
<div class="flex w-full items-center space-x-3 rounded-xl p-3 md:p-4 neomorph is-nxxsmall">
|
||||
<img alt="@media.AltText" class="object-cover rounded-lg neomorph is-nxxsmall max-h-24 md:max-h-40 max-w-[6rem] md:max-w-[12rem]" src="@media.Base64Preview" />
|
||||
<div class="flex w-full self-start flex-col justify-between space-y-2">
|
||||
<div class="flex w-full space-x-3">
|
||||
<div class="flex-1">
|
||||
<p class="text-xs md:text-sm break-all">
|
||||
<i class="ion-md-image"></i> <b>@media.FileName</b>
|
||||
</p>
|
||||
<p class="text-xs break-all">
|
||||
<i class="ion-md-code"></i> @media.ContentType <i class="ion-md-fitness"></i> @media.Size.GetFileSize(CascadingState.Localizer)
|
||||
</p>
|
||||
</div>
|
||||
<button class="button is-small is-rounded self-start neoBtnSmall" @onclick="() => RemoveFile(media)" type="button">
|
||||
<span class="icon">
|
||||
<i class="ion-md-trash text-base text-red-400"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="field w-full">
|
||||
<div class="control w-full">
|
||||
<InputTextArea @bind-Value="media.AltText" class="textarea w-full is-small !rounded-lg neoInput"
|
||||
placeholder="@CascadingState.Localizer["Alternative text"]" rows="1" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
case MediaType.Video:
|
||||
case MediaType.Documents:
|
||||
<div class="flex items-center space-x-3 align-center rounded-xl p-3 md:p-4 neomorph is-nxxsmall">
|
||||
<span>
|
||||
<i class="text-2xl @MessageForm.MediaType.GetMediaTypeIcon()"></i>
|
||||
</span>
|
||||
<div class="flex flex-col w-full space-y-1">
|
||||
<p class="text-xs md:text-sm break-all">
|
||||
<b>@media.FileName</b>
|
||||
</p>
|
||||
<p class="text-xs break-all">
|
||||
<i class="ion-md-code"></i> @media.ContentType <i class="ion-md-fitness"></i> @media.Size.GetFileSize(CascadingState.Localizer)
|
||||
</p>
|
||||
</div>
|
||||
<button class="button is-small is-rounded neoBtnSmall" @onclick="() => RemoveFile(media)" type="button">
|
||||
<span class="icon">
|
||||
<i class="ion-md-trash text-base text-red-400"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
break;
|
||||
case MediaType.Audio:
|
||||
<div class="flex items-center space-x-3 align-center rounded-xl p-3 md:p-4 neomorph is-nxxsmall">
|
||||
<span>
|
||||
<i class="text-2xl @MessageForm.MediaType.GetMediaTypeIcon()"></i>
|
||||
</span>
|
||||
<div class="flex flex-col w-full space-y-1">
|
||||
<p class="text-xs md:text-sm break-all">
|
||||
<b>@media.FileName</b>
|
||||
</p>
|
||||
<p class="text-xs break-all">
|
||||
<i class="ion-md-code"></i> @media.ContentType <i class="ion-md-fitness"></i> @media.Size.GetFileSize(CascadingState.Localizer)
|
||||
</p>
|
||||
<audio controls="controls" class="w-full max-h-8">
|
||||
<source src="@media.Base64Preview" type="@media.ContentType">
|
||||
</audio>
|
||||
</div>
|
||||
<button class="button is-small is-rounded neoBtnSmall" @onclick="() => RemoveFile(media)" type="button">
|
||||
<span class="icon">
|
||||
<i class="ion-md-trash text-base text-red-400"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
@if (fileInputErrorMessage is { Length: > 0 })
|
||||
{
|
||||
<div class="help is-danger p-1 md:p-2 mb-3 rounded-xl neomorphInset is-nxxsmall">
|
||||
@((MarkupString)fileInputErrorMessage)
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex justify-between space-x-3 h-[30px]">
|
||||
<div class="flex space-x-3">
|
||||
<DropdownButton CssDirection="is-left" IsOpen="MessageForm.IsScopeOptionsOpen">
|
||||
<DropdownTrigger>
|
||||
<button class="button is-small is-rounded @SUtility.IfTrueThen(MessageForm.IsScopeOptionsOpen, "neoBtnSmallInsetPlain", "neoBtnSmall")" @onclick="OpenCloseMessageType"
|
||||
title="@string.Format(CascadingState.Localizer["Message scope type: {0}"], CascadingState.Localizer[MessageType.Direct.ToString()])" type="button">
|
||||
<span class="icon">
|
||||
<i class="@MessageForm.MessageType.GetMessageTypeIcon() text-base"></i>
|
||||
</span>
|
||||
</button>
|
||||
</DropdownTrigger>
|
||||
<DropdownContent>
|
||||
<div class="flex space-x-3 px-2">
|
||||
<button class="button is-small is-rounded @SUtility.IfTrueThen(MessageForm.MessageType is MessageType.Direct, "neoBtnSmallInsetPlain", "neoBtnSmall")" @onclick="() => UpdateMessageType(MessageType.Direct)"
|
||||
title="@CascadingState.Localizer[MessageType.Direct.ToString()]" type="button">
|
||||
<span class="icon">
|
||||
<i class="ion-md-paper-plane text-base"></i>
|
||||
</span>
|
||||
</button>
|
||||
<button class="button is-small is-rounded @SUtility.IfTrueThen(MessageForm.MessageType is MessageType.FollowersOnly, "neoBtnSmallInsetPlain", "neoBtnSmall")" @onclick="() => UpdateMessageType(MessageType.FollowersOnly)"
|
||||
title="@CascadingState.Localizer[MessageType.FollowersOnly.ToString()]" type="button">
|
||||
<span class="icon">
|
||||
<i class="ion-md-lock text-base"></i>
|
||||
</span>
|
||||
</button>
|
||||
<button class="button is-small is-rounded @SUtility.IfTrueThen(MessageForm.MessageType is MessageType.Unlisted, "neoBtnSmallInsetPlain", "neoBtnSmall")" @onclick="() => UpdateMessageType(MessageType.Unlisted)"
|
||||
title="@CascadingState.Localizer[MessageType.Unlisted.ToString()]" type="button">
|
||||
<span class="icon">
|
||||
<i class="ion-md-unlock text-base"></i>
|
||||
</span>
|
||||
</button>
|
||||
<button class="button is-small is-rounded @SUtility.IfTrueThen(MessageForm.MessageType is MessageType.Public, "neoBtnSmallInsetPlain", "neoBtnSmall")" @onclick="() => UpdateMessageType(MessageType.Public)"
|
||||
title="@CascadingState.Localizer[MessageType.Public.ToString()]" type="button">
|
||||
<span class="icon">
|
||||
<i class="ion-md-globe text-base"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</DropdownContent>
|
||||
</DropdownButton>
|
||||
|
||||
<div class="file is-small">
|
||||
<label class="file-label overflow-visible rounded-full neoBtnSmall h-[30px]">
|
||||
<InputFile accept="@acceptedFilesTypes" class="file-input" multiple="@ShouldHaveMultipleUpload()" OnChange="OnFileChange" disabled="@ShouldDisableUpload()" />
|
||||
<span class="file-cta">
|
||||
<span class="file-icon @SUtility.IfTrueThen(MessageForm.Media.Count == 0, "mr-0")">
|
||||
<i class="ion-md-attach text-base"></i>
|
||||
</span>
|
||||
@if (MessageForm.Media.Count != 0)
|
||||
{
|
||||
<span class="file-label">+@MessageForm.Media.Count</span>
|
||||
}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="control has-icons-left">
|
||||
<div class="select is-small is-rounded neoSelect">
|
||||
<InputSelect TValue="MediaType" Value="MessageForm.MediaType" ValueChanged="v => OnMediaTypeChanged(v)" ValueExpression="() => MessageForm.MediaType" disabled="@(MessageForm.Media.Count > 0)">
|
||||
@foreach (var mediaType in Enum.GetValues<MediaType>())
|
||||
{
|
||||
<option value="@mediaType">@CascadingState.Localizer[mediaType.ToString()]</option>
|
||||
}
|
||||
</InputSelect>
|
||||
</div>
|
||||
<span class="icon is-small is-left">
|
||||
<i class="@MessageForm.MediaType.GetMediaTypeIcon()"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="control has-icons-left">
|
||||
<div class="select is-small is-rounded neoSelect">
|
||||
<InputSelect TValue="ContentType" Value="MessageForm.ContentType" ValueChanged="v => OnContentTypeChanged(v)" ValueExpression="() => MessageForm.ContentType">
|
||||
@foreach (var contentType in Enum.GetValues<ContentType>())
|
||||
{
|
||||
<option value="@contentType">@CascadingState.Localizer[contentType.ToString()]</option>
|
||||
}
|
||||
</InputSelect>
|
||||
</div>
|
||||
<span class="icon is-small is-left">
|
||||
<i class="@MessageForm.ContentType.GetContentTypeIcon()"></i>
|
||||
</span>
|
||||
<div class="help is-danger">
|
||||
<ValidationMessage For="() => MessageForm.ContentType" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (showPreviewButton)
|
||||
{
|
||||
<button class="button is-small is-rounded @SUtility.IfTrueThen(isPreviewOpen, "neoBtnSmallInsetPlain", "neoBtnSmall")" @onclick="OnOpenClosePreview"
|
||||
title="@CascadingState.Localizer["Show preview"]" type="button">
|
||||
<span class="icon">
|
||||
<i class="@SUtility.IfTrueThen(isPreviewOpen, "ion-md-eye-off", "ion-md-eye") text-base"></i>
|
||||
</span>
|
||||
</button>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex space-x-3">
|
||||
|
||||
<button class="button is-small is-rounded has-icons-right neoBtnSmall" type="submit">
|
||||
<span>@CascadingState.Localizer["Post"]</span>
|
||||
<span class="icon is-right">
|
||||
<i class="ion-md-send"></i>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</EditForm>
|
||||
|
||||
@code {
|
||||
|
||||
[CascadingParameter] CascadingState CascadingState { get; set; }
|
||||
[Parameter] public Message AnsweringMessage { get; set; }
|
||||
[Parameter] public EventCallback<MessageForm> OnMessageSubmit { get; set; }
|
||||
MessageForm MessageForm { get; set; } = new();
|
||||
int totalCharacters { get; set; } = 0;
|
||||
string fileInputErrorMessage { get; set; }
|
||||
string contentError { get; set; }
|
||||
string acceptedFilesTypes { get; set; } = ".jpg,.jpeg,.png,.gif";
|
||||
bool showPreviewButton { get; set; } = false;
|
||||
bool isPreviewOpen { get; set; } = false;
|
||||
|
||||
void OpenCloseMessageType()
|
||||
{
|
||||
MessageForm.IsScopeOptionsOpen = !MessageForm.IsScopeOptionsOpen;
|
||||
}
|
||||
|
||||
void UpdateMessageType(MessageType messageType)
|
||||
{
|
||||
MessageForm.MessageType = messageType;
|
||||
MessageForm.IsScopeOptionsOpen = false;
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (AnsweringMessage != default)
|
||||
{
|
||||
MessageForm.Title = AnsweringMessage.Title;
|
||||
MessageForm.RootMessageId = AnsweringMessage.RootMessageId ?? AnsweringMessage.MessageId;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShouldDisableUpload()
|
||||
{
|
||||
switch (MessageForm.MediaType)
|
||||
{
|
||||
case MediaType.Images:
|
||||
return MessageForm.Media.Count == 5;
|
||||
case MediaType.Video:
|
||||
case MediaType.Audio:
|
||||
return MessageForm.Media.Count == 1;
|
||||
case MediaType.Documents:
|
||||
return MessageForm.Media.Count == 3;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShouldHaveMultipleUpload()
|
||||
{
|
||||
return MessageForm.MediaType is MediaType.Images or MediaType.Documents;
|
||||
}
|
||||
|
||||
async Task OnFileChange(InputFileChangeEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
fileInputErrorMessage = string.Empty;
|
||||
var maximumFileCount = MessageForm.MediaType switch
|
||||
{
|
||||
MediaType.Images => 5,
|
||||
MediaType.Audio => 1,
|
||||
MediaType.Video => 1,
|
||||
MediaType.Documents => 3
|
||||
};
|
||||
if (eventArgs.FileCount > maximumFileCount)
|
||||
{
|
||||
fileInputErrorMessage = string.Format(CascadingState.Localizer["The maximum number of files accepted is {0}, but {1} were supplied."], maximumFileCount, eventArgs.FileCount);
|
||||
return;
|
||||
}
|
||||
if (eventArgs.FileCount + MessageForm.Media.Count > maximumFileCount)
|
||||
{
|
||||
fileInputErrorMessage = string.Format(CascadingState.Localizer["The maximum number of files accepted is {0}, but {1} were supplied."], maximumFileCount, $"{MessageForm.Media.Count}+{eventArgs.FileCount}");
|
||||
return;
|
||||
}
|
||||
var maxAllowedSize = MessageForm.MediaType switch
|
||||
{
|
||||
MediaType.Images => 3_145_728,
|
||||
MediaType.Audio => 5_242_880,
|
||||
MediaType.Video => 20_971_520,
|
||||
MediaType.Documents => 3_145_728
|
||||
};
|
||||
var uploadMedia = default(UploadMedia);
|
||||
using (var memStream = new MemoryStream())
|
||||
foreach (var file in eventArgs.GetMultipleFiles(maximumFileCount))
|
||||
{
|
||||
if (file.Name == default || file.ContentType == default) continue;
|
||||
if (MessageForm.Media.Any(m => m.FileName == file.Name)) continue;
|
||||
if (file.Size > maxAllowedSize)
|
||||
{
|
||||
fileInputErrorMessage += string.Format(CascadingState.Localizer["Supplied file \"{0}\" with size {1:N0}MBs exceeds the maximum of {2:N0}MBs."], file.Name, file.Size / 1_048_576, maxAllowedSize / 1_048_576) + "<br/>";
|
||||
continue;
|
||||
}
|
||||
|
||||
uploadMedia = new()
|
||||
{
|
||||
FileName = file.Name,
|
||||
ContentType = file.ContentType,
|
||||
Size = file.Size
|
||||
};
|
||||
try
|
||||
{
|
||||
using (var imgStream = file.OpenReadStream(maxAllowedSize))
|
||||
{
|
||||
await imgStream.CopyToAsync(memStream);
|
||||
memStream.Position = 0;
|
||||
uploadMedia.Blob = memStream.ToArray();
|
||||
await memStream.FlushAsync();
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
fileInputErrorMessage = e.Message;
|
||||
continue;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
fileInputErrorMessage = e.Message;
|
||||
continue;
|
||||
}
|
||||
if (MessageForm.MediaType is MediaType.Images or MediaType.Audio)
|
||||
uploadMedia.Base64Preview = $"data:{uploadMedia.ContentType};base64,{Convert.ToBase64String(uploadMedia.Blob)}";
|
||||
|
||||
MessageForm.Media.Add(uploadMedia);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
fileInputErrorMessage = e.Message;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveFile(UploadMedia media)
|
||||
{
|
||||
MessageForm.Media.Remove(media);
|
||||
}
|
||||
|
||||
void OnTitleChanged(string value)
|
||||
{
|
||||
MessageForm.Title = value;
|
||||
}
|
||||
|
||||
void ContentLengthChanged()
|
||||
{
|
||||
totalCharacters = MessageForm.Content?.Length ?? 0;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
void OnContentTypeChanged(ContentType contentType)
|
||||
{
|
||||
MessageForm.ContentType = contentType;
|
||||
showPreviewButton = contentType is ContentType.Markdown or ContentType.HTML;
|
||||
}
|
||||
|
||||
void OnMediaTypeChanged(MediaType mediaType)
|
||||
{
|
||||
MessageForm.MediaType = mediaType;
|
||||
acceptedFilesTypes = mediaType switch
|
||||
{
|
||||
MediaType.Images => ".jpg,.jpeg,.png,.gif",
|
||||
MediaType.Video => ".webm,.mp4,.m4v",
|
||||
MediaType.Audio => ".mp3,.wav,.flac,.m4a",
|
||||
MediaType.Documents => ".xlsx,.csv,.ppt,.odt",
|
||||
_ => default
|
||||
};
|
||||
}
|
||||
|
||||
void OnOpenClosePreview()
|
||||
{
|
||||
isPreviewOpen = !isPreviewOpen;
|
||||
}
|
||||
|
||||
async Task OnValidSubmit()
|
||||
{
|
||||
contentError = default;
|
||||
if ((MessageForm.Content is { Length: 0 } && MessageForm.Media.Count == 0))
|
||||
{
|
||||
contentError = CascadingState.Localizer["Missing content, either message or media"];
|
||||
return;
|
||||
}
|
||||
|
||||
await OnMessageSubmit.InvokeAsync(MessageForm);
|
||||
}
|
||||
}
|
36
Components/OpenDownContainer.razor
Normal file
36
Components/OpenDownContainer.razor
Normal file
@ -0,0 +1,36 @@
|
||||
<div class="flex flex-col w-full space-y-4">
|
||||
|
||||
<div class="flex w-full justify-between items-center py-2 md:py-3 px-3 md:px-4 space-x-2 rounded-lg neomorph is-nxsmall">
|
||||
@TitleChildren
|
||||
<button class="button is-rounded is-small @ButtonCss"
|
||||
@onclick:preventDefault @onclick="OpenCloseInnerContent" disabled="@(!HasInnerContent)">
|
||||
<span class="icon">
|
||||
<i aria-hidden="true" class="@LeftIconCss"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="@InnerContentContainerCss @Hidden">
|
||||
@InnerContent
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment TitleChildren { get; set; }
|
||||
[Parameter] public RenderFragment InnerContent { get; set; }
|
||||
[Parameter] public bool HasInnerContent { get; set; } = true;
|
||||
[Parameter] public string InnerContentContainerCss { get; set; } = "block w-auto ml-5 md:ml-10 rounded-lg neomorph is-nxsmall";
|
||||
|
||||
[Parameter]
|
||||
public bool IsOpen { get; set; } = false;
|
||||
string Hidden { get; set; } = VConstants.HideClass;
|
||||
string ButtonCss => $"{(HasInnerContent ? default : "cursor-not-allowed")} neoBtnSmall";
|
||||
string LeftIconCss => IsOpen ? "ion-md-arrow-dropup" : "ion-md-arrow-dropdown";
|
||||
|
||||
void OpenCloseInnerContent()
|
||||
{
|
||||
IsOpen = !IsOpen;
|
||||
Hidden = IsOpen ? default : VConstants.HideClass;
|
||||
}
|
||||
}
|
@ -1,6 +1,187 @@
|
||||
namespace decePubClient.Extensions;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
|
||||
public class GenericExtensions
|
||||
using Blazored.LocalStorage;
|
||||
|
||||
using decePubClient.Models;
|
||||
using decePubClient.Models.Types;
|
||||
using decePubClient.Resources;
|
||||
using decePubClient.Services;
|
||||
|
||||
using DnetIndexedDb;
|
||||
using DnetIndexedDb.Fluent;
|
||||
using DnetIndexedDb.Models;
|
||||
|
||||
using Markdig;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace decePubClient.Extensions;
|
||||
|
||||
public static class GenericExtensions
|
||||
{
|
||||
|
||||
public static NameValueCollection QueryString(this NavigationManager navigationManager) =>
|
||||
HttpUtility.ParseQueryString(new Uri(navigationManager.Uri).Query);
|
||||
|
||||
public static string QueryString(this NavigationManager navigationManager, string key) =>
|
||||
navigationManager.QueryString()[key];
|
||||
|
||||
public static T QueryString<T>(this NavigationManager navigationManager, string key)
|
||||
{
|
||||
var value = navigationManager.QueryString()[key];
|
||||
var converter = TypeDescriptor.GetConverter(typeof(T));
|
||||
if (converter.IsValid(value))
|
||||
return (T)Convert.ChangeType(value, typeof(T));
|
||||
else
|
||||
return default;
|
||||
}
|
||||
|
||||
public static async Task SetDefaultCulture(this WebAssemblyHost host)
|
||||
{
|
||||
var storage = host.Services.GetRequiredService<ILocalStorageService>();
|
||||
var language = await storage.GetItemAsync<string>("languageCode");
|
||||
if (language is { Length: 0 })
|
||||
await storage.SetItemAsync("languageCode", language);
|
||||
|
||||
var culture = new CultureInfo(language ??= "en-GB");
|
||||
CultureInfo.DefaultThreadCurrentCulture = culture;
|
||||
CultureInfo.DefaultThreadCurrentUICulture = culture;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddIndexedDb(this IServiceCollection services)
|
||||
{
|
||||
return services.AddIndexedDbDatabase<IndexedDb>(options =>
|
||||
{
|
||||
var model = new IndexedDbDatabaseModel()
|
||||
.WithName("data")
|
||||
.WithVersion(1)
|
||||
.WithModelId(1);
|
||||
|
||||
model.AddStore(nameof(Message))
|
||||
.WithKey(nameof(Message.MessageId))
|
||||
.AddUniqueIndex(nameof(Message.MessageId))
|
||||
.AddIndex(nameof(Message.RootMessageId))
|
||||
.AddIndex(nameof(Message.User))
|
||||
.AddIndex(nameof(Message.MessageType))
|
||||
.AddIndex(nameof(Message.Title))
|
||||
.AddIndex(nameof(Message.Content))
|
||||
.AddIndex(nameof(Message.CreatedAt))
|
||||
.AddIndex(nameof(Message.Medias));
|
||||
|
||||
options.UseDatabase(model);
|
||||
});
|
||||
}
|
||||
|
||||
public static string GetPassedTime(this DateTime dateTime, IStringLocalizer<AllStrings> localizer)
|
||||
{
|
||||
var timeframe = DateTime.Now - dateTime.ToLocalTime();
|
||||
switch ((int)timeframe.TotalHours)
|
||||
{
|
||||
case >= 24:
|
||||
return string.Format(localizer["{0}d"], (int)timeframe.TotalDays);
|
||||
case >= 1 and < 24:
|
||||
return string.Format(localizer["{0}h"], (int)timeframe.TotalHours);
|
||||
case 0:
|
||||
return string.Format(localizer["{0}m"], (int)timeframe.TotalMinutes);
|
||||
default:
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetFileSize(this long size, IStringLocalizer<AllStrings> localizer)
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case >= 1_048_576:
|
||||
return string.Format(localizer["{0:N0}MB"], size / 1_048_576);
|
||||
case < 1_048_576:
|
||||
return string.Format(localizer["{0}KB"], size / 1_024);
|
||||
default:
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ParseContent(this string content, ContentType contentType)
|
||||
{
|
||||
switch (contentType)
|
||||
{
|
||||
case ContentType.PlainText:
|
||||
return content;
|
||||
case ContentType.HTML:
|
||||
return ((MarkupString)content).Value;
|
||||
case ContentType.Markdown:
|
||||
return Markdown.ToHtml(content);
|
||||
default:
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetMessageTypeIcon(this MessageType messageType)
|
||||
{
|
||||
switch (messageType)
|
||||
{
|
||||
case MessageType.Public:
|
||||
return "ion-md-globe";
|
||||
case MessageType.Unlisted:
|
||||
return "ion-md-unlock";
|
||||
case MessageType.FollowersOnly:
|
||||
return "ion-md-lock";
|
||||
case MessageType.Direct:
|
||||
return "ion-md-paper-plane";
|
||||
default:
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetTimelineTypeIcon(this TimelineType timelineType)
|
||||
{
|
||||
switch (timelineType)
|
||||
{
|
||||
case TimelineType.Home:
|
||||
return "ion-md-home";
|
||||
case TimelineType.Local:
|
||||
return "ion-md-people";
|
||||
case TimelineType.Federation:
|
||||
return "ion-md-globe";
|
||||
default:
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetContentTypeIcon(this ContentType contentType)
|
||||
{
|
||||
switch (contentType)
|
||||
{
|
||||
case ContentType.PlainText:
|
||||
return "ion-md-quote";
|
||||
case ContentType.HTML:
|
||||
return "ion-logo-html5";
|
||||
case ContentType.Markdown:
|
||||
return "ion-logo-markdown";
|
||||
default:
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetMediaTypeIcon(this MediaType mediaType)
|
||||
{
|
||||
switch (mediaType)
|
||||
{
|
||||
case MediaType.Images:
|
||||
return "ion-md-images";
|
||||
case MediaType.Video:
|
||||
return "ion-md-videocam";
|
||||
case MediaType.Documents:
|
||||
return "ion-md-document";
|
||||
case MediaType.Audio:
|
||||
return "ion-md-volume-high";
|
||||
default:
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +1,34 @@
|
||||
|
||||
using decePubClient.Models;
|
||||
|
||||
namespace decePubClient.Helpers;
|
||||
|
||||
public static class Faker
|
||||
{
|
||||
static IReadOnlyList<MessageUser> Users => new List<MessageUser>
|
||||
{
|
||||
new(),
|
||||
new()
|
||||
{
|
||||
UserId = "7b5703dc-aee8-46b1-aed2-cd06021a1c0c",
|
||||
DisplayName = "loweel",
|
||||
UserName = "@loweel@bbs.keinpfusch.net",
|
||||
PictureUrl = "https://bbs.keinpfusch.net/media/4729611f9aaef76399600ba2f117e5da609e5bf46dd7d502dae3e7b9fdc5cc78.WBMX2L9V1D00",
|
||||
BackgroundUrl = "https://bbs.keinpfusch.net/media/6e283b943ca297629cb35b7fdfc790907dfd24b6303518e10992b2b5a6658947.3EUB6O4OMR2X",
|
||||
ProfileUrl = "https://letsrulethe.world/users/AG6rE2nRya826QEJFY"
|
||||
},
|
||||
new()
|
||||
{
|
||||
UserId = "bc9c2a2b-fc5f-42fc-b907-ac30203eed45",
|
||||
DisplayName = "Valentina Nappi",
|
||||
UserName = "@valentina.nappi@mastodon.uno",
|
||||
PictureUrl = "https://cdn.masto.host/mastodonuno/cache/accounts/avatars/106/816/797/491/758/442/original/2b2995b82af966fb.jpg",
|
||||
BackgroundUrl = "https://cdn.masto.host/mastodonuno/cache/accounts/headers/106/816/797/491/758/442/original/898aedf6cd3a2da3.jpeg",
|
||||
ProfileUrl = "https://mastodon.uno/web/@valenappi@beta.birdsite.live"
|
||||
}
|
||||
};
|
||||
|
||||
public static MessageUser GetRandomUser()
|
||||
{
|
||||
return Users[Random.Shared.Next(0, Users.Count)];
|
||||
}
|
||||
}
|
@ -1,10 +1,6 @@
|
||||
using collAnon.Shared;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Mail;
|
||||
using System.Text.Json;
|
||||
using System.Net.Mail;
|
||||
|
||||
namespace collAnon.Client.Helpers
|
||||
namespace decePubClient.Helpers
|
||||
{
|
||||
public static class SUtility
|
||||
{
|
||||
@ -27,12 +23,6 @@ namespace collAnon.Client.Helpers
|
||||
return !(end < now && now < start);
|
||||
}
|
||||
|
||||
public static bool CacheHasExpired(long? lastTimeCacheTimeTicks)
|
||||
{
|
||||
if (!lastTimeCacheTimeTicks.HasValue) return true;
|
||||
return (DateTime.Now.Ticks - lastTimeCacheTimeTicks.Value) > VConstants.CacheExpirationPeriod.Ticks;
|
||||
}
|
||||
|
||||
public static string GetFileIcon(string fileName)
|
||||
{
|
||||
switch (Path.GetExtension(fileName))
|
||||
@ -62,21 +52,6 @@ namespace collAnon.Client.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetMissingMimeType(string fileName)
|
||||
{
|
||||
switch (Path.GetExtension(fileName))
|
||||
{
|
||||
case ".docx":
|
||||
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
||||
|
||||
case ".xlsx":
|
||||
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsValidEmail(string email)
|
||||
{
|
||||
try
|
||||
@ -89,18 +64,5 @@ namespace collAnon.Client.Helpers
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetQrCodeBase64(string base64String) => $"data:image/png;base64,{base64String}";
|
||||
|
||||
public static int GetRand()
|
||||
{
|
||||
var random = new Random(DateTime.Now.Millisecond);
|
||||
return random.Next(0, random.Next(10, 1000));
|
||||
}
|
||||
|
||||
public static T Deserialize<T>(string value)
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(value, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,179 @@
|
||||
<h3>CascadingState</h3>
|
||||
@if (PublicCacheData != null)
|
||||
{
|
||||
if (PublicCacheData.ThemeIsDarkMode)
|
||||
{
|
||||
<Meta Content="@($"hsl({PublicCacheData.ThemeIndexColour},16%,12%)")" Name="theme-color"/>
|
||||
<Meta Content="@($"hsl({PublicCacheData.ThemeIndexColour},16%,12%)")" Name="background-color"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<Meta Content="@($"hsl({PublicCacheData.ThemeIndexColour},84%,88%)")" Name="theme-color"/>
|
||||
<Meta Content="@($"hsl({PublicCacheData.ThemeIndexColour},84%,88%)")" Name="background-color"/>
|
||||
}
|
||||
}
|
||||
<style>
|
||||
:root {
|
||||
@if (PublicCacheData != null)
|
||||
{@if (PublicCacheData.ThemeIsDarkMode)
|
||||
{
|
||||
@($@"--background: hsl({PublicCacheData.ThemeIndexColour},16%,12%);
|
||||
--text-color: hsl({PublicCacheData.ThemeIndexColour},16%,73.6%);
|
||||
--placeholder-text-color: hsla({PublicCacheData.ThemeIndexColour},84%,52.8%,.3);
|
||||
--primary-color: hsl({PublicCacheData.ThemeIndexColour},16%,12%);
|
||||
--primary-color-light: hsl({PublicCacheData.ThemeIndexColour},84%,100%);
|
||||
--primary-color-dark: hsl({PublicCacheData.ThemeIndexColour},16%,33%);
|
||||
--primary-gradiend-light: hsl({PublicCacheData.ThemeIndexColour},16%,16%);
|
||||
--primary-gradiend-dark: hsl({PublicCacheData.ThemeIndexColour},16%,8%);
|
||||
--primary-gradiend-lighter: hsl({PublicCacheData.ThemeIndexColour},16%,20%);
|
||||
--primary-gradiend-darker: hsl({PublicCacheData.ThemeIndexColour},16%,4%);
|
||||
--light-shadow: hsla({PublicCacheData.ThemeIndexColour},84%,66%,.1);
|
||||
--dark-shadow: hsla({PublicCacheData.ThemeIndexColour},16%,1%,.5);")
|
||||
}
|
||||
else
|
||||
{
|
||||
@($@"--background: hsl({PublicCacheData.ThemeIndexColour},84%,88%);
|
||||
--text-color: hsl({PublicCacheData.ThemeIndexColour},84%,26.4%);
|
||||
--placeholder-text-color: hsla({PublicCacheData.ThemeIndexColour},84%,26.4%,.3);
|
||||
--primary-color: hsl({PublicCacheData.ThemeIndexColour},84%,88%);
|
||||
--primary-color-light: hsl({PublicCacheData.ThemeIndexColour},84%,100%);
|
||||
--primary-color-dark: hsl({PublicCacheData.ThemeIndexColour},84%,66%);
|
||||
--primary-gradiend-light: hsl({PublicCacheData.ThemeIndexColour},84%,92%);
|
||||
--primary-gradiend-dark: hsl({PublicCacheData.ThemeIndexColour},84%,84%);
|
||||
--primary-gradiend-lighter: hsl({PublicCacheData.ThemeIndexColour},84%,96%);
|
||||
--primary-gradiend-darker: hsl({PublicCacheData.ThemeIndexColour},84%,80%);
|
||||
--light-shadow: hsla({PublicCacheData.ThemeIndexColour},84%,100%,.5);
|
||||
--dark-shadow: hsla({PublicCacheData.ThemeIndexColour},84%,66%,.5);")
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<CascadingValue IsFixed=false Value=this>
|
||||
@ChildContent
|
||||
</CascadingValue>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Inject] public IStringLocalizer<AllStrings> Localizer { get; set; }
|
||||
[Inject] IStorage DbStorage { get; set; }
|
||||
[Inject] ILocalStorageService Storage { get; set; }
|
||||
[Inject] IJSRuntime JS { get; set; }
|
||||
// [Inject] DataService DataService { get; set; }
|
||||
[Inject] public AppStatusService Status { get; set; }
|
||||
[Inject] ILogger<CascadingState> Logger { get; set; }
|
||||
|
||||
public bool IsOnline { get; set; } = true;
|
||||
Timer IsOnlineTimer { get; set; }
|
||||
public PublicCacheData PublicCacheData { get; set; }
|
||||
public User User { get; set; }
|
||||
|
||||
DotNetObjectReference<CascadingState> cascadingStateReference;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsOnlineTimer = new Timer(async _ => await UpdateIsOnline(), new AutoResetEvent(false), 0, 10000);
|
||||
cascadingStateReference = DotNetObjectReference.Create(this);
|
||||
await JS.InvokeVoidAsync("cascadingStateInstanceReference", cascadingStateReference);
|
||||
PublicCacheData = await Storage.GetItemAsync<PublicCacheData>(nameof(PublicCacheData));
|
||||
if (PublicCacheData == null)
|
||||
{
|
||||
PublicCacheData = new();
|
||||
await UpdatePublicCache(PublicCacheData);
|
||||
}
|
||||
|
||||
User = new();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, $"{nameof(CascadingState)}.{nameof(OnInitializedAsync)}");
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask UpdatePublicCache(PublicCacheData publicCacheData)
|
||||
{
|
||||
try
|
||||
{
|
||||
PublicCacheData = publicCacheData;
|
||||
await Storage.SetItemAsync(nameof(PublicCacheData), PublicCacheData);
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"{nameof(CascadingState)}.{nameof(UpdatePublicCache)}");
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public async Task LogFromJs(string message, string where)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ProcessError(new(message), where);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, $"{nameof(CascadingState)}.{nameof(LogFromJs)}");
|
||||
}
|
||||
}
|
||||
|
||||
async Task UpdateIsOnline()
|
||||
{
|
||||
try
|
||||
{
|
||||
var latestOnlineState = await Status.IsOnline();
|
||||
|
||||
//var latestOnlineState = Random.Shared.Next() % 2 == 0;
|
||||
if (latestOnlineState != IsOnline)
|
||||
{
|
||||
IsOnline = latestOnlineState;
|
||||
StateHasChanged();
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// var pingIsOnline = await DataService.Ping();
|
||||
// if (pingIsOnline != IsOnline)
|
||||
// {
|
||||
// IsOnline = pingIsOnline;
|
||||
// StateHasChanged();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, $"{nameof(CascadingState)}.{nameof(UpdateIsOnline)}");
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask ProcessError(Exception ex, string where)
|
||||
{
|
||||
try
|
||||
{
|
||||
await DbStorage.AddLog(ex, where);
|
||||
Logger.LogError(ex, where);
|
||||
await Task.Run(() => {
|
||||
});
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.LogError(exception, $"{nameof(CascadingState)}.{nameof(ProcessError)}");
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask ProcessWarning(string message, string where)
|
||||
{
|
||||
try
|
||||
{
|
||||
await DbStorage.AddLog(message, where);
|
||||
Logger.LogWarning("{where} - {message}", where, message);
|
||||
await Task.Run(() => {
|
||||
});
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.LogError(exception, $"{nameof(CascadingState)}.{nameof(ProcessWarning)}");
|
||||
}
|
||||
}
|
||||
}
|
9
LayerComponents/PagesBase.cs
Normal file
9
LayerComponents/PagesBase.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace decePubClient.LayerComponents
|
||||
{
|
||||
public class PagesBase : ComponentBase
|
||||
{
|
||||
public bool IsLoading { get; set; } = true;
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
namespace decePubClient.Models;
|
||||
using decePubClient.Models.Types;
|
||||
|
||||
namespace decePubClient.Models;
|
||||
|
||||
public class ActionBarFilter
|
||||
{
|
||||
|
||||
public TimelineType TimelineType { get; set; } = TimelineType.Home;
|
||||
public TimeSortingType TimeSortingType { get; set; } = TimeSortingType.Ascending;
|
||||
}
|
11
Models/Media.cs
Normal file
11
Models/Media.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace decePubClient.Models
|
||||
{
|
||||
public class Media
|
||||
{
|
||||
public string FileName { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string ContentType { get; set; }
|
||||
public string AltText { get; set; }
|
||||
public byte[] Blob { get; set; } //TODO TEMPORARY
|
||||
}
|
||||
}
|
10
Models/Mention.cs
Normal file
10
Models/Mention.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace decePubClient.Models
|
||||
{
|
||||
public class Mention
|
||||
{
|
||||
public string DisplayName { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public string ProfileUrl { get; set; }
|
||||
}
|
||||
}
|
23
Models/Message.cs
Normal file
23
Models/Message.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System.ComponentModel;
|
||||
using System.Text.Json.Serialization;
|
||||
using decePubClient.Models.Types;
|
||||
|
||||
namespace decePubClient.Models
|
||||
{
|
||||
public class Message
|
||||
{
|
||||
public MessageUser User { get; set; } = new();
|
||||
public MessageType MessageType { get; set; } = MessageType.Public;
|
||||
public string RootMessageId { get; set; }
|
||||
public string MessageId { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Content { get; set; }
|
||||
public bool IsFavourite { get; set; } = false;
|
||||
public bool IsBoosted { get; set; } = false;
|
||||
public List<Media> Medias { get; set; } = new();
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[Bindable(false), JsonIgnore]
|
||||
public bool IsOptionsOpen { get; set; } = false;
|
||||
}
|
||||
}
|
@ -1,6 +1,28 @@
|
||||
namespace decePubClient.Models;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using decePubClient.Models.Types;
|
||||
using decePubClient.Resources;
|
||||
|
||||
namespace decePubClient.Models;
|
||||
|
||||
public class MessageForm
|
||||
{
|
||||
|
||||
public string RootMessageId { get; set; }
|
||||
|
||||
[Required(ErrorMessageResourceName = ValidationNames.Required, ErrorMessageResourceType = typeof(ErrorMessages))]
|
||||
public MessageType MessageType { get; set; } = MessageType.Public;
|
||||
[Required(ErrorMessageResourceName = ValidationNames.Required, ErrorMessageResourceType = typeof(ErrorMessages))]
|
||||
public ContentType ContentType { get; set; } = ContentType.PlainText;
|
||||
[StringLength(64, ErrorMessageResourceName = ValidationNames.MaxLength, ErrorMessageResourceType = typeof(ErrorMessages))]
|
||||
public string Title { get; set; }
|
||||
[StringLength(5_000, ErrorMessageResourceName = ValidationNames.MaxLength, ErrorMessageResourceType = typeof(ErrorMessages))]
|
||||
public string Content { get; set; }
|
||||
public List<UploadMedia> Media { get; set; } = new();
|
||||
|
||||
[JsonIgnore, Bindable(false)]
|
||||
public MediaType MediaType { get; set; } = MediaType.Images;
|
||||
[JsonIgnore, Bindable(false)]
|
||||
public bool IsScopeOptionsOpen { get; set; } = false;
|
||||
}
|
12
Models/MessageUser.cs
Normal file
12
Models/MessageUser.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace decePubClient.Models
|
||||
{
|
||||
public class MessageUser
|
||||
{
|
||||
public string UserId { get; set; } = "45f14fa8-c40f-4121-997c-ef2542196a50";
|
||||
public string UserName { get; set; } = "@loosy@letsrulethe.world";
|
||||
public string DisplayName { get; set; } = "loosy";
|
||||
public string PictureUrl { get; set; } = "https://letsrulethe.world/media/c22d7a6dfcce11e4d2d8d4f6298842a36751b0a179dc5333d24663e4b93793b4.jpg";
|
||||
public string BackgroundUrl { get; set; } = "https://letsrulethe.world/media/717cc7f5a090cfbe77be46941060b9a54454c351c74ff2f056363e002c8e2c3f.png";
|
||||
public string ProfileUrl { get; set; } = "https://letsrulethe.world/users/loosy";
|
||||
}
|
||||
}
|
7
Models/SettingsOptions.cs
Normal file
7
Models/SettingsOptions.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace decePubClient.Models
|
||||
{
|
||||
public class SettingsOptions
|
||||
{
|
||||
public bool IsOpen { get; set; }
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
namespace decePubClient.Models.Types;
|
||||
|
||||
public class ContentType
|
||||
public enum ContentType
|
||||
{
|
||||
|
||||
PlainText,
|
||||
HTML,
|
||||
Markdown
|
||||
}
|
10
Models/Types/MessageType.cs
Normal file
10
Models/Types/MessageType.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace decePubClient.Models.Types
|
||||
{
|
||||
public enum MessageType
|
||||
{
|
||||
Direct,
|
||||
FollowersOnly,
|
||||
Unlisted,
|
||||
Public
|
||||
}
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
namespace decePubClient.Models.Types;
|
||||
|
||||
public enum TimeSortingType { }
|
||||
public enum TimeSortingType
|
||||
{
|
||||
Ascending,
|
||||
Descending
|
||||
}
|
@ -1,6 +1,16 @@
|
||||
namespace decePubClient.Models;
|
||||
using System.ComponentModel;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace decePubClient.Models;
|
||||
|
||||
public class UploadMedia
|
||||
{
|
||||
|
||||
public string ContentType { get; set; }
|
||||
public string FileName { get; set; }
|
||||
public string AltText { get; set; }
|
||||
public byte[] Blob { get; set; }
|
||||
public string Base64Preview { get; set; }
|
||||
|
||||
[JsonIgnore, Bindable(false)]
|
||||
public long Size { get; set; }
|
||||
}
|
21
Models/User.cs
Normal file
21
Models/User.cs
Normal file
@ -0,0 +1,21 @@
|
||||
namespace decePubClient.Models
|
||||
{
|
||||
public class User
|
||||
{
|
||||
public string Id { get; set; } = "45f14fa8-c40f-4121-997c-ef2542196a50";
|
||||
public bool IsAuthenticated { get; set; } = false;
|
||||
public List<UserClaim> Claims { get; set; } = new();
|
||||
|
||||
public string UserName { get; set; } = "@loosy@letsrulethe.world";
|
||||
public string DisplayName { get; set; } = "loosy";
|
||||
public string PictureUrl { get; set; } = "https://letsrulethe.world/media/c22d7a6dfcce11e4d2d8d4f6298842a36751b0a179dc5333d24663e4b93793b4.jpg";
|
||||
public string BackgroundUrl { get; set; } = "https://letsrulethe.world/media/717cc7f5a090cfbe77be46941060b9a54454c351c74ff2f056363e002c8e2c3f.png";
|
||||
public string ProfileUrl { get; set; } = "https://letsrulethe.world/users/loosy";
|
||||
}
|
||||
|
||||
public class UserClaim
|
||||
{
|
||||
public string Type { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
7
Models/VConstants.cs
Normal file
7
Models/VConstants.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace decePubClient.Models
|
||||
{
|
||||
public static class VConstants
|
||||
{
|
||||
public const string HideClass = "hidden";
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
@page "/administration"
|
||||
@inherits PagesBase
|
||||
|
||||
<section class="block relative w-full h-full neomorphInset is-nxsmall rounded-xl">
|
||||
|
||||
|
23
Pages/Authentication.razor
Normal file
23
Pages/Authentication.razor
Normal file
@ -0,0 +1,23 @@
|
||||
@page "/authentication/{action}"
|
||||
@inherits PagesBase
|
||||
|
||||
|
||||
<section class="block relative w-full h-full neomorphInset is-nxsmall rounded-xl">
|
||||
|
||||
<div class="flex flex-col space-y-4 p-4 md:p-5 w-full h-full absolute overflow-y-auto">
|
||||
|
||||
<RemoteAuthenticatorView Action="@Action" />
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public string Action { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
@page "/counter"
|
||||
|
||||
<PageTitle>Counter</PageTitle>
|
||||
|
||||
<h1>Counter</h1>
|
||||
|
||||
<p role="status">Current count: @currentCount</p>
|
||||
|
||||
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
||||
|
||||
@code {
|
||||
private int currentCount = 0;
|
||||
|
||||
private void IncrementCount()
|
||||
{
|
||||
currentCount++;
|
||||
}
|
||||
}
|
@ -1,42 +1,207 @@
|
||||
@page "/expand/{messageId}"
|
||||
<Title>@Localizer</Title>
|
||||
@page "/expand"
|
||||
@page "/expand/{messageId}"
|
||||
@inherits PagesBase
|
||||
<Title>@CascadingState.Localizer</Title>
|
||||
|
||||
<section class="block relative w-full h-full neomorphInset is-nxsmall rounded-xl">
|
||||
<div class="flex w-full h-full flex-col space-y-4">
|
||||
|
||||
<div class="flex flex-col space-y-4 p-4 md:p-5 w-full h-full absolute overflow-y-auto">
|
||||
<section class="block relative w-full h-full neomorphInset is-nxsmall rounded-xl">
|
||||
<div class="flex flex-col space-y-4 p-4 md:p-5 w-full h-full absolute overflow-y-auto @SUtility.IfTrueThen(TimeSortingType is TimeSortingType.Descending, "flex-col-reverse")">
|
||||
|
||||
</div>
|
||||
@if (IsLoading)
|
||||
{
|
||||
<LoadingData/>
|
||||
}
|
||||
|
||||
</section>
|
||||
@foreach (var message in Ancestors)
|
||||
{
|
||||
<Content CssContainer="ml-6 w-auto" IncludeExpand="false" Message="message"
|
||||
OnMessageBoost="async (m) => await OnMessageBoost(m)" OnMessageDelete="async (m) => await OnMessageDelete(m)"
|
||||
OnMessageFavourite="async (m) => await OnMessageFavourite(m)" OnMessageReply="async (m) => await OnMessageReply(m)"
|
||||
OnMessageMediaDownload="async (m) => await OnMessageMediaDownload(m)"
|
||||
OnUserBlock="async (u) => await OnUserBlock(u)" OnUserDirectMessage="async (u) => await OnUserDirectMessage(u)" OnUserSilence="async (u) => await OnUserSilence(u)" />
|
||||
}
|
||||
|
||||
<Content CssContainer="is-nsmall" IncludeExpand="false" Message="CurrentMessage"
|
||||
OnMessageBoost="async (m) => await OnMessageBoost(m)" OnMessageDelete="async (m) => await OnMessageDelete(m)"
|
||||
OnMessageFavourite="async (m) => await OnMessageFavourite(m)" OnMessageReply="async (m) => await OnMessageReply(m)"
|
||||
OnMessageMediaDownload="async (m) => await OnMessageMediaDownload(m)"
|
||||
OnUserBlock="async (u) => await OnUserBlock(u)" OnUserDirectMessage="async (u) => await OnUserDirectMessage(u)" OnUserSilence="async (u) => await OnUserSilence(u)" />
|
||||
|
||||
@foreach (var message in Descendants)
|
||||
{
|
||||
<Content CssContainer="ml-6 w-auto" IncludeExpand="false" Message="message"
|
||||
OnMessageBoost="async (m) => await OnMessageBoost(m)" OnMessageDelete="async (m) => await OnMessageDelete(m)"
|
||||
OnMessageFavourite="async (m) => await OnMessageFavourite(m)" OnMessageReply="async (m) => await OnMessageReply(m)"
|
||||
OnMessageMediaDownload="async (m) => await OnMessageMediaDownload(m)"
|
||||
OnUserBlock="async (u) => await OnUserBlock(u)" OnUserDirectMessage="async (u) => await OnUserDirectMessage(u)" OnUserSilence="async (u) => await OnUserSilence(u)" />
|
||||
}
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] IStringLocalizer<AllStrings> Localizer { get; set; }
|
||||
[CascadingParameter] CascadingState CascadingState { get; set; }
|
||||
[Inject] NavigationManager Navigation { get; set; }
|
||||
[Inject] IStorage DbStorage { get; set; }
|
||||
[SupplyParameterFromQuery] string messageId { get; set; }
|
||||
[Inject] ILocalStorageService LocalStorage { get; set; }
|
||||
[Inject] IBlazorDownloadFileService BlazorDownloadFileService { get; set; }
|
||||
[Inject] MessagesService MessagesService { get; set; }
|
||||
[Parameter] public string MessageId { get; set; }
|
||||
|
||||
List<Message> Messages { get; set; } = new();
|
||||
List<Message> Ancestors { get; set; } = new();
|
||||
Message CurrentMessage { get; set; } = new();
|
||||
List<Message> Descendants { get; set; } = new();
|
||||
|
||||
TimeSortingType TimeSortingType { get; set; } = TimeSortingType.Ascending;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (messageId is { Length: 0 })
|
||||
if (MessageId is { Length: 0 })
|
||||
{
|
||||
Navigation.NavigateTo("/");
|
||||
return;
|
||||
}
|
||||
|
||||
var currentMessage = await DbStorage.GetMessage(messageId);
|
||||
|
||||
var messages = await DbStorage.GetMessages();
|
||||
|
||||
if (currentMessage.RootMessageId is { Length: > 0 })
|
||||
Messages = messages.Where(m => m.RootMessageId == currentMessage.RootMessageId)
|
||||
.OrderByDescending(m => m.CreatedAt)
|
||||
.ToList();
|
||||
var filters = await LocalStorage.GetItemAsync<ActionBarFilter>(nameof(ActionBarFilter));
|
||||
if (filters == default)
|
||||
await LocalStorage.SetItemAsync(nameof(ActionBarFilter), new ActionBarFilter());
|
||||
else
|
||||
Messages = messages.Where(m => m.RootMessageId == messageId)
|
||||
.OrderByDescending(m => m.CreatedAt)
|
||||
.ToList();
|
||||
TimeSortingType = filters.TimeSortingType;
|
||||
|
||||
CurrentMessage = await DbStorage.GetMessage(MessageId);
|
||||
|
||||
Ancestors = new List<Message>
|
||||
{
|
||||
new()
|
||||
{
|
||||
RootMessageId = default,
|
||||
MessageId = "51C698E3-7C28-4C90-9212-48D5C81DE089",
|
||||
User = Faker.GetRandomUser(),
|
||||
MessageType = (MessageType)Random.Shared.Next(0, 4),
|
||||
Title = CurrentMessage?.Title,
|
||||
Content = "<p>esempio di messaggio <b>precedente</b> a quello espanso</p>",
|
||||
Medias = new(),
|
||||
CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
|
||||
IsFavourite = Random.Shared.Next() % 2 == 0,
|
||||
IsBoosted = Random.Shared.Next() % 2 == 0,
|
||||
}
|
||||
};
|
||||
|
||||
Descendants = new List<Message>
|
||||
{
|
||||
new()
|
||||
{
|
||||
RootMessageId = default,
|
||||
MessageId = "0569A1DF-46FC-4485-9F11-F8CDFB794404",
|
||||
User = Faker.GetRandomUser(),
|
||||
MessageType = (MessageType)Random.Shared.Next(0, 4),
|
||||
Title = CurrentMessage?.Title,
|
||||
Content = "<p>esempio di messaggio <b>successivo</b> a quello espanso</p>",
|
||||
Medias = new(),
|
||||
CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
|
||||
IsFavourite = Random.Shared.Next() % 2 == 0,
|
||||
IsBoosted = Random.Shared.Next() % 2 == 0,
|
||||
}
|
||||
};
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
void OnTimeSortingChanged(TimeSortingType timeSortingType)
|
||||
{
|
||||
TimeSortingType = timeSortingType;
|
||||
}
|
||||
|
||||
async Task OnMessageReply(MessageForm messageForm)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
});
|
||||
var replyMessage = await MessagesService.SubmitMessage(messageForm);
|
||||
Descendants.Add(new()
|
||||
{
|
||||
MessageId = Guid.NewGuid().ToString(),
|
||||
Content = messageForm.Content?.ParseContent(messageForm.ContentType),
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
MessageType = messageForm.MessageType,
|
||||
Medias = messageForm.Media.Select(m => new Media
|
||||
{
|
||||
FileName = m.FileName,
|
||||
AltText = m.AltText,
|
||||
ContentType = m.ContentType,
|
||||
Url = m.Base64Preview,
|
||||
Blob = m.Blob
|
||||
}).ToList(),
|
||||
Title = messageForm.Title,
|
||||
User = new MessageUser
|
||||
{
|
||||
UserId = CascadingState.User.Id,
|
||||
UserName = CascadingState.User.UserName,
|
||||
DisplayName = CascadingState.User.DisplayName,
|
||||
PictureUrl = CascadingState.User.PictureUrl,
|
||||
ProfileUrl = CascadingState.User.ProfileUrl,
|
||||
BackgroundUrl = CascadingState.User.BackgroundUrl
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async ValueTask OnMessageBoost(Message message)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
});
|
||||
message.IsBoosted = !message.IsBoosted;
|
||||
var boostedMessage = await MessagesService.BoostUnboostMessage(message);
|
||||
}
|
||||
|
||||
async ValueTask OnMessageFavourite(Message message)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
});
|
||||
message.IsFavourite = !message.IsFavourite;
|
||||
var favouriteMessage = await MessagesService.FavouriteUnfavouriteMessage(message);
|
||||
}
|
||||
|
||||
async ValueTask OnMessageDelete(Message message)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
});
|
||||
var deleteResult = await MessagesService.DeleteMessage(message);
|
||||
}
|
||||
|
||||
async ValueTask OnMessageMediaDownload(Media media)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
});
|
||||
await BlazorDownloadFileService.DownloadFileAsync(media.FileName, media.Blob, media.ContentType);
|
||||
}
|
||||
|
||||
async ValueTask OnUserBlock(MessageUser messageUser)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
});
|
||||
var blockResult = await MessagesService.BlockUserFromMessage(messageUser);
|
||||
}
|
||||
|
||||
async ValueTask OnUserDirectMessage(Message message)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
});
|
||||
var directMessage = await MessagesService.ReplyMessage(message, MessageType.Direct);
|
||||
}
|
||||
|
||||
async ValueTask OnUserSilence(MessageUser messageUser)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
});
|
||||
var silenceResult = await MessagesService.SilenceUserFromMessage(messageUser);
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
@page "/fetchdata"
|
||||
@inject HttpClient Http
|
||||
|
||||
<PageTitle>Weather forecast</PageTitle>
|
||||
|
||||
<h1>Weather forecast</h1>
|
||||
|
||||
<p>This component demonstrates fetching data from the server.</p>
|
||||
|
||||
@if (forecasts == null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Temp. (C)</th>
|
||||
<th>Temp. (F)</th>
|
||||
<th>Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var forecast in forecasts)
|
||||
{
|
||||
<tr>
|
||||
<td>@forecast.Date.ToShortDateString()</td>
|
||||
<td>@forecast.TemperatureC</td>
|
||||
<td>@forecast.TemperatureF</td>
|
||||
<td>@forecast.Summary</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
@code {
|
||||
private WeatherForecast[]? forecasts;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");
|
||||
}
|
||||
|
||||
public class WeatherForecast
|
||||
{
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
public int TemperatureC { get; set; }
|
||||
|
||||
public string? Summary { get; set; }
|
||||
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
||||
}
|
@ -1,9 +1,225 @@
|
||||
@page "/"
|
||||
@page "/home"
|
||||
@page "/index"
|
||||
@inherits PagesBase
|
||||
<Title>@CascadingState.Localizer["Index"]</Title>
|
||||
|
||||
<PageTitle>Index</PageTitle>
|
||||
<div class="flex w-full h-full flex-col space-y-4">
|
||||
|
||||
<h1>Hello, world!</h1>
|
||||
<section class="block relative w-full h-full neomorphInset is-nxsmall rounded-xl">
|
||||
<div class="flex flex-col space-y-4 p-4 md:p-5 w-full h-full absolute overflow-y-auto @SUtility.IfTrueThen(TimeSortingType is TimeSortingType.Descending, "flex-col-reverse")">
|
||||
|
||||
Welcome to your new app.
|
||||
@if (IsLoading)
|
||||
{
|
||||
<LoadingData/>
|
||||
}
|
||||
else if (Messages.Count == 0)
|
||||
{
|
||||
<p class="w-full text-center text-lg">
|
||||
<i class="ion-ios-remove-circle-outline"></i> @CascadingState.Localizer["Empty"]
|
||||
</p>
|
||||
}
|
||||
|
||||
<SurveyPrompt Title="How is Blazor working for you?" />
|
||||
@foreach (var message in Messages)
|
||||
{
|
||||
<Content Message="message"
|
||||
OnMessageBoost="async (m) => await OnMessageBoost(m)" OnMessageDelete="async (m) => await OnMessageDelete(m)"
|
||||
OnMessageFavourite="async (m) => await OnMessageFavourite(m)" OnMessageReply="async (m) => await OnMessageReply(m)"
|
||||
OnMessageMediaDownload="async (m) => await OnMessageMediaDownload(m)"
|
||||
OnUserBlock="async (u) => await OnUserBlock(u)" OnUserDirectMessage="async (u) => await OnUserDirectMessage(u)" OnUserSilence="async (u) => await OnUserSilence(u)" />
|
||||
}
|
||||
@*<AuthorizeView>
|
||||
<Authorized>
|
||||
@foreach (var message in Messages)
|
||||
{
|
||||
<Content Message="message"
|
||||
OnMessageReply="(m) => OnMessageReply(m)" OnMessageBoost="(m) => OnMessageBoost(m)"
|
||||
OnMessageFavourite="(m) => OnMessageFavourite(m)" OnMessageDelete="(m) => OnMessageDelete(m)"
|
||||
OnUserBlock="(u) => OnUserBlock(u)" OnUserDirectMessage="(u) => OnUserDirectMessage(u)" OnUserMute="(u) => OnUserMute(u)" />
|
||||
}
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
@foreach (var message in Messages)
|
||||
{
|
||||
<Content Message="message" OnMessageReply="(m) => OnMessageReply(m)" />
|
||||
}
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>*@
|
||||
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<ActionBar OnMessageSubmit="OnMessageSubmit" OnTimelineChanged="OnTimelineChanged" OnTimeSortingChanged="OnTimeSortingChanged" />
|
||||
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] Task<AuthenticationState> AuthState { get; set; }
|
||||
[CascadingParameter] CascadingState CascadingState { get; set; }
|
||||
[Inject] IBlazorDownloadFileService BlazorDownloadFileService { get; set; }
|
||||
[Inject] IStorage Storage { get; set; }
|
||||
[Inject] ILocalStorageService LocalStorage { get; set; }
|
||||
[Inject] HttpClient Http { get; set; }
|
||||
[Inject] MessagesService MessagesService { get; set; }
|
||||
|
||||
TimelineType TimelineType { get; set; } = TimelineType.Home;
|
||||
TimeSortingType TimeSortingType { get; set; } = TimeSortingType.Ascending;
|
||||
|
||||
IReadOnlyList<Claim> userClaim
|
||||
{
|
||||
get
|
||||
{
|
||||
var auth = AuthState.Result;
|
||||
if (auth.User.Identity?.IsAuthenticated ?? false)
|
||||
return auth.User.Claims.ToList();
|
||||
return new List<Claim>();
|
||||
}
|
||||
}
|
||||
|
||||
List<Message> IncomingMessages { get; set; } = new();
|
||||
List<Message> Messages { get; set; } = new();
|
||||
string Response { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var filters = await LocalStorage.GetItemAsync<ActionBarFilter>(nameof(ActionBarFilter));
|
||||
if (filters == default)
|
||||
await LocalStorage.SetItemAsync(nameof(ActionBarFilter), new ActionBarFilter());
|
||||
else
|
||||
{
|
||||
TimelineType = filters.TimelineType;
|
||||
TimeSortingType = filters.TimeSortingType;
|
||||
}
|
||||
|
||||
Messages = await Storage.GetMessages();
|
||||
//try
|
||||
//{
|
||||
// var response = await Http.GetAsync("api/test");
|
||||
// Response = await response.Content.ReadAsStringAsync();
|
||||
//}
|
||||
//catch
|
||||
//{
|
||||
//}
|
||||
IsLoading = false;
|
||||
}
|
||||
|
||||
void OnTimelineChanged(TimelineType timelineType)
|
||||
{
|
||||
TimelineType = timelineType;
|
||||
}
|
||||
|
||||
void OnTimeSortingChanged(TimeSortingType timeSortingType)
|
||||
{
|
||||
TimeSortingType = timeSortingType;
|
||||
}
|
||||
|
||||
async Task OnMessageSubmit(MessageForm messageForm)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
});
|
||||
var submitMessage = await MessagesService.SubmitMessage(messageForm);
|
||||
Messages.Insert(0, new()
|
||||
{
|
||||
MessageId = Guid.NewGuid().ToString(),
|
||||
Content = messageForm.Content?.ParseContent(messageForm.ContentType),
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
MessageType = messageForm.MessageType,
|
||||
Medias = messageForm.Media.Select(m => new Media
|
||||
{
|
||||
FileName = m.FileName,
|
||||
AltText = m.AltText,
|
||||
ContentType = m.ContentType,
|
||||
Url = m.Base64Preview,
|
||||
Blob = m.Blob
|
||||
}).ToList(),
|
||||
Title = messageForm.Title,
|
||||
User = new MessageUser
|
||||
{
|
||||
UserId = CascadingState.User.Id,
|
||||
UserName = CascadingState.User.UserName,
|
||||
DisplayName = CascadingState.User.DisplayName,
|
||||
PictureUrl = CascadingState.User.PictureUrl,
|
||||
ProfileUrl = CascadingState.User.ProfileUrl,
|
||||
BackgroundUrl = CascadingState.User.BackgroundUrl
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async Task OnMessageReply(MessageForm messageForm)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
});
|
||||
var replyMessage = await MessagesService.SubmitMessage(messageForm);
|
||||
Messages.Insert(0, new()
|
||||
{
|
||||
RootMessageId = messageForm.RootMessageId,
|
||||
MessageId = Guid.NewGuid().ToString(),
|
||||
Content = messageForm.Content?.ParseContent(messageForm.ContentType),
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
MessageType = messageForm.MessageType,
|
||||
Medias = messageForm.Media.Select(m => new Media
|
||||
{
|
||||
FileName = m.FileName,
|
||||
AltText = m.AltText,
|
||||
ContentType = m.ContentType,
|
||||
Url = m.Base64Preview,
|
||||
Blob = m.Blob
|
||||
}).ToList(),
|
||||
Title = messageForm.Title,
|
||||
User = new MessageUser
|
||||
{
|
||||
UserId = CascadingState.User.Id,
|
||||
UserName = CascadingState.User.UserName,
|
||||
DisplayName = CascadingState.User.DisplayName,
|
||||
PictureUrl = CascadingState.User.PictureUrl,
|
||||
ProfileUrl = CascadingState.User.ProfileUrl,
|
||||
BackgroundUrl = CascadingState.User.BackgroundUrl
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async ValueTask OnMessageBoost(Message message)
|
||||
{
|
||||
message.IsBoosted = !message.IsBoosted;
|
||||
var boostedMessage = await MessagesService.BoostUnboostMessage(message);
|
||||
}
|
||||
|
||||
async ValueTask OnMessageFavourite(Message message)
|
||||
{
|
||||
message.IsFavourite = !message.IsFavourite;
|
||||
var favouriteMessage = await MessagesService.FavouriteUnfavouriteMessage(message);
|
||||
}
|
||||
|
||||
async ValueTask OnMessageDelete(Message message)
|
||||
{
|
||||
Messages.Remove(message);
|
||||
var deleteResult = await MessagesService.DeleteMessage(message);
|
||||
}
|
||||
|
||||
async ValueTask OnMessageMediaDownload(Media media)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
});
|
||||
await BlazorDownloadFileService.DownloadFileAsync(media.FileName, media.Blob, media.ContentType);
|
||||
}
|
||||
|
||||
async ValueTask OnUserBlock(MessageUser messageUser)
|
||||
{
|
||||
var blockResult = await MessagesService.BlockUserFromMessage(messageUser);
|
||||
}
|
||||
|
||||
async ValueTask OnUserDirectMessage(Message message)
|
||||
{
|
||||
var directMessage = await MessagesService.ReplyMessage(message, MessageType.Direct);
|
||||
}
|
||||
|
||||
async ValueTask OnUserSilence(MessageUser messageUser)
|
||||
{
|
||||
var silenceResult = await MessagesService.SilenceUserFromMessage(messageUser);
|
||||
}
|
||||
|
||||
}
|
16
Pages/Login.razor
Normal file
16
Pages/Login.razor
Normal file
@ -0,0 +1,16 @@
|
||||
@page "/login"
|
||||
@inherits PagesBase
|
||||
<Title>@CascadingState.Localizer["Login"]</Title>
|
||||
|
||||
<section class="block relative w-full h-full neomorphInset is-nxsmall rounded-xl">
|
||||
|
||||
<div class="flex flex-col space-y-4 p-4 md:p-5 w-full h-full absolute overflow-y-auto">
|
||||
<LoginDisplay />
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] CascadingState CascadingState { get; set; }
|
||||
|
||||
}
|
25
Pages/Logout.razor
Normal file
25
Pages/Logout.razor
Normal file
@ -0,0 +1,25 @@
|
||||
@page "/logout"
|
||||
@inherits PagesBase
|
||||
<Title>@CascadingState.Localizer["Logout"]</Title>
|
||||
|
||||
<section class="block relative w-full h-full neomorphInset is-nxsmall rounded-xl">
|
||||
|
||||
<div class="flex flex-col space-y-4 p-4 md:p-5 w-full h-full absolute overflow-y-auto">
|
||||
<h1 class="text-center">@CascadingState.Localizer["Logging out..."]</h1>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] CascadingState CascadingState { get; set; }
|
||||
//[Inject] TokenAuthStateProvider AuthStateProvider { get; set; }
|
||||
[Inject] SignOutSessionStateManager SignOutSessionStateManager { get; set; }
|
||||
[Inject] NavigationManager Navigation { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
//await AuthStateProvider.LogoutAsync(true);
|
||||
await SignOutSessionStateManager.SetSignOutState();
|
||||
Navigation.NavigateTo("authenticatio/logout");
|
||||
}
|
||||
}
|
75
Pages/Settings.razor
Normal file
75
Pages/Settings.razor
Normal file
@ -0,0 +1,75 @@
|
||||
@page "/settings"
|
||||
@inherits PagesBase
|
||||
<Title>@CascadingState.Localizer["Settings"]</Title>
|
||||
|
||||
<section class="block relative w-full h-full neomorphInset is-nxsmall rounded-xl">
|
||||
|
||||
<div class="flex flex-col space-y-4 p-4 md:p-5 w-full h-full absolute overflow-y-auto">
|
||||
|
||||
<OpenDownContainer>
|
||||
<TitleChildren>
|
||||
<p class="inline-flex items-center space-x-2">
|
||||
<i class="ion-md-cog text-lg"></i> @CascadingState.Localizer["General"]
|
||||
</p>
|
||||
</TitleChildren>
|
||||
<InnerContent>
|
||||
<div class="block w-full p-3 md:p-4">
|
||||
<p class="w-full text-center text-lg">
|
||||
<i class="ion-ios-remove-circle-outline"></i> @CascadingState.Localizer["Empty"]
|
||||
</p>
|
||||
</div>
|
||||
</InnerContent>
|
||||
</OpenDownContainer>
|
||||
|
||||
<OpenDownContainer>
|
||||
<TitleChildren>
|
||||
<p class="inline-flex items-center space-x-2">
|
||||
<i class="ion-md-person text-lg"></i> @CascadingState.Localizer["Profile"]
|
||||
</p>
|
||||
</TitleChildren>
|
||||
<InnerContent>
|
||||
<div class="block w-full p-3 md:p-4">
|
||||
<p class="w-full text-center text-lg">
|
||||
<i class="ion-ios-remove-circle-outline"></i> @CascadingState.Localizer["Empty"]
|
||||
</p>
|
||||
</div>
|
||||
</InnerContent>
|
||||
</OpenDownContainer>
|
||||
|
||||
<OpenDownContainer>
|
||||
<TitleChildren>
|
||||
<p class="inline-flex items-center space-x-2">
|
||||
<i class="ion-md-archive text-lg"></i> @CascadingState.Localizer["Data import/export"]
|
||||
</p>
|
||||
</TitleChildren>
|
||||
<InnerContent>
|
||||
<div class="block w-full p-3 md:p-4">
|
||||
<p class="w-full text-center text-lg">
|
||||
<i class="ion-ios-remove-circle-outline"></i> @CascadingState.Localizer["Empty"]
|
||||
</p>
|
||||
</div>
|
||||
</InnerContent>
|
||||
</OpenDownContainer>
|
||||
|
||||
<OpenDownContainer>
|
||||
<TitleChildren>
|
||||
<p class="inline-flex items-center space-x-2">
|
||||
<i class="ion-md-eye-off text-lg"></i> @CascadingState.Localizer["Mutes/Blocks"]
|
||||
</p>
|
||||
</TitleChildren>
|
||||
<InnerContent>
|
||||
<div class="block w-full p-3 md:p-4">
|
||||
<p class="w-full text-center text-lg">
|
||||
<i class="ion-ios-remove-circle-outline"></i> @CascadingState.Localizer["Empty"]
|
||||
</p>
|
||||
</div>
|
||||
</InnerContent>
|
||||
</OpenDownContainer>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] CascadingState CascadingState { get; set; }
|
||||
}
|
68
Program.cs
68
Program.cs
@ -1,12 +1,72 @@
|
||||
using decePubClient;
|
||||
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text.Json;
|
||||
using Append.Blazor.Notifications;
|
||||
using Blazored.LocalStorage;
|
||||
using Blazored.Modal;
|
||||
using decePubClient.Extensions;
|
||||
using decePubClient.Services;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
|
||||
using Toolbelt.Blazor.Extensions.DependencyInjection;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
builder.RootComponents.Add<App>("#app");
|
||||
builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||
// builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||
|
||||
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||
builder.Services.AddBlazorDownloadFile();
|
||||
builder.Services.AddOptions()
|
||||
.AddTransient<AppStatusService>();
|
||||
builder.Services.AddBlazoredLocalStorage(config =>
|
||||
{
|
||||
config.JsonSerializerOptions.WriteIndented = false;
|
||||
config.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
|
||||
config.JsonSerializerOptions.IgnoreReadOnlyProperties = true;
|
||||
config.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip;
|
||||
config.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
||||
})
|
||||
.AddBlazoredModal()
|
||||
//.AddScoped<TokenAuthStateProvider>()
|
||||
.AddScoped<CustomAuthenticationMessageHandler>()
|
||||
.AddScoped<MessagesService>()
|
||||
//.AddScoped<AuthenticationStateProvider>(provider => provider.GetRequiredService<TokenAuthStateProvider>())
|
||||
//.AddTransient<IHttpService, HttpService>()
|
||||
.AddHeadElementHelper(options => options.DisableClientScriptAutoInjection = true)
|
||||
.AddLocalization()
|
||||
.AddNotifications()
|
||||
.AddTransient<IStorage, Storage>()
|
||||
.AddIndexedDb();
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
|
||||
builder.Services.AddOidcAuthentication(options =>
|
||||
{
|
||||
builder.Configuration.Bind("Local", options.ProviderOptions);
|
||||
options.ProviderOptions.Authority = "https://demo.identityserver.io";
|
||||
options.ProviderOptions.ClientId = "interactive.public";
|
||||
options.ProviderOptions.ResponseType = "code";
|
||||
options.ProviderOptions.DefaultScopes.Add("api");
|
||||
options.ProviderOptions.DefaultScopes.Add("email");
|
||||
options.ProviderOptions.DefaultScopes.Add("profile");
|
||||
});
|
||||
|
||||
builder.Services.AddHttpClient("default", client =>
|
||||
{
|
||||
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress);
|
||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
});
|
||||
|
||||
builder.Services.AddHttpClient("ComponentsWebAssembly_CSharp.ServerAPI", client =>
|
||||
{
|
||||
client.BaseAddress = new Uri("https://demo.identityserver.io");
|
||||
})
|
||||
//.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>()
|
||||
.AddHttpMessageHandler<CustomAuthenticationMessageHandler>();
|
||||
|
||||
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("ComponentsWebAssembly_CSharp.ServerAPI"));
|
||||
builder.Services.AddScoped<MessagesService>();
|
||||
|
||||
var host = builder.Build();
|
||||
await host.SetDefaultCulture();
|
||||
await host.RunAsync();
|
72
Resources/ErrorMessages.Designer.cs
generated
Normal file
72
Resources/ErrorMessages.Designer.cs
generated
Normal file
@ -0,0 +1,72 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace decePubClient.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class ErrorMessages {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal ErrorMessages() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("decePubClient.Resources.ErrorMessages", typeof(ErrorMessages).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This field is required..
|
||||
/// </summary>
|
||||
public static string Required {
|
||||
get {
|
||||
return ResourceManager.GetString("Required", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
namespace decePubClient.Resources;
|
||||
|
||||
public class ErrorMessages
|
||||
{
|
||||
|
||||
}
|
@ -1,21 +1,123 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Required" xml:space="preserve">
|
||||
<value>This field is required.</value>
|
||||
</data>
|
||||
</root>
|
@ -0,0 +1,92 @@
|
||||
body {
|
||||
color: var(--text-color);
|
||||
background-color: var(--background);
|
||||
}
|
||||
|
||||
.background {
|
||||
background-color: var(--background);
|
||||
}
|
||||
|
||||
details > summary::-webkit-details-marker {
|
||||
color: var(--text-color)
|
||||
}
|
||||
|
||||
:focus-visible {
|
||||
outline: none
|
||||
}
|
||||
|
||||
*, ::after, ::before {
|
||||
scrollbar-color: inherit;
|
||||
scrollbar-width: inherit;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button {
|
||||
width: 8px;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
border: thin solid transparent;
|
||||
box-shadow: none;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--primary-color-dark);
|
||||
border: thin solid transparent;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--primary-color-dark);
|
||||
}
|
||||
|
||||
::-moz-selection { /* Code for Firefox */
|
||||
color: var(--primary-color);
|
||||
background: var(--primary-color-dark);
|
||||
}
|
||||
|
||||
::selection {
|
||||
color: var(--primary-color);
|
||||
background: var(--primary-color-dark);
|
||||
}
|
||||
|
||||
.flex.flex-col-reverse>div:first-child {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.loadAnimation span {
|
||||
animation-name: blink;
|
||||
animation-duration: 1.4s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.loadAnimation span:nth-child(1) {
|
||||
animation-delay: .4s;
|
||||
}
|
||||
|
||||
.loadAnimation span:nth-child(2) {
|
||||
animation-delay: .3s;
|
||||
}
|
||||
|
||||
.loadAnimation span:nth-child(3) {
|
||||
animation-delay: .2s;
|
||||
}
|
||||
|
||||
.loadAnimation span:nth-child(4) {
|
||||
animation-delay: .2s;
|
||||
}
|
||||
|
||||
.loadAnimation span:nth-child(5) {
|
||||
animation-delay: .3s;
|
||||
}
|
||||
|
||||
.loadAnimation span:nth-child(6) {
|
||||
animation-delay: .4s;
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
@import "mixins.scss";
|
||||
@import "variables.scss";
|
||||
@import "neomorph.scss";
|
||||
@import "base.scss";
|
@ -1,57 +1,32 @@
|
||||
//MEDIA QUERY MANAGER
|
||||
|
||||
/*$breakpoint argument choices:
|
||||
- phone
|
||||
- tab-port
|
||||
- tab-land
|
||||
- desk
|
||||
- big-desktop
|
||||
*/
|
||||
|
||||
@mixin MediaQuery($breakpoint) {
|
||||
// max-width:768px;
|
||||
@if $breakpoint==phone {
|
||||
@media screen and (max-width: 768px) {
|
||||
@if $breakpoint==sm {
|
||||
@media (min-width: 640px) {
|
||||
@content
|
||||
}
|
||||
}
|
||||
|
||||
@if $breakpoint==tab-p {
|
||||
// min-width:768px;
|
||||
// max-width:900px;
|
||||
@media screen and (min-width: 768px) and (max-width: 900px) {
|
||||
@if $breakpoint==md {
|
||||
@media (min-width: 768px) {
|
||||
@content
|
||||
}
|
||||
}
|
||||
|
||||
@if $breakpoint==tab-l {
|
||||
// min-width:901px;
|
||||
// max-width:1200px;
|
||||
@media screen and (min-width: 901px) and (max-width: 1200px) {
|
||||
@if $breakpoint==lg {
|
||||
@media (min-width: 1024px) {
|
||||
@content
|
||||
}
|
||||
}
|
||||
|
||||
@if $breakpoint==laptop {
|
||||
// min-width:1201px;
|
||||
// max-width:1366px;
|
||||
@media screen and (min-width: 1201px) and (max-width: 1366px) {
|
||||
@if $breakpoint==xl {
|
||||
@media (min-width: 1280px) {
|
||||
@content
|
||||
}
|
||||
}
|
||||
|
||||
@if $breakpoint==desk {
|
||||
// min-width:1201px;
|
||||
// max-width:1800px;
|
||||
@media screen and (min-width: 1201px) and (max-width: 1800px) {
|
||||
@content
|
||||
}
|
||||
}
|
||||
|
||||
@if $breakpoint==big-d {
|
||||
// min-width:1801px;
|
||||
// max-width:4000px;
|
||||
@media screen and (min-width: 1801px) and (max-width: 4000px) {
|
||||
@if $breakpoint=="2xl" {
|
||||
@media (min-width: 1536px) {
|
||||
@content
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,653 @@
|
||||
:root {
|
||||
--shadow-offset: 8px;
|
||||
--blur-radius: 16px;
|
||||
--is-inset: inherit;
|
||||
}
|
||||
|
||||
.neomorph {
|
||||
box-shadow: calc(-1 * var(--shadow-offset)) calc(-1 * var(--shadow-offset)) var(--blur-radius) var(--light-shadow), var(--shadow-offset) var(--shadow-offset) var(--blur-radius) var(--dark-shadow);
|
||||
|
||||
&Inset {
|
||||
box-shadow: inset var(--shadow-offset) var(--shadow-offset) var(--blur-radius) var(--dark-shadow), inset calc(-1 * var(--shadow-offset)) calc(-1 * var(--shadow-offset)) var(--blur-radius) var(--light-shadow);
|
||||
|
||||
&.is-nxxsmall {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px;
|
||||
|
||||
&-sm {
|
||||
@include MediaQuery(sm) {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&-md {
|
||||
@include MediaQuery(md) {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&-lg {
|
||||
@include MediaQuery(lg) {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&-xl {
|
||||
@include MediaQuery(xl) {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&-2xl {
|
||||
@include MediaQuery(2xl) {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-nxsmall {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px;
|
||||
|
||||
&-sm {
|
||||
@include MediaQuery(sm) {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&-md {
|
||||
@include MediaQuery(md) {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&-lg {
|
||||
@include MediaQuery(lg) {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&-xl {
|
||||
@include MediaQuery(xl) {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&-2xl {
|
||||
@include MediaQuery(2xl) {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-nsmall {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px;
|
||||
|
||||
&-sm {
|
||||
@include MediaQuery(sm) {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&-md {
|
||||
@include MediaQuery(md) {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&-lg {
|
||||
@include MediaQuery(lg) {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&-xl {
|
||||
@include MediaQuery(xl) {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&-2xl {
|
||||
@include MediaQuery(2xl) {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-nxxsmall {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px;
|
||||
|
||||
&-sm {
|
||||
@include MediaQuery(sm) {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&-md {
|
||||
@include MediaQuery(md) {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&-lg {
|
||||
@include MediaQuery(lg) {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&-xl {
|
||||
@include MediaQuery(xl) {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&-2xl {
|
||||
@include MediaQuery(2xl) {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-nxsmall {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px;
|
||||
|
||||
&-sm {
|
||||
@include MediaQuery(sm) {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&-md {
|
||||
@include MediaQuery(md) {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&-lg {
|
||||
@include MediaQuery(lg) {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&-xl {
|
||||
@include MediaQuery(xl) {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&-2xl {
|
||||
@include MediaQuery(2xl) {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-nsmall {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px;
|
||||
|
||||
&-sm {
|
||||
@include MediaQuery(sm) {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&-md {
|
||||
@include MediaQuery(md) {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&-lg {
|
||||
@include MediaQuery(lg) {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&-xl {
|
||||
@include MediaQuery(xl) {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&-2xl {
|
||||
@include MediaQuery(2xl) {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-nnormal {
|
||||
--shadow-offset: 8px;
|
||||
--blur-radius: 16px;
|
||||
|
||||
&-sm {
|
||||
@include MediaQuery(sm) {
|
||||
--shadow-offset: 8px;
|
||||
--blur-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&-md {
|
||||
@include MediaQuery(md) {
|
||||
--shadow-offset: 8px;
|
||||
--blur-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&-lg {
|
||||
@include MediaQuery(lg) {
|
||||
--shadow-offset: 8px;
|
||||
--blur-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&-xl {
|
||||
@include MediaQuery(xl) {
|
||||
--shadow-offset: 8px;
|
||||
--blur-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&-2xl {
|
||||
@include MediaQuery(2xl) {
|
||||
--shadow-offset: 8px;
|
||||
--blur-radius: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button.neo,.neo {
|
||||
&Btn {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
|
||||
border: none;
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
|
||||
&:focus {
|
||||
background-image: linear-gradient(-45deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
|
||||
border: none;
|
||||
}
|
||||
|
||||
&Small {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
|
||||
border: none;
|
||||
|
||||
&:focus {
|
||||
background-image: linear-gradient(-45deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
|
||||
border: none;
|
||||
|
||||
&:not(:active) {
|
||||
background-image: linear-gradient(-45deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
|
||||
}
|
||||
|
||||
& input[type=file] {
|
||||
background-image: linear-gradient(-45deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
}
|
||||
}
|
||||
|
||||
@include MediaQuery(phone) {
|
||||
&-mobile {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
|
||||
border: none;
|
||||
|
||||
&:focus {
|
||||
background-image: linear-gradient(-45deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
|
||||
border: none;
|
||||
|
||||
&:not(:active) {
|
||||
background-image: linear-gradient(-45deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&Plain {
|
||||
box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
|
||||
border: none;
|
||||
|
||||
&:focus {
|
||||
box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
|
||||
border: none;
|
||||
}
|
||||
|
||||
@include MediaQuery(phone) {
|
||||
&-mobile {
|
||||
box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
|
||||
border: none;
|
||||
|
||||
&:focus {
|
||||
box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&InsetPlain {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
border: none;
|
||||
|
||||
&:focus {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
border: none;
|
||||
}
|
||||
|
||||
@include MediaQuery(phone) {
|
||||
&-mobile {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
border: none;
|
||||
|
||||
&:focus {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&XInsetPlain {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
border: none;
|
||||
|
||||
&:focus {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
border: none;
|
||||
}
|
||||
|
||||
@include MediaQuery(phone) {
|
||||
&-mobile {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
border: none;
|
||||
|
||||
&:focus {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&InsetPlain {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
border: none;
|
||||
|
||||
&:focus {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
border: none;
|
||||
|
||||
&:not(:active) {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
}
|
||||
}
|
||||
|
||||
@include MediaQuery(phone) {
|
||||
&-mobile {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
border: none;
|
||||
|
||||
&:focus {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
border: none;
|
||||
|
||||
&:not(:active) {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&File {
|
||||
box-shadow: 0px 0px 0px var(--light-shadow), 0px 0px 0px var(--dark-shadow);
|
||||
transition: all .2s linear;
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
|
||||
@include MediaQuery(phone) {
|
||||
box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
|
||||
background: var(--primary-color);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
background: linear-gradient(-45deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&.isSelected {
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
|
||||
&:focus-visible {
|
||||
background: linear-gradient(-45deg, var(--primary-gradiend-lighter), var(--primary-gradiend-darker));
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-active, &.active {
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
color: var(--black);
|
||||
}
|
||||
}
|
||||
|
||||
&Input {
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
border: none;
|
||||
|
||||
&:focus {
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
&Select {
|
||||
& > select {
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
border: none;
|
||||
|
||||
&:focus {
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.neoCheckbox {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
|
||||
&:focus + label:before {
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter)) !important;
|
||||
}
|
||||
|
||||
&Container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
& + label {
|
||||
padding: .15rem .15rem .15rem 2rem;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
|
||||
&:before {
|
||||
animation-name: none;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
border-radius: 100px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0rem;
|
||||
content: '';
|
||||
border: none;
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:checked.is-checked-bold + label {
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
&:checked + label:after {
|
||||
display: inline-block;
|
||||
width: .375rem;
|
||||
height: .6rem;
|
||||
top: .35rem;
|
||||
left: .55rem;
|
||||
transform: translateY(0rem) rotate(45deg);
|
||||
border-width: .1rem;
|
||||
border-top-width: 0.1rem;
|
||||
border-left-width: 0.1rem;
|
||||
border-style: solid;
|
||||
border-top-style: solid;
|
||||
border-left-style: solid;
|
||||
border-color: var(--text-color);
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
position: absolute;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
hr.neoSeparator {
|
||||
&Flat {
|
||||
height: 10px;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
&Pressed {
|
||||
height: 10px;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
}
|
||||
}
|
||||
|
||||
input.neoRange[type=range] {
|
||||
height: 30px;
|
||||
-webkit-appearance: none;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
animate: 0.2s;
|
||||
border-radius: 20px;
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(90deg, hsl(0, 90%, 80%), hsl(10, 90%, 80%), hsl(20, 90%, 80%), hsl(30, 90%, 80%), hsl(40, 90%, 80%), hsl(50, 90%, 80%), hsl(60, 90%, 80%), hsl(70, 90%, 80%), hsl(80, 90%, 80%), hsl(90, 90%, 80%), hsl(100, 90%, 80%), hsl(110, 90%, 80%), hsl(120, 90%, 80%), hsl(130, 90%, 80%), hsl(140, 90%, 80%), hsl(150, 90%, 80%), hsl(160, 90%, 80%), hsl(170, 90%, 80%), hsl(180, 90%, 80%), hsl(190, 90%, 80%), hsl(200, 90%, 80%), hsl(210, 90%, 80%), hsl(220, 90%, 80%), hsl(230, 90%, 80%), hsl(240, 90%, 80%), hsl(250, 90%, 80%), hsl(260, 90%, 80%), hsl(270, 90%, 80%), hsl(280, 90%, 80%), hsl(290, 90%, 80%), hsl(300, 90%, 80%), hsl(310, 90%, 80%), hsl(320, 90%, 80%), hsl(330, 90%, 80%), hsl(340, 90%, 80%), hsl(350, 90%, 80%), hsl(359, 90%, 80%)) !important;
|
||||
}
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
border: none;
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 2px 2px 4px var(--dark-shadow), -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-light), var(--primary-gradiend-dark)) !important;
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
&:focus::-webkit-slider-runnable-track {
|
||||
background: linear-gradient(90deg, hsl(0, 90%, 80%), hsl(10, 90%, 80%), hsl(20, 90%, 80%), hsl(30, 90%, 80%), hsl(40, 90%, 80%), hsl(50, 90%, 80%), hsl(60, 90%, 80%), hsl(70, 90%, 80%), hsl(80, 90%, 80%), hsl(90, 90%, 80%), hsl(100, 90%, 80%), hsl(110, 90%, 80%), hsl(120, 90%, 80%), hsl(130, 90%, 80%), hsl(140, 90%, 80%), hsl(150, 90%, 80%), hsl(160, 90%, 80%), hsl(170, 90%, 80%), hsl(180, 90%, 80%), hsl(190, 90%, 80%), hsl(200, 90%, 80%), hsl(210, 90%, 80%), hsl(220, 90%, 80%), hsl(230, 90%, 80%), hsl(240, 90%, 80%), hsl(250, 90%, 80%), hsl(260, 90%, 80%), hsl(270, 90%, 80%), hsl(280, 90%, 80%), hsl(290, 90%, 80%), hsl(300, 90%, 80%), hsl(310, 90%, 80%), hsl(320, 90%, 80%), hsl(330, 90%, 80%), hsl(340, 90%, 80%), hsl(350, 90%, 80%), hsl(359, 90%, 80%)) !important;
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
border-radius: 20px;
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(90deg, hsl(0, 90%, 80%), hsl(10, 90%, 80%), hsl(20, 90%, 80%), hsl(30, 90%, 80%), hsl(40, 90%, 80%), hsl(50, 90%, 80%), hsl(60, 90%, 80%), hsl(70, 90%, 80%), hsl(80, 90%, 80%), hsl(90, 90%, 80%), hsl(100, 90%, 80%), hsl(110, 90%, 80%), hsl(120, 90%, 80%), hsl(130, 90%, 80%), hsl(140, 90%, 80%), hsl(150, 90%, 80%), hsl(160, 90%, 80%), hsl(170, 90%, 80%), hsl(180, 90%, 80%), hsl(190, 90%, 80%), hsl(200, 90%, 80%), hsl(210, 90%, 80%), hsl(220, 90%, 80%), hsl(230, 90%, 80%), hsl(240, 90%, 80%), hsl(250, 90%, 80%), hsl(260, 90%, 80%), hsl(270, 90%, 80%), hsl(280, 90%, 80%), hsl(290, 90%, 80%), hsl(300, 90%, 80%), hsl(310, 90%, 80%), hsl(320, 90%, 80%), hsl(330, 90%, 80%), hsl(340, 90%, 80%), hsl(350, 90%, 80%), hsl(359, 90%, 80%)) !important;
|
||||
border: none;
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
border: none;
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 2px 2px 4px var(--dark-shadow), -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-light), var(--primary-gradiend-dark)) !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
:root{
|
||||
--background: hsl(25,84%,88%);
|
||||
--text-color: hsl(25,84%,26.4%);
|
||||
--placeholder-text-color: hsla(25,84%,26.4%,.3);
|
||||
--black: hsl(0, 0%, 4%);
|
||||
--black-bis: hsl(0, 0%, 7%);
|
||||
--black-ter: hsl(0, 0%, 14%);
|
||||
--white: hsl(0, 4%, 99.8%);
|
||||
--primary-color: hsl(25,84%,88%);
|
||||
--primary-color-light: hsl(25,84%,100%);
|
||||
--primary-color-dark: hsl(25,84%,66%);
|
||||
--primary-gradiend-light: hsl(25,84%,92%);
|
||||
--primary-gradiend-dark: hsl(25,84%,84%);
|
||||
--primary-gradiend-lighter: hsl(25,84%,96%);
|
||||
--primary-gradiend-darker: hsl(25,84%,80%);
|
||||
--secondary-color: hsl(205, 84%, 88%);
|
||||
--secondary-color-light: hsl(205, 84%, 100%);
|
||||
--secondary-color-dark: hsl(205, 84%, 84%);
|
||||
--light-shadow: hsla(25,84%,100%, .5);
|
||||
--dark-shadow: hsla(25,84%,66%, .5);
|
||||
/*--fa-primary-color: hsl(25,84%,26.4%);
|
||||
--fa-secondary-color: hsl(25,84%,66%);*/
|
||||
--fa-primary-color: hsl(205,84%,26.4%);
|
||||
--fa-secondary-color: hsl(205,84%,66%);
|
||||
/*--fa-primary-color: hsl(115,84%,26.4%);
|
||||
--fa-secondary-color: hsl(115,84%,66%);*/
|
||||
--fa-primary-opacity: 0.80;
|
||||
--fa-secondary-opacity: 0.80;
|
||||
--danger-color: hsl(0, 82%, 91%);
|
||||
--warning-color: hsl(48, 82%, 91%);
|
||||
--info-color: hsl(196, 82%, 91%);
|
||||
--plus-green: hsl(96, 49%, 49%);
|
||||
--plus-gold: hsl(51, 72%, 46%);
|
||||
}
|
@ -1,6 +1,54 @@
|
||||
namespace decePubClient.Services;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace decePubClient.Services;
|
||||
|
||||
public class AppStatusService
|
||||
{
|
||||
|
||||
readonly IJSRuntime JsRuntime;
|
||||
readonly IJSInProcessRuntime JsSyncRuntime;
|
||||
|
||||
public AppStatusService(IJSRuntime JSRuntime)
|
||||
{
|
||||
JsRuntime = JSRuntime;
|
||||
JsSyncRuntime = (IJSInProcessRuntime)JSRuntime;
|
||||
}
|
||||
|
||||
public async ValueTask<bool> IsOnline() => await JsSyncRuntime.InvokeAsync<bool>("isOnline");
|
||||
|
||||
public bool InputCaptureSupported() => JsSyncRuntime.Invoke<bool>("inputCaptureSupported");
|
||||
|
||||
public bool IsServiceWorkerAvailable() => JsSyncRuntime.Invoke<bool>("isServiceWorkerAvailable");
|
||||
|
||||
public string Language() => JsSyncRuntime.Invoke<string>("getLanguage");
|
||||
|
||||
public void Language(string language) => JsSyncRuntime.InvokeVoid("setLanguage", language);
|
||||
|
||||
public long GetHeight(string classId) => JsSyncRuntime.Invoke<long>("getHeight", classId);
|
||||
|
||||
public void SetAppBadge(int counter) => JsSyncRuntime.InvokeVoid("setAppBadge", counter);
|
||||
|
||||
public void ClearAppBadge() => JsSyncRuntime.InvokeVoid("clearAppBadge");
|
||||
|
||||
public async ValueTask ClearCache()
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("clearCache");
|
||||
}
|
||||
|
||||
public async ValueTask ClearLocalStorage()
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("clearLocalStorage");
|
||||
}
|
||||
|
||||
public void ReloadPage()
|
||||
{
|
||||
JsSyncRuntime.InvokeVoid("reloadPage");
|
||||
}
|
||||
|
||||
public bool IsMobileMedia() => JsSyncRuntime.Invoke<bool>("isMobileMedia");
|
||||
|
||||
public bool CanShare() => JsSyncRuntime.Invoke<bool>("canShareStuff");
|
||||
|
||||
public async ValueTask ShareTextUrl(string title, string text, Uri uri) => await JsRuntime.InvokeVoidAsync("shareTextUrl", title, text, uri.ToString());
|
||||
|
||||
public async ValueTask ShareText(string title, string text) => await JsRuntime.InvokeVoidAsync("shareText", title, text);
|
||||
}
|
@ -8,15 +8,15 @@ namespace decePubClient.Services
|
||||
{
|
||||
public interface IHttpService
|
||||
{
|
||||
Task<HttpResponseMessage> Get(string uri, object payload = default, string?[] queryParams = default);
|
||||
Task<HttpResponseMessage> Get(string uri, object payload = default, string[] queryParams = default);
|
||||
|
||||
Task<HttpResponseMessage> GetAnon(string uri, object payload = default, string?[] queryParams = default);
|
||||
Task<HttpResponseMessage> GetAnon(string uri, object payload = default, string[] queryParams = default);
|
||||
|
||||
Task<HttpResponseMessage> Post(string uri, object payload = default);
|
||||
|
||||
Task<HttpResponseMessage> PostAnon(string uri, object payload = default);
|
||||
|
||||
Task<HttpResponseMessage> Delete(string uri, string?[] queryParams = default, object payload = default);
|
||||
Task<HttpResponseMessage> Delete(string uri, string[] queryParams = default, object payload = default);
|
||||
}
|
||||
|
||||
public class HttpService : IHttpService
|
||||
@ -41,7 +41,7 @@ namespace decePubClient.Services
|
||||
DbStorage = dbStorage;
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> Get(string uri, object payload = default, string?[] queryParams = default)
|
||||
public async Task<HttpResponseMessage> Get(string uri, object payload = default, string[] queryParams = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -82,7 +82,7 @@ namespace decePubClient.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> GetAnon(string uri, object payload = default, string?[] queryParams = default)
|
||||
public async Task<HttpResponseMessage> GetAnon(string uri, object payload = default, string[] queryParams = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -123,7 +123,7 @@ namespace decePubClient.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> Delete(string uri, string?[] queryParams = default, object payload = default)
|
||||
public async Task<HttpResponseMessage> Delete(string uri, string[] queryParams = default, object payload = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
322
Services/IStorage.cs
Normal file
322
Services/IStorage.cs
Normal file
@ -0,0 +1,322 @@
|
||||
using decePubClient.Helpers;
|
||||
using decePubClient.Models;
|
||||
using decePubClient.Models.Types;
|
||||
using Markdig;
|
||||
|
||||
namespace decePubClient.Services
|
||||
{
|
||||
public interface IStorage
|
||||
{
|
||||
ValueTask<List<Message>> GetMessages();
|
||||
ValueTask<Message> GetMessage(string messageId);
|
||||
ValueTask<string> AddMessages(List<Message> messages);
|
||||
ValueTask<string> UpdateMessages(List<Message> messages);
|
||||
ValueTask<string> RemoveMessage(string messageId);
|
||||
|
||||
ValueTask<ICollection<ClientLogs>> GetClientLogs();
|
||||
ValueTask AddLog(Exception exception, string where);
|
||||
ValueTask AddLog(string message, string where);
|
||||
|
||||
ValueTask<bool> RemoveAll(bool includeClientLogs = false);
|
||||
}
|
||||
|
||||
public class Storage : IStorage
|
||||
{
|
||||
readonly IndexedDb _db;
|
||||
readonly ILogger<Storage> _logger;
|
||||
|
||||
public Storage(IndexedDb indexedDb, ILogger<Storage> logger)
|
||||
{
|
||||
_db = indexedDb;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
#region Messagges
|
||||
|
||||
public async ValueTask<Message> GetMessage(string messageId)
|
||||
{
|
||||
await _db.OpenIndexedDb();
|
||||
var message = await _db.GetByKey<string, Message>(nameof(Message), messageId);
|
||||
|
||||
if (message is null)
|
||||
{
|
||||
var messages = await GetMessages();
|
||||
return messages.FirstOrDefault(m => m.MessageId == messageId);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public async ValueTask<List<Message>> GetMessages()
|
||||
{
|
||||
await _db.OpenIndexedDb();
|
||||
var messages = await _db.GetAll<Message>(nameof(Message)) ?? new();
|
||||
|
||||
|
||||
if (messages.Count == 0)
|
||||
{
|
||||
messages.Add(new()
|
||||
{
|
||||
RootMessageId = default,
|
||||
MessageId = "992167EE-8947-4823-B1E2-B4E1019B6974",
|
||||
User = Faker.GetRandomUser(),
|
||||
MessageType = (MessageType)Random.Shared.Next(0, 4),
|
||||
Title = default,
|
||||
Content = "test di messaggio normale senza titolo",
|
||||
Medias = new(),
|
||||
CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
|
||||
IsFavourite = Random.Shared.Next() % 2 == 0,
|
||||
IsBoosted = Random.Shared.Next() % 2 == 0,
|
||||
});
|
||||
messages.Add(new()
|
||||
{
|
||||
RootMessageId = default,
|
||||
MessageId = "C5307644-4EA8-4B5F-A240-7CFE4DE34741",
|
||||
User = Faker.GetRandomUser(),
|
||||
MessageType = (MessageType)Random.Shared.Next(0, 4),
|
||||
Title = "sopra la panca la capra canta",
|
||||
Content = Markdown.ToHtml("test di messaggio con titolo"),
|
||||
Medias = new(),
|
||||
CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
|
||||
IsFavourite = Random.Shared.Next() % 2 == 0,
|
||||
IsBoosted = Random.Shared.Next() % 2 == 0,
|
||||
});
|
||||
messages.Add(new()
|
||||
{
|
||||
RootMessageId = default,
|
||||
MessageId = "E473D562-D210-4453-93DA-157D4B90B0D8",
|
||||
User = Faker.GetRandomUser(),
|
||||
MessageType = (MessageType)Random.Shared.Next(0, 4),
|
||||
Title = default,
|
||||
Content = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd test di messaggio con overflow del testo",
|
||||
Medias = new(),
|
||||
CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
|
||||
IsFavourite = Random.Shared.Next() % 2 == 0,
|
||||
IsBoosted = Random.Shared.Next() % 2 == 0,
|
||||
});
|
||||
messages.Add(new()
|
||||
{
|
||||
RootMessageId = default,
|
||||
MessageId = "adf89dc6-0484-430e-a7c9-fcb1179b0c28",
|
||||
User = Faker.GetRandomUser(),
|
||||
MessageType = (MessageType)Random.Shared.Next(0, 4),
|
||||
Title = default,
|
||||
Content = "test video",
|
||||
Medias = new()
|
||||
{
|
||||
new()
|
||||
{
|
||||
FileName = "8252d175e714db88beb8ee0349dbac90405d3d5148e96e0cfd7de3b0876dbb1b.mp4",
|
||||
Url = "https://ihatebeinga.live/media/8252d175e714db88beb8ee0349dbac90405d3d5148e96e0cfd7de3b0876dbb1b.mp4",
|
||||
AltText = "https://ihatebeinga.live/media/8252d175e714db88beb8ee0349dbac90405d3d5148e96e0cfd7de3b0876dbb1b.mp4",
|
||||
ContentType = "video/mp4"
|
||||
}
|
||||
},
|
||||
CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
|
||||
IsFavourite = Random.Shared.Next() % 2 == 0,
|
||||
IsBoosted = Random.Shared.Next() % 2 == 0,
|
||||
});
|
||||
messages.Add(new()
|
||||
{
|
||||
RootMessageId = default,
|
||||
MessageId = "96025b43-5235-44e9-a3fa-98df31edfbfb",
|
||||
User = Faker.GetRandomUser(),
|
||||
MessageType = (MessageType)Random.Shared.Next(0, 4),
|
||||
Title = default,
|
||||
Content = "test immagine",
|
||||
Medias = new()
|
||||
{
|
||||
new()
|
||||
{
|
||||
FileName = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
|
||||
Url = "https://ihatebeinga.live/media/51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
|
||||
AltText = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
|
||||
ContentType = "image/jpg"
|
||||
}
|
||||
},
|
||||
CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
|
||||
IsFavourite = Random.Shared.Next() % 2 == 0,
|
||||
IsBoosted = Random.Shared.Next() % 2 == 0,
|
||||
});
|
||||
messages.Add(new()
|
||||
{
|
||||
RootMessageId = default,
|
||||
MessageId = "96025b43-5235-44e9-a3fa-98df31edfbfb",
|
||||
User = Faker.GetRandomUser(),
|
||||
MessageType = (MessageType)Random.Shared.Next(0, 4),
|
||||
Title = default,
|
||||
Content = "test immagini",
|
||||
Medias = new()
|
||||
{
|
||||
new()
|
||||
{
|
||||
FileName = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
|
||||
Url = "https://ihatebeinga.live/media/51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
|
||||
AltText = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
|
||||
ContentType = "image/jpg"
|
||||
},
|
||||
new()
|
||||
{
|
||||
FileName = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
|
||||
Url = "https://ihatebeinga.live/media/51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
|
||||
AltText = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
|
||||
ContentType = "image/jpg"
|
||||
},
|
||||
new()
|
||||
{
|
||||
FileName = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
|
||||
Url = "https://ihatebeinga.live/media/51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
|
||||
AltText = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
|
||||
ContentType = "image/jpg"
|
||||
}
|
||||
},
|
||||
CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
|
||||
IsFavourite = Random.Shared.Next() % 2 == 0,
|
||||
IsBoosted = Random.Shared.Next() % 2 == 0,
|
||||
});
|
||||
}
|
||||
|
||||
messages = messages.OrderByDescending(m => m.CreatedAt).ToList();
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
public async ValueTask<string> AddMessages(List<Message> messages)
|
||||
{
|
||||
await _db.OpenIndexedDb();
|
||||
var result = await _db.AddItems(nameof(Message), messages);
|
||||
_logger.LogInformation(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async ValueTask<string> UpdateMessages(List<Message> messages)
|
||||
{
|
||||
await _db.OpenIndexedDb();
|
||||
var result = await _db.UpdateItems(nameof(Message), messages);
|
||||
_logger.LogInformation(result);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public async ValueTask<string> RemoveMessage(string messageId)
|
||||
{
|
||||
await _db.OpenIndexedDb();
|
||||
var result = await _db.DeleteByKey(nameof(Message), messageId);
|
||||
_logger.LogInformation(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Logs
|
||||
|
||||
public async ValueTask<ICollection<ClientLogs>> GetClientLogs()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation($"opening on {nameof(GetClientLogs)}");
|
||||
await _db.OpenIndexedDb();
|
||||
return (await _db.GetAll<ClientLogs>(nameof(ClientLogs))).OrderByDescending(cl => cl.TimeStamp).ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return new List<ClientLogs>();
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask AddLog(Exception exception, string where)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _db.OpenIndexedDb();
|
||||
|
||||
var maxKey = await _db.GetMaxKey<long>(nameof(ClientLogs));
|
||||
var minKey = await _db.GetMinKey<long>(nameof(ClientLogs));
|
||||
|
||||
if (minKey - maxKey <= -100)
|
||||
for (var i = minKey; i < minKey + 50; i++)
|
||||
_ = await _db.DeleteByKey(nameof(ClientLogs), i);
|
||||
|
||||
maxKey += 1;
|
||||
var result = await _db.AddItems<ClientLogs>(nameof(ClientLogs), new()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Id = maxKey,
|
||||
Where = where,
|
||||
Exception = new()
|
||||
{
|
||||
Type = exception.GetType().ToString(),
|
||||
HelpLink = exception.HelpLink,
|
||||
HResult = exception.HResult.ToString(),
|
||||
InnerExceptionMessage = exception.InnerException?.Message,
|
||||
Message = exception.Message,
|
||||
Source = exception.Source,
|
||||
StackTrace = exception.StackTrace,
|
||||
TargetSiteName = exception.TargetSite?.Name
|
||||
}
|
||||
}
|
||||
});
|
||||
_logger.LogDebug($"{nameof(AddLog)}() add logs result = {result}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask AddLog(string message, string where)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _db.OpenIndexedDb();
|
||||
|
||||
var maxKey = await _db.GetMaxKey<long>(nameof(ClientLogs));
|
||||
var minKey = await _db.GetMinKey<long>(nameof(ClientLogs));
|
||||
|
||||
if (minKey - maxKey <= -100)
|
||||
for (var i = minKey; i < minKey + 50; i++)
|
||||
_ = await _db.DeleteByKey(nameof(ClientLogs), i);
|
||||
|
||||
maxKey += 1;
|
||||
var result = await _db.AddItems<ClientLogs>(nameof(ClientLogs), new()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Id = maxKey,
|
||||
Where = where,
|
||||
Exception = null,
|
||||
WarningMessage = message
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public async ValueTask<bool> RemoveAll(bool includeClientLogs = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation($"opening on {nameof(RemoveAll)}");
|
||||
await _db.OpenIndexedDb();
|
||||
_ = await _db.DeleteAll(nameof(Message));
|
||||
if (includeClientLogs)
|
||||
_ = await _db.DeleteAll(nameof(ClientLogs));
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
14
Services/IndexedDb.cs
Normal file
14
Services/IndexedDb.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using DnetIndexedDb;
|
||||
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace decePubClient.Services
|
||||
{
|
||||
public class IndexedDb : IndexedDbInterop
|
||||
{
|
||||
public IndexedDb(IJSRuntime jsRuntime, IndexedDbOptions<IndexedDb> options)
|
||||
: base(jsRuntime, options)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,77 @@
|
||||
namespace decePubClient.Services
|
||||
using Blazored.Modal.Services;
|
||||
using decePubClient.Models;
|
||||
using decePubClient.Models.Types;
|
||||
|
||||
namespace decePubClient.Services
|
||||
{
|
||||
public class MessagesService
|
||||
{
|
||||
public MessagesService()
|
||||
{
|
||||
readonly IModalService modalService;
|
||||
readonly IStorage storage;
|
||||
readonly HttpClient http;
|
||||
|
||||
public MessagesService(IHttpClientFactory clientFactory, IModalService modalService, IStorage storage)
|
||||
{
|
||||
this.modalService = modalService;
|
||||
this.storage = storage;
|
||||
http = clientFactory.CreateClient("default");
|
||||
}
|
||||
|
||||
//Covers also direct message
|
||||
public async Task<Message> SubmitMessage(MessageForm messageForm)
|
||||
{
|
||||
//TODO
|
||||
|
||||
await Task.Run(() => {});
|
||||
return new();
|
||||
}
|
||||
|
||||
//Covers also direct message
|
||||
public async Task<Message> ReplyMessage(Message messageToReply, MessageType messageType = MessageType.Public)
|
||||
{
|
||||
//TODO
|
||||
|
||||
await Task.Run(() => {});
|
||||
return messageToReply;
|
||||
}
|
||||
|
||||
public async Task<Message> BoostUnboostMessage(Message messageToReply)
|
||||
{
|
||||
//TODO
|
||||
|
||||
await Task.Run(() => {});
|
||||
return messageToReply;
|
||||
}
|
||||
|
||||
public async Task<Message> FavouriteUnfavouriteMessage(Message messageToReply)
|
||||
{
|
||||
//TODO
|
||||
|
||||
await Task.Run(() => {});
|
||||
return messageToReply;
|
||||
}
|
||||
|
||||
public async Task<Message> DeleteMessage(Message messageToReply)
|
||||
{
|
||||
//TODO
|
||||
|
||||
await Task.Run(() => {});
|
||||
return messageToReply;
|
||||
}
|
||||
|
||||
public async Task<MessageUser> BlockUserFromMessage(MessageUser messageUser)
|
||||
{
|
||||
//TODO
|
||||
await Task.Run(() => {});
|
||||
return messageUser;
|
||||
}
|
||||
|
||||
public async Task<MessageUser> SilenceUserFromMessage(MessageUser messageUser)
|
||||
{
|
||||
//TODO
|
||||
|
||||
await Task.Run(() => {});
|
||||
return messageUser;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
Shared/LoginDisplay.razor
Normal file
24
Shared/LoginDisplay.razor
Normal file
@ -0,0 +1,24 @@
|
||||
@inject NavigationManager Navigation
|
||||
@inject SignOutSessionStateManager SignOutManager
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<button class="button" @onclick="BeginSignOut">
|
||||
@CascadingState.Localizer["Logout"]
|
||||
</button>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<NavLink ActiveClass="neoBtnSmallInsetPlain" class="button is-rounded neoBtnSmallPlain" href="authentication/login">
|
||||
@CascadingState.Localizer["Login"]
|
||||
</NavLink>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] CascadingState CascadingState { get; set; }
|
||||
private async Task BeginSignOut(MouseEventArgs args)
|
||||
{
|
||||
await SignOutManager.SetSignOutState();
|
||||
Navigation.NavigateTo("authentication/logout");
|
||||
}
|
||||
}
|
@ -1,17 +1,11 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<div class="page">
|
||||
<div class="sidebar">
|
||||
<NavMenu />
|
||||
</div>
|
||||
<div class="relative flex flex-col md:flex-row w-full h-full">
|
||||
<NavMenu />
|
||||
|
||||
<main>
|
||||
<div class="top-row px-4">
|
||||
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
|
||||
</div>
|
||||
|
||||
<article class="content px-4">
|
||||
@Body
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
<div class="block flex-1 relative w-full h-full">
|
||||
<div class="block absolute overflow-y-auto w-full h-full p-3 md:py-4 md:pl-2 md:pr-4">
|
||||
@Body
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,39 +1,222 @@
|
||||
<div class="top-row ps-3 navbar navbar-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="">decePubClient</a>
|
||||
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="flex justify-between align-center pt-3 px-3 md:p-0">
|
||||
|
||||
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
|
||||
<nav class="flex-column">
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
||||
<span class="oi oi-home" aria-hidden="true"></span> Home
|
||||
</NavLink>
|
||||
</div>
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="counter">
|
||||
<span class="oi oi-plus" aria-hidden="true"></span> Counter
|
||||
</NavLink>
|
||||
</div>
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="fetchdata">
|
||||
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="relative md:w-52">
|
||||
<button class="button is-rounded inline-flex is-small neoBtnSmall relative md:hidden" @onclick="ToggleNavMenu">
|
||||
<i class="ion-md-menu text-lg"></i>
|
||||
</button>
|
||||
<div class="absolute background md:relative top-full md:top-auto mt-2 w-52 md:w-full overflow-y-auto rounded-lg neomorph is-nsmall md:shadow-none z-50 @menuToggle md:block">
|
||||
<div class="flex flex-col space-y-3 p-3 md:p-4">
|
||||
|
||||
<div class="flex space-x-3 p-3 md:p-4 neomorph is-nxsmall rounded-xl bg-cover bg-no-repeat bg-right" style="background-image:linear-gradient(to left, var(--background) 55%, transparent), url('@(CascadingState.User?.BackgroundUrl)');">
|
||||
<a class="block h-8 w-8 md:h-12 md:w-12 flex-none" href="@CascadingState.User?.ProfileUrl" title="@CascadingState.User?.UserName">
|
||||
<img alt="@CascadingState.User?.UserName" class="h-8 w-8 md:h-12 md:w-12 object-cover rounded-full neomorph is-nxxsmall" src="@(CascadingState.User?.PictureUrl ?? "/imgs/icon-192.png")" />
|
||||
</a>
|
||||
<div class="flex flex-col w-full min-w-0 space-y-2">
|
||||
<p class="shrink truncate">
|
||||
<b title="@CascadingState.User?.DisplayName">
|
||||
@CascadingState.User?.DisplayName
|
||||
</b>
|
||||
</p>
|
||||
<p class="flex-1 min-w-6 truncate text-xs">
|
||||
<a class="underline" href="@CascadingState.User?.ProfileUrl" title="@CascadingState.User?.UserName">
|
||||
@CascadingState.User?.UserName
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NavLink ActiveClass="neoBtnSmallInsetPlain" class="button has-icons-left is-rounded neoBtnSmallPlain" href="/" Match="NavLinkMatch.All">
|
||||
<span class="icon is-left">
|
||||
<i class="ion-md-home"></i>
|
||||
</span>
|
||||
<span>@CascadingState.Localizer["Home"]</span>
|
||||
</NavLink>
|
||||
<NavLink ActiveClass="neoBtnSmallInsetPlain" class="button has-icons-left is-rounded neoBtnSmallPlain" href="settings">
|
||||
<span class="icon is-left">
|
||||
<i class="ion-md-settings"></i>
|
||||
</span>
|
||||
<span>@CascadingState.Localizer["Settings"]</span>
|
||||
</NavLink>
|
||||
@*<AuthorizeView>
|
||||
<Authorized>
|
||||
<NavLink ActiveClass="neoBtnSmallInsetPlain" class="button is-rounded neoBtnSmallPlain" href="administration">
|
||||
@Localizer["Administration"]
|
||||
</NavLink>
|
||||
<NavLink ActiveClass="neoBtnSmallInsetPlain" class="button is-rounded neoBtnSmallPlain" href="logout">
|
||||
@Localizer["Logout"]
|
||||
</NavLink>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<NavLink ActiveClass="neoBtnSmallInsetPlain" class="button is-rounded neoBtnSmallPlain" href="login">
|
||||
@Localizer["Login"]
|
||||
</NavLink>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>*@
|
||||
|
||||
<NavLink ActiveClass="neoBtnSmallInsetPlain" class="button has-icons-left is-rounded neoBtnSmallPlain" href="administration">
|
||||
<span class="icon is-left has-text-danger">
|
||||
<i class="ion-md-switch"></i>
|
||||
</span>
|
||||
<span>@CascadingState.Localizer["Administration"]</span>
|
||||
</NavLink>
|
||||
<NavLink ActiveClass="neoBtnSmallInsetPlain" class="button has-icons-left is-rounded neoBtnSmallPlain" href="login">
|
||||
<span class="icon is-left has-text-success">
|
||||
<i class="ion-md-log-in"></i>
|
||||
</span>
|
||||
<span>@CascadingState.Localizer["Login"]</span>
|
||||
</NavLink>
|
||||
<NavLink ActiveClass="neoBtnSmallInsetPlain" class="button has-icons-left is-rounded neoBtnSmallPlain" href="logout">
|
||||
<span class="icon is-left has-text-danger">
|
||||
<i class="ion-md-log-out"></i>
|
||||
</span>
|
||||
<span>@CascadingState.Localizer["Logout"]</span>
|
||||
</NavLink>
|
||||
|
||||
<div class="field is-grouped is-align-items-center">
|
||||
<div class="control">
|
||||
<button @onclick:stopPropagation @onclick:preventDefault @onclick="ResetToOriginalColour" class="button is-rounded has-icons-left is-small mb-0 ml-0 neoBtn" style="background:hsl(25,84%,88%) !important"
|
||||
type="button">
|
||||
<span class="icon is-left"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="control is-expanded is-flex">
|
||||
<input class="neoRange fullwidth" id="ColourIndex_@(RandomId)"
|
||||
max="359" min="0" @onchange="UpdateThemeColour" step="1" type="range" value="@ThemeIndexColour">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<EditForm class="field is-grouped is-grouped-right" Context="DarkModeForm" Model="ThemeIsDarkMode">
|
||||
<div class="control">
|
||||
<label>
|
||||
<InputCheckbox class="toggle-checkbox"
|
||||
Value="ThemeIsDarkMode" ValueChanged="async v => await UpdateThemeDarkMode(v)" ValueExpression="() => ThemeIsDarkMode" />
|
||||
<div class="toggle-slot neomorph is-nxxsmall cursor-pointer">
|
||||
<div class="sun-icon-wrapper">
|
||||
<div class="sun-icon">
|
||||
<i class="ion-md-sunny text-xs"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toggle-button"></div>
|
||||
<div class="moon-icon-wrapper">
|
||||
<div class="moon-icon">
|
||||
<i class="ion-md-moon text-xs"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</EditForm>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="md:hidden">
|
||||
<img alt="" class="h-[30px]" src="imgs/icon-512.png">
|
||||
</div>
|
||||
|
||||
<div class="md:hidden">
|
||||
<a class="neomorph is-nxxsmall" href="settings">
|
||||
<img alt="@CascadingState.User?.UserName" class="object-cover w-[30px] h-[30px] rounded-full"
|
||||
src="@CascadingState.User?.PictureUrl">
|
||||
</a>
|
||||
|
||||
@* <button class="button is-rounded is-small has-icons-left neoBtnSmall "> *@
|
||||
@* $1$ <AuthorizeView> #1# *@
|
||||
@* $1$ <Authorized> #1# *@
|
||||
@* $1$ <span class="icon is-left"> #1# *@
|
||||
@* $1$ <img alt="@CascadingState.User.UserName" class="object-cover" src="@CascadingState.User.PictureUrl"> #1# *@
|
||||
@* $1$ </span> #1# *@
|
||||
@* $1$ </Authorized> #1# *@
|
||||
@* $1$ <NotAuthorized> #1# *@
|
||||
@* $1$ <span class="icon is-left"> #1# *@
|
||||
@* $1$ <i class="ion-md-person"></i> #1# *@
|
||||
@* $1$ </span> #1# *@
|
||||
@* $1$ </NotAuthorized> #1# *@
|
||||
@* $1$ </AuthorizeView> #1# *@
|
||||
@* </button> *@
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
@code {
|
||||
private bool collapseNavMenu = true;
|
||||
[CascadingParameter] CascadingState CascadingState { get; set; }
|
||||
string menuToggle = "hidden";
|
||||
bool IsThemeChanging { get; set; } = false;
|
||||
bool ThemeIsDarkMode { get; set; } = false;
|
||||
short ThemeIndexColour { get; set; } = 25;
|
||||
int RandomId { get; set; }
|
||||
|
||||
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
});
|
||||
RandomId = Random.Shared.Next(0, int.MaxValue);
|
||||
if (!CascadingState.Status.IsMobileMedia())
|
||||
menuToggle = default;
|
||||
|
||||
private void ToggleNavMenu()
|
||||
{
|
||||
collapseNavMenu = !collapseNavMenu;
|
||||
}
|
||||
}
|
||||
ThemeIsDarkMode = CascadingState.PublicCacheData?.ThemeIsDarkMode ?? false;
|
||||
ThemeIndexColour = CascadingState.PublicCacheData?.ThemeIndexColour ?? 25;
|
||||
}
|
||||
|
||||
private void ToggleNavMenu()
|
||||
{
|
||||
menuToggle = menuToggle is { Length: > 0 } ? default : "hidden";
|
||||
}
|
||||
|
||||
protected async Task ResetToOriginalColour()
|
||||
{
|
||||
IsThemeChanging = true;
|
||||
CascadingState.PublicCacheData.ThemeIndexColour =
|
||||
ThemeIndexColour =
|
||||
25;
|
||||
|
||||
// if (AuthData?.User != null)
|
||||
// {
|
||||
// AuthData.User.UserSettings.ThemeIndexColour = ThemeIndexColour;
|
||||
// await Storage.SetItemAsync(nameof(AuthData), AuthData);
|
||||
// }
|
||||
|
||||
await CascadingState.UpdatePublicCache(CascadingState.PublicCacheData);
|
||||
|
||||
IsThemeChanging = false;
|
||||
}
|
||||
|
||||
protected async Task UpdateThemeColour(ChangeEventArgs eventArgs)
|
||||
{
|
||||
IsThemeChanging = true;
|
||||
var indexColour = short.Parse(eventArgs.Value?.ToString());
|
||||
CascadingState.PublicCacheData.ThemeIndexColour =
|
||||
ThemeIndexColour =
|
||||
indexColour;
|
||||
|
||||
// if (AuthData?.User != null)
|
||||
// {
|
||||
// AuthData.User.UserSettings.ThemeIndexColour = ThemeIndexColour;
|
||||
// await Storage.SetItemAsync(nameof(AuthData), AuthData);
|
||||
// }
|
||||
|
||||
await CascadingState.UpdatePublicCache(CascadingState.PublicCacheData);
|
||||
|
||||
IsThemeChanging = false;
|
||||
}
|
||||
|
||||
protected async Task UpdateThemeDarkMode(bool isDarkMode)
|
||||
{
|
||||
IsThemeChanging = true;
|
||||
CascadingState.PublicCacheData.ThemeIsDarkMode =
|
||||
ThemeIsDarkMode = isDarkMode;
|
||||
Console.WriteLine("Dark updated {0}", ThemeIsDarkMode);
|
||||
|
||||
// if (AuthData?.User != null)
|
||||
// {
|
||||
// AuthData.User.UserSettings.ThemeIsDarkMode = ThemeIsDarkMode;
|
||||
// await Storage.SetItemAsync(nameof(AuthData), AuthData);
|
||||
// }
|
||||
|
||||
await CascadingState.UpdatePublicCache(CascadingState.PublicCacheData);
|
||||
|
||||
IsThemeChanging = false;
|
||||
}
|
||||
}
|
8
Shared/RedirectToLogin.razor
Normal file
8
Shared/RedirectToLogin.razor
Normal file
@ -0,0 +1,8 @@
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
@code {
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}");
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
<div class="alert alert-secondary mt-4">
|
||||
<span class="oi oi-pencil me-2" aria-hidden="true"></span>
|
||||
<strong>@Title</strong>
|
||||
|
||||
<span class="text-nowrap">
|
||||
Please take our
|
||||
<a target="_blank" class="font-weight-bold link-dark" href="https://go.microsoft.com/fwlink/?linkid=2148851">brief survey</a>
|
||||
</span>
|
||||
and tell us what you think.
|
||||
</div>
|
||||
|
||||
@code {
|
||||
// Demonstrates how a parent component can supply parameters
|
||||
[Parameter]
|
||||
public string? Title { get; set; }
|
||||
}
|
@ -1,10 +1,31 @@
|
||||
@using System.Net.Http
|
||||
@using System.IO
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using System.Security.Claims
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.AspNetCore.Components.WebAssembly.Http
|
||||
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using Microsoft.Extensions.Localization
|
||||
@using Microsoft.Extensions.Logging
|
||||
@using Microsoft.JSInterop
|
||||
@using Toolbelt.Blazor.HeadElement
|
||||
@using Blazored.Modal
|
||||
@using Blazored.Modal.Services
|
||||
@using Blazored.LocalStorage
|
||||
@using Blazor.DownloadFileFast.Interfaces
|
||||
@using Markdig
|
||||
@using decePubClient
|
||||
@using decePubClient.Services
|
||||
@using decePubClient.Shared
|
||||
@using decePubClient.Models
|
||||
@using decePubClient.Models.Types
|
||||
@using decePubClient.Helpers
|
||||
@using decePubClient.Resources
|
||||
@using decePubClient.Extensions
|
||||
@using decePubClient.Components
|
||||
@using decePubClient.LayerComponents
|
||||
|
@ -1,3 +1,22 @@
|
||||
{
|
||||
|
||||
}
|
||||
[
|
||||
{
|
||||
"outputFileName": "wwwroot/css/style.min.css",
|
||||
"inputFiles": [
|
||||
// "wwwroot/vendor/solid.css",
|
||||
// "wwwroot/vendor/fontawesome.css",
|
||||
//"wwwroot/vendor/open-iconic.css",
|
||||
"wwwroot/vendor/ionicons.css",
|
||||
"wwwroot/vendor/toggle-dark-light-mode.css",
|
||||
"wwwroot/vendor/bulma.css",
|
||||
"wwwroot/css/main.css",
|
||||
"wwwroot/vendor/tailwind.css",
|
||||
"wwwroot/css/tailwind-override.css"
|
||||
],
|
||||
"minify": {
|
||||
"enabled": false,
|
||||
"gzip": false,
|
||||
"brotli": false
|
||||
},
|
||||
"sourceMap": false
|
||||
}
|
||||
]
|
||||
|
@ -1 +0,0 @@
|
||||
///<binding AfterBuild='Clean output files, Update all files, Stylesheets, wwwroot/css/style.min.css' />
|
@ -1,3 +1,12 @@
|
||||
{
|
||||
|
||||
}
|
||||
[
|
||||
{
|
||||
"outputFile": "wwwroot/css/main.css",
|
||||
"inputFile": "SCSS/main.scss",
|
||||
"minify": {
|
||||
"enabled": false
|
||||
},
|
||||
"options": {
|
||||
"sourceMap": false
|
||||
}
|
||||
}
|
||||
]
|
@ -1,19 +1,57 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<Nullable>disable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
|
||||
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.1" PrivateAssets="all" />
|
||||
<PackageReference Include="BuildBundlerMinifier" Version="3.2.449" />
|
||||
<PackageReference Include="BuildWebCompiler" Version="1.12.405" PrivateAssets="all" />
|
||||
<PackageReference Include="BundlerMinifier.Core" Version="3.2.449" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="6.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="6.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.2" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
<PackageReference Include="Append.Blazor.Notifications" Version="1.1.0" />
|
||||
<PackageReference Include="BlazorDownloadFileFast" Version="0.2.0" />
|
||||
<PackageReference Include="Blazored.LocalStorage" Version="4.2.0" />
|
||||
<PackageReference Include="Blazored.Modal" Version="6.0.1" />
|
||||
<PackageReference Include="DnetIndexedDb" Version="2.3.1" />
|
||||
<PackageReference Include="Markdig" Version="0.27.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.2" />
|
||||
<PackageReference Include="Toolbelt.Blazor.HeadElement" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Resources\ErrorMessages.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>ErrorMessages.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resources\AllStrings.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>AllStrings.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="Resources\ErrorMessages.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>ErrorMessages.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="npm run buildcss" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32112.339
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "decePubClient", "decePubClient.csproj", "{EBE69805-3005-49C2-81E4-A314A19F68B6}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "decePubClient", "decePubClient.csproj", "{EBE69805-3005-49C2-81E4-A314A19F68B6}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
216
package-lock.json
generated
216
package-lock.json
generated
@ -8,6 +8,7 @@
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz",
|
||||
"integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/highlight": "^7.16.7"
|
||||
}
|
||||
@ -15,12 +16,14 @@
|
||||
"@babel/helper-validator-identifier": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
|
||||
"integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="
|
||||
"integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/highlight": {
|
||||
"version": "7.16.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz",
|
||||
"integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.16.7",
|
||||
"chalk": "^2.0.0",
|
||||
@ -31,6 +34,7 @@
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
@ -39,6 +43,7 @@
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
@ -49,6 +54,7 @@
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
@ -56,17 +62,20 @@
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
@ -96,20 +105,55 @@
|
||||
"fastq": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"@tailwindcss/aspect-ratio": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.4.0.tgz",
|
||||
"integrity": "sha512-WJu0I4PpqNPuutpaA9zDUq2JXR+lorZ7PbLcKNLmb6GL9/HLfC7w3CRsMhJF4BbYd/lkY6CfXOvkYpuGnZfkpQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@tailwindcss/forms": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.4.0.tgz",
|
||||
"integrity": "sha512-DeaQBx6EgEeuZPQACvC+mKneJsD8am1uiJugjgQK1+/Vt+Ai0GpFBC2T2fqnUad71WgOxyrZPE6BG1VaI6YqfQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mini-svg-data-uri": "^1.2.3"
|
||||
}
|
||||
},
|
||||
"@tailwindcss/line-clamp": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.3.1.tgz",
|
||||
"integrity": "sha512-pNr0T8LAc3TUx/gxCfQZRe9NB2dPEo/cedPHzUGIPxqDMhgjwNm6jYxww4W5l0zAsAddxr+XfZcqttGiFDgrGg==",
|
||||
"dev": true
|
||||
},
|
||||
"@tailwindcss/typography": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.1.tgz",
|
||||
"integrity": "sha512-AmSzZSgLhHKlILKduU+PKBTHL6c+al82syZlRid1xgmlWwXagLigO+O++B4C0scpMfzW//f/3YCRcwwEHWoU3w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash.castarray": "^4.4.0",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.merge": "^4.6.2"
|
||||
}
|
||||
},
|
||||
"@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
|
||||
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn": {
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
|
||||
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn-node": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz",
|
||||
"integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"acorn": "^7.0.0",
|
||||
"acorn-walk": "^7.0.0",
|
||||
@ -119,7 +163,8 @@
|
||||
"acorn-walk": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
|
||||
"integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA=="
|
||||
"integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
@ -146,7 +191,8 @@
|
||||
"arg": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz",
|
||||
"integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA=="
|
||||
"integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==",
|
||||
"dev": true
|
||||
},
|
||||
"array-union": {
|
||||
"version": "3.0.1",
|
||||
@ -157,6 +203,7 @@
|
||||
"version": "10.4.2",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz",
|
||||
"integrity": "sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browserslist": "^4.19.1",
|
||||
"caniuse-lite": "^1.0.30001297",
|
||||
@ -183,6 +230,7 @@
|
||||
"version": "4.19.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz",
|
||||
"integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001286",
|
||||
"electron-to-chromium": "^1.4.17",
|
||||
@ -194,22 +242,26 @@
|
||||
"callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"camelcase-css": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="
|
||||
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
|
||||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001306",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001306.tgz",
|
||||
"integrity": "sha512-Wd1OuggRzg1rbnM5hv1wXs2VkxJH/AA+LuudlIqvZiCvivF+wJJe2mgBZC8gPMgI7D76PP5CTx8Luvaqc1V6OQ=="
|
||||
"integrity": "sha512-Wd1OuggRzg1rbnM5hv1wXs2VkxJH/AA+LuudlIqvZiCvivF+wJJe2mgBZC8gPMgI7D76PP5CTx8Luvaqc1V6OQ==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
@ -257,6 +309,7 @@
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
|
||||
"integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
@ -268,12 +321,14 @@
|
||||
"cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"defined": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
|
||||
"integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM="
|
||||
"integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
|
||||
"dev": true
|
||||
},
|
||||
"dependency-graph": {
|
||||
"version": "0.11.0",
|
||||
@ -284,6 +339,7 @@
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz",
|
||||
"integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"acorn-node": "^1.6.1",
|
||||
"defined": "^1.0.0",
|
||||
@ -293,7 +349,8 @@
|
||||
"didyoumean": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
|
||||
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
|
||||
"dev": true
|
||||
},
|
||||
"dir-glob": {
|
||||
"version": "3.0.1",
|
||||
@ -306,12 +363,14 @@
|
||||
"dlv": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
|
||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
|
||||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.4.63",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.63.tgz",
|
||||
"integrity": "sha512-e0PX/LRJPFRU4kzJKLvTobxyFdnANCvcoDCe8XcyTqP58nTWIwdsHvXLIl1RkB39X5yaosLaroMASWB0oIsgCA=="
|
||||
"integrity": "sha512-e0PX/LRJPFRU4kzJKLvTobxyFdnANCvcoDCe8XcyTqP58nTWIwdsHvXLIl1RkB39X5yaosLaroMASWB0oIsgCA==",
|
||||
"dev": true
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
@ -322,6 +381,7 @@
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
@ -334,7 +394,8 @@
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true
|
||||
},
|
||||
"fast-glob": {
|
||||
"version": "3.2.11",
|
||||
@ -367,7 +428,8 @@
|
||||
"fraction.js": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz",
|
||||
"integrity": "sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA=="
|
||||
"integrity": "sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==",
|
||||
"dev": true
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "10.0.0",
|
||||
@ -388,7 +450,8 @@
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
@ -430,6 +493,7 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1"
|
||||
}
|
||||
@ -437,7 +501,8 @@
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"ignore": {
|
||||
"version": "5.2.0",
|
||||
@ -448,6 +513,7 @@
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"parent-module": "^1.0.0",
|
||||
"resolve-from": "^4.0.0"
|
||||
@ -456,7 +522,8 @@
|
||||
"is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
|
||||
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
|
||||
"dev": true
|
||||
},
|
||||
"is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
@ -470,6 +537,7 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
|
||||
"integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has": "^1.0.3"
|
||||
}
|
||||
@ -500,12 +568,14 @@
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"dev": true
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "6.1.0",
|
||||
@ -524,7 +594,26 @@
|
||||
"lines-and-columns": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.castarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
|
||||
"integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"merge2": {
|
||||
"version": "1.4.1",
|
||||
@ -540,20 +629,29 @@
|
||||
"picomatch": "^2.2.3"
|
||||
}
|
||||
},
|
||||
"mini-svg-data-uri": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.3.tgz",
|
||||
"integrity": "sha512-gSfqpMRC8IxghvMcxzzmMnWpXAChSA+vy4cia33RgerMS8Fex95akUyQZPbxJJmeBGiGmK7n/1OpUX8ksRjIdA==",
|
||||
"dev": true
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"dev": true
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
|
||||
"integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA=="
|
||||
"integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==",
|
||||
"dev": true
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz",
|
||||
"integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA=="
|
||||
"integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
@ -563,17 +661,20 @@
|
||||
"normalize-range": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
|
||||
"integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI="
|
||||
"integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
|
||||
"dev": true
|
||||
},
|
||||
"object-hash": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
|
||||
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw=="
|
||||
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
|
||||
"dev": true
|
||||
},
|
||||
"parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"callsites": "^3.0.0"
|
||||
}
|
||||
@ -582,6 +683,7 @@
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
@ -592,7 +694,8 @@
|
||||
"path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-type": {
|
||||
"version": "4.0.0",
|
||||
@ -618,6 +721,7 @@
|
||||
"version": "8.4.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz",
|
||||
"integrity": "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.2.0",
|
||||
"picocolors": "^1.0.0",
|
||||
@ -643,10 +747,28 @@
|
||||
"yargs": "^17.0.0"
|
||||
}
|
||||
},
|
||||
"postcss-discard-comments": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.2.tgz",
|
||||
"integrity": "sha512-6VQ3pYTsJHEsN2Bic88Aa7J/Brn4Bv8j/rqaFQZkH+pcVkKYwxCIvoMQkykEW7fBjmofdTnQgcivt5CCBJhtrg==",
|
||||
"dev": true
|
||||
},
|
||||
"postcss-import": {
|
||||
"version": "14.0.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.2.tgz",
|
||||
"integrity": "sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"postcss-value-parser": "^4.0.0",
|
||||
"read-cache": "^1.0.0",
|
||||
"resolve": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"postcss-js": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz",
|
||||
"integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase-css": "^2.0.1"
|
||||
}
|
||||
@ -664,10 +786,20 @@
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz",
|
||||
"integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"postcss-selector-parser": "^6.0.6"
|
||||
}
|
||||
},
|
||||
"postcss-nesting": {
|
||||
"version": "10.1.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.2.tgz",
|
||||
"integrity": "sha512-dJGmgmsvpzKoVMtDMQQG/T6FSqs6kDtUDirIfl4KnjMCiY9/ETX8jdKyCd20swSRAbUYkaBKV20pxkzxoOXLqQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"postcss-selector-parser": "^6.0.8"
|
||||
}
|
||||
},
|
||||
"postcss-reporter": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.0.5.tgz",
|
||||
@ -681,6 +813,7 @@
|
||||
"version": "6.0.9",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz",
|
||||
"integrity": "sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
@ -689,7 +822,8 @@
|
||||
"postcss-value-parser": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"dev": true
|
||||
},
|
||||
"pretty-hrtime": {
|
||||
"version": "1.0.3",
|
||||
@ -704,7 +838,8 @@
|
||||
"quick-lru": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
|
||||
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
|
||||
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
|
||||
"dev": true
|
||||
},
|
||||
"read-cache": {
|
||||
"version": "1.0.0",
|
||||
@ -731,6 +866,7 @@
|
||||
"version": "1.22.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
|
||||
"integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-core-module": "^2.8.1",
|
||||
"path-parse": "^1.0.7",
|
||||
@ -740,7 +876,8 @@
|
||||
"resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true
|
||||
},
|
||||
"reusify": {
|
||||
"version": "1.0.4",
|
||||
@ -763,7 +900,8 @@
|
||||
"source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.3",
|
||||
@ -787,6 +925,7 @@
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
@ -794,12 +933,14 @@
|
||||
"supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true
|
||||
},
|
||||
"tailwindcss": {
|
||||
"version": "3.0.18",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.18.tgz",
|
||||
"integrity": "sha512-ihPTpEyA5ANgZbwKlgrbfnzOp9R5vDHFWmqxB1PT8NwOGCOFVVMl+Ps1cQQ369acaqqf1BEF77roCwK0lvNmTw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arg": "^5.0.1",
|
||||
"chalk": "^4.1.2",
|
||||
@ -827,6 +968,7 @@
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-glob": "^4.0.3"
|
||||
}
|
||||
@ -854,7 +996,8 @@
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
||||
"dev": true
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
@ -869,7 +1012,8 @@
|
||||
"xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"dev": true
|
||||
},
|
||||
"y18n": {
|
||||
"version": "5.0.8",
|
||||
|
19
package.json
19
package.json
@ -4,12 +4,27 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"buildcss": "postcss wwwroot/vendor/tailwind-generator.css -o wwwroot/vendor/tailwind.css"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.thepra.dev/thepra/decePubClient.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"postcss-cli": "^9.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/aspect-ratio": "^0.4.0",
|
||||
"@tailwindcss/forms": "^0.4.0",
|
||||
"@tailwindcss/line-clamp": "^0.3.0",
|
||||
"@tailwindcss/typography": "^0.5.0",
|
||||
"autoprefixer": "^10.4.0",
|
||||
"postcss": "^8.4.5",
|
||||
"postcss-import": "^14.0.2",
|
||||
"postcss-nesting": "^10.0.3",
|
||||
"tailwindcss": "^3.0.18",
|
||||
"postcss-discard-comments": "^5.0.2"
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
'postcss-discard-comments': {}
|
||||
}
|
||||
}
|
@ -1,7 +1,13 @@
|
||||
const colors = require('tailwindcss/colors');
|
||||
module.exports = {
|
||||
content: [],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
content: [
|
||||
'./**/*.cs',
|
||||
'./**/*.html',
|
||||
'./**/*.razor'
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
variants: {},
|
||||
plugins: [],
|
||||
}
|
||||
|
13
wwwroot/appsettings.json
Normal file
13
wwwroot/appsettings.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"Local": {
|
||||
"Authority": "https://openidconnect.net",
|
||||
"ClientId": "1234.locahost",
|
||||
"RedirectUri": "",
|
||||
"MetadataUrl": "",
|
||||
"PostLogoutRedirectUri": "",
|
||||
"ResponseType": "",
|
||||
"ResponseMode": "",
|
||||
"AdditionalProviderParameters": [],
|
||||
"DefaultScopes": []
|
||||
}
|
||||
}
|
@ -2,10 +2,10 @@
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square70x70logo src="/imgs/mstile-70x70.png" />
|
||||
<square150x150logo src="/imgs/mstile-150x150.png" />
|
||||
<square310x310logo src="/imgs/mstile-310x310.png" />
|
||||
<wide310x150logo src="/imgs/mstile-310x150.png" />
|
||||
<square70x70logo src="/imgs/icon-192.png" />
|
||||
<square150x150logo src="/imgs/icon-512.png" />
|
||||
<square310x310logo src="/imgs/icon-512.png" />
|
||||
<wide310x150logo src="/imgs/icon-512.png" />
|
||||
<TileColor>#fadcc7</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
|
@ -1,3 +1,447 @@
|
||||
:root {
|
||||
--background: #fadcc7;
|
||||
--text-color: #7c3a0b;
|
||||
--placeholder-text-color: rgba(124, 58, 11, 0.3);
|
||||
--black: #0a0a0a;
|
||||
--black-bis: #121212;
|
||||
--black-ter: #242424;
|
||||
--white: #fffefe;
|
||||
--primary-color: #fadcc7;
|
||||
--primary-color-light: white;
|
||||
--primary-color-dark: #f19c5f;
|
||||
--primary-gradiend-light: #fce8d9;
|
||||
--primary-gradiend-dark: #f8d0b4;
|
||||
--primary-gradiend-lighter: #fdf3ec;
|
||||
--primary-gradiend-darker: #f7c5a1;
|
||||
--secondary-color: #c7e5fa;
|
||||
--secondary-color-light: white;
|
||||
--secondary-color-dark: #b4dcf8;
|
||||
--light-shadow: rgba(255, 255, 255, 0.5);
|
||||
--dark-shadow: rgba(241, 156, 95, 0.5);
|
||||
/*--fa-primary-color: hsl(25,84%,26.4%);
|
||||
--fa-secondary-color: hsl(25,84%,66%);*/
|
||||
--fa-primary-color: #0b4d7c;
|
||||
--fa-secondary-color: #5fb4f1;
|
||||
/*--fa-primary-color: hsl(115,84%,26.4%);
|
||||
--fa-secondary-color: hsl(115,84%,66%);*/
|
||||
--fa-primary-opacity: 0.80;
|
||||
--fa-secondary-opacity: 0.80;
|
||||
--danger-color: #fbd5d5;
|
||||
--warning-color: #fbf3d5;
|
||||
--info-color: #d5f1fb;
|
||||
--plus-green: #71ba40;
|
||||
--plus-gold: #cab021; }
|
||||
|
||||
:root {
|
||||
--shadow-offset: 8px;
|
||||
--blur-radius: 16px;
|
||||
--is-inset: inherit; }
|
||||
|
||||
/*# sourceMappingURL=main.css.map */
|
||||
.neomorph {
|
||||
box-shadow: calc(-1 * var(--shadow-offset)) calc(-1 * var(--shadow-offset)) var(--blur-radius) var(--light-shadow), var(--shadow-offset) var(--shadow-offset) var(--blur-radius) var(--dark-shadow); }
|
||||
.neomorphInset {
|
||||
box-shadow: inset var(--shadow-offset) var(--shadow-offset) var(--blur-radius) var(--dark-shadow), inset calc(-1 * var(--shadow-offset)) calc(-1 * var(--shadow-offset)) var(--blur-radius) var(--light-shadow); }
|
||||
.neomorphInset.is-nxxsmall {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px; }
|
||||
@media (min-width: 640px) {
|
||||
.neomorphInset.is-nxxsmall-sm {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px; } }
|
||||
@media (min-width: 768px) {
|
||||
.neomorphInset.is-nxxsmall-md {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px; } }
|
||||
@media (min-width: 1024px) {
|
||||
.neomorphInset.is-nxxsmall-lg {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px; } }
|
||||
@media (min-width: 1280px) {
|
||||
.neomorphInset.is-nxxsmall-xl {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px; } }
|
||||
.neomorphInset.is-nxsmall {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px; }
|
||||
@media (min-width: 640px) {
|
||||
.neomorphInset.is-nxsmall-sm {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px; } }
|
||||
@media (min-width: 768px) {
|
||||
.neomorphInset.is-nxsmall-md {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px; } }
|
||||
@media (min-width: 1024px) {
|
||||
.neomorphInset.is-nxsmall-lg {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px; } }
|
||||
@media (min-width: 1280px) {
|
||||
.neomorphInset.is-nxsmall-xl {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px; } }
|
||||
.neomorphInset.is-nsmall {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px; }
|
||||
@media (min-width: 640px) {
|
||||
.neomorphInset.is-nsmall-sm {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px; } }
|
||||
@media (min-width: 768px) {
|
||||
.neomorphInset.is-nsmall-md {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px; } }
|
||||
@media (min-width: 1024px) {
|
||||
.neomorphInset.is-nsmall-lg {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px; } }
|
||||
@media (min-width: 1280px) {
|
||||
.neomorphInset.is-nsmall-xl {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px; } }
|
||||
.neomorph.is-nxxsmall {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px; }
|
||||
@media (min-width: 640px) {
|
||||
.neomorph.is-nxxsmall-sm {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px; } }
|
||||
@media (min-width: 768px) {
|
||||
.neomorph.is-nxxsmall-md {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px; } }
|
||||
@media (min-width: 1024px) {
|
||||
.neomorph.is-nxxsmall-lg {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px; } }
|
||||
@media (min-width: 1280px) {
|
||||
.neomorph.is-nxxsmall-xl {
|
||||
--shadow-offset: 2px;
|
||||
--blur-radius: 4px; } }
|
||||
.neomorph.is-nxsmall {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px; }
|
||||
@media (min-width: 640px) {
|
||||
.neomorph.is-nxsmall-sm {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px; } }
|
||||
@media (min-width: 768px) {
|
||||
.neomorph.is-nxsmall-md {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px; } }
|
||||
@media (min-width: 1024px) {
|
||||
.neomorph.is-nxsmall-lg {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px; } }
|
||||
@media (min-width: 1280px) {
|
||||
.neomorph.is-nxsmall-xl {
|
||||
--shadow-offset: 3px;
|
||||
--blur-radius: 6px; } }
|
||||
.neomorph.is-nsmall {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px; }
|
||||
@media (min-width: 640px) {
|
||||
.neomorph.is-nsmall-sm {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px; } }
|
||||
@media (min-width: 768px) {
|
||||
.neomorph.is-nsmall-md {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px; } }
|
||||
@media (min-width: 1024px) {
|
||||
.neomorph.is-nsmall-lg {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px; } }
|
||||
@media (min-width: 1280px) {
|
||||
.neomorph.is-nsmall-xl {
|
||||
--shadow-offset: 6px;
|
||||
--blur-radius: 12px; } }
|
||||
.neomorph.is-nnormal {
|
||||
--shadow-offset: 8px;
|
||||
--blur-radius: 16px; }
|
||||
@media (min-width: 640px) {
|
||||
.neomorph.is-nnormal-sm {
|
||||
--shadow-offset: 8px;
|
||||
--blur-radius: 16px; } }
|
||||
@media (min-width: 768px) {
|
||||
.neomorph.is-nnormal-md {
|
||||
--shadow-offset: 8px;
|
||||
--blur-radius: 16px; } }
|
||||
@media (min-width: 1024px) {
|
||||
.neomorph.is-nnormal-lg {
|
||||
--shadow-offset: 8px;
|
||||
--blur-radius: 16px; } }
|
||||
@media (min-width: 1280px) {
|
||||
.neomorph.is-nnormal-xl {
|
||||
--shadow-offset: 8px;
|
||||
--blur-radius: 16px; } }
|
||||
|
||||
button.neoBtn, .neoBtn {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
|
||||
border: none;
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden; }
|
||||
button.neoBtn:focus, .neoBtn:focus {
|
||||
background-image: linear-gradient(-45deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
|
||||
border: none; }
|
||||
button.neoBtnSmall, .neoBtnSmall {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
|
||||
border: none; }
|
||||
button.neoBtnSmall:focus, .neoBtnSmall:focus {
|
||||
background-image: linear-gradient(-45deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
|
||||
border: none; }
|
||||
button.neoBtnSmall:focus:not(:active), .neoBtnSmall:focus:not(:active) {
|
||||
background-image: linear-gradient(-45deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow); }
|
||||
button.neoBtnSmall:focus input[type=file], .neoBtnSmall:focus input[type=file] {
|
||||
background-image: linear-gradient(-45deg, var(--primary-gradiend-light), var(--primary-gradiend-dark)); }
|
||||
button.neoBtnSmallPlain, .neoBtnSmallPlain {
|
||||
box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
|
||||
border: none; }
|
||||
button.neoBtnSmallPlain:focus, .neoBtnSmallPlain:focus {
|
||||
box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow);
|
||||
border: none; }
|
||||
button.neoBtnSmallInsetPlain, .neoBtnSmallInsetPlain {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
border: none; }
|
||||
button.neoBtnSmallInsetPlain:focus, .neoBtnSmallInsetPlain:focus {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
border: none; }
|
||||
button.neoBtnSmallXInsetPlain, .neoBtnSmallXInsetPlain {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
border: none; }
|
||||
button.neoBtnSmallXInsetPlain:focus, .neoBtnSmallXInsetPlain:focus {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
border: none; }
|
||||
button.neoBtnInsetPlain, .neoBtnInsetPlain {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
border: none; }
|
||||
button.neoBtnInsetPlain:focus, .neoBtnInsetPlain:focus {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
border: none; }
|
||||
button.neoBtnInsetPlain:focus:not(:active), .neoBtnInsetPlain:focus:not(:active) {
|
||||
background-image: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow); }
|
||||
|
||||
button.neoFile, .neoFile {
|
||||
box-shadow: 0px 0px 0px var(--light-shadow), 0px 0px 0px var(--dark-shadow);
|
||||
transition: all .2s linear;
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden; }
|
||||
button.neoFile:hover, .neoFile:hover {
|
||||
box-shadow: -3px -3px 6px var(--light-shadow), 3px 3px 6px var(--dark-shadow); }
|
||||
button.neoFile:focus-visible, .neoFile:focus-visible {
|
||||
background: linear-gradient(-45deg, var(--primary-gradiend-light), var(--primary-gradiend-dark));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
outline: none; }
|
||||
button.neoFile.isSelected, .neoFile.isSelected {
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow); }
|
||||
button.neoFile.isSelected:focus-visible, .neoFile.isSelected:focus-visible {
|
||||
background: linear-gradient(-45deg, var(--primary-gradiend-lighter), var(--primary-gradiend-darker));
|
||||
outline: none; }
|
||||
button.neoFile.is-active, button.neoFile.active, .neoFile.is-active, .neoFile.active {
|
||||
box-shadow: inset 3px 3px 6px var(--dark-shadow), inset -3px -3px 6px var(--light-shadow);
|
||||
color: var(--black); }
|
||||
|
||||
button.neoInput, .neoInput {
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
border: none; }
|
||||
button.neoInput:focus, .neoInput:focus {
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
border: none; }
|
||||
|
||||
button.neoSelect > select, .neoSelect > select {
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light));
|
||||
border: none; }
|
||||
button.neoSelect > select:focus, .neoSelect > select:focus {
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter));
|
||||
border: none; }
|
||||
|
||||
.neoCheckbox {
|
||||
opacity: 0;
|
||||
width: 0; }
|
||||
.neoCheckbox:focus + label:before {
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-darker), var(--primary-gradiend-lighter)) !important; }
|
||||
.neoCheckboxContainer {
|
||||
position: relative; }
|
||||
.neoCheckbox + label {
|
||||
padding: .15rem .15rem .15rem 2rem;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5; }
|
||||
.neoCheckbox + label:before {
|
||||
animation-name: none;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
border-radius: 100px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0rem;
|
||||
content: '';
|
||||
border: none;
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light)) !important; }
|
||||
.neoCheckbox:checked.is-checked-bold + label {
|
||||
font-weight: bold; }
|
||||
.neoCheckbox:checked + label:after {
|
||||
display: inline-block;
|
||||
width: .375rem;
|
||||
height: .6rem;
|
||||
top: .35rem;
|
||||
left: .55rem;
|
||||
transform: translateY(0rem) rotate(45deg);
|
||||
border-width: .1rem;
|
||||
border-top-width: 0.1rem;
|
||||
border-left-width: 0.1rem;
|
||||
border-style: solid;
|
||||
border-top-style: solid;
|
||||
border-left-style: solid;
|
||||
border-color: var(--text-color);
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
position: absolute;
|
||||
content: ''; }
|
||||
|
||||
hr.neoSeparatorFlat {
|
||||
height: 10px;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
box-shadow: -2px -2px 4px var(--light-shadow), 2px 2px 4px var(--dark-shadow);
|
||||
background: var(--background); }
|
||||
|
||||
hr.neoSeparatorPressed {
|
||||
height: 10px;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-dark), var(--primary-gradiend-light)); }
|
||||
|
||||
input.neoRange[type=range] {
|
||||
height: 30px;
|
||||
-webkit-appearance: none;
|
||||
width: 100%;
|
||||
background: transparent; }
|
||||
input.neoRange[type=range]:focus {
|
||||
outline: none; }
|
||||
input.neoRange[type=range]::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
animate: 0.2s;
|
||||
border-radius: 20px;
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(90deg, #fa9e9e, #faad9e, #fabd9e, #facc9e, #fadb9e, #faeb9e, #fafa9e, #ebfa9e, #dbfa9e, #ccfa9e, #bdfa9e, #adfa9e, #9efa9e, #9efaad, #9efabd, #9efacc, #9efadb, #9efaeb, #9efafa, #9eebfa, #9edbfa, #9eccfa, #9ebdfa, #9eadfa, #9e9efa, #ad9efa, #bd9efa, #cc9efa, #db9efa, #eb9efa, #fa9efa, #fa9eeb, #fa9edb, #fa9ecc, #fa9ebd, #fa9ead, #fa9ea0) !important; }
|
||||
input.neoRange[type=range]::-webkit-slider-thumb {
|
||||
border: none;
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 2px 2px 4px var(--dark-shadow), -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-light), var(--primary-gradiend-dark)) !important;
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
margin-top: -3px; }
|
||||
input.neoRange[type=range]:focus::-webkit-slider-runnable-track {
|
||||
background: linear-gradient(90deg, #fa9e9e, #faad9e, #fabd9e, #facc9e, #fadb9e, #faeb9e, #fafa9e, #ebfa9e, #dbfa9e, #ccfa9e, #bdfa9e, #adfa9e, #9efa9e, #9efaad, #9efabd, #9efacc, #9efadb, #9efaeb, #9efafa, #9eebfa, #9edbfa, #9eccfa, #9ebdfa, #9eadfa, #9e9efa, #ad9efa, #bd9efa, #cc9efa, #db9efa, #eb9efa, #fa9efa, #fa9eeb, #fa9edb, #fa9ecc, #fa9ebd, #fa9ead, #fa9ea0) !important; }
|
||||
input.neoRange[type=range]::-moz-range-track {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
border-radius: 20px;
|
||||
box-shadow: inset 2px 2px 4px var(--dark-shadow), inset -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(90deg, #fa9e9e, #faad9e, #fabd9e, #facc9e, #fadb9e, #faeb9e, #fafa9e, #ebfa9e, #dbfa9e, #ccfa9e, #bdfa9e, #adfa9e, #9efa9e, #9efaad, #9efabd, #9efacc, #9efadb, #9efaeb, #9efafa, #9eebfa, #9edbfa, #9eccfa, #9ebdfa, #9eadfa, #9e9efa, #ad9efa, #bd9efa, #cc9efa, #db9efa, #eb9efa, #fa9efa, #fa9eeb, #fa9edb, #fa9ecc, #fa9ebd, #fa9ead, #fa9ea0) !important;
|
||||
border: none; }
|
||||
input.neoRange[type=range]::-moz-range-thumb {
|
||||
border: none;
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 2px 2px 4px var(--dark-shadow), -2px -2px 4px var(--light-shadow);
|
||||
background: linear-gradient(145deg, var(--primary-gradiend-light), var(--primary-gradiend-dark)) !important;
|
||||
cursor: pointer; }
|
||||
|
||||
body {
|
||||
color: var(--text-color);
|
||||
background-color: var(--background); }
|
||||
|
||||
.background {
|
||||
background-color: var(--background); }
|
||||
|
||||
details > summary::-webkit-details-marker {
|
||||
color: var(--text-color); }
|
||||
|
||||
:focus-visible {
|
||||
outline: none; }
|
||||
|
||||
*, ::after, ::before {
|
||||
scrollbar-color: inherit;
|
||||
scrollbar-width: inherit; }
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px; }
|
||||
|
||||
::-webkit-scrollbar-button {
|
||||
width: 8px;
|
||||
height: 5px; }
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
border: thin solid transparent;
|
||||
box-shadow: none;
|
||||
border-radius: 10px; }
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--primary-color-dark);
|
||||
border: thin solid transparent;
|
||||
border-radius: 10px; }
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--primary-color-dark); }
|
||||
|
||||
::-moz-selection {
|
||||
/* Code for Firefox */
|
||||
color: var(--primary-color);
|
||||
background: var(--primary-color-dark); }
|
||||
|
||||
::selection {
|
||||
color: var(--primary-color);
|
||||
background: var(--primary-color-dark); }
|
||||
|
||||
.flex.flex-col-reverse > div:first-child {
|
||||
margin-top: 1rem; }
|
||||
|
||||
.loadAnimation span {
|
||||
animation-name: blink;
|
||||
animation-duration: 1.4s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-fill-mode: both; }
|
||||
|
||||
.loadAnimation span:nth-child(1) {
|
||||
animation-delay: .4s; }
|
||||
|
||||
.loadAnimation span:nth-child(2) {
|
||||
animation-delay: .3s; }
|
||||
|
||||
.loadAnimation span:nth-child(3) {
|
||||
animation-delay: .2s; }
|
||||
|
||||
.loadAnimation span:nth-child(4) {
|
||||
animation-delay: .2s; }
|
||||
|
||||
.loadAnimation span:nth-child(5) {
|
||||
animation-delay: .3s; }
|
||||
|
||||
.loadAnimation span:nth-child(6) {
|
||||
animation-delay: .4s; }
|
||||
|
8844
wwwroot/css/style.min.css
vendored
8844
wwwroot/css/style.min.css
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -0,0 +1,6 @@
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
a.button{
|
||||
text-decoration: none;
|
||||
}
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 326 KiB After Width: | Height: | Size: 301 KiB |
Binary file not shown.
Binary file not shown.
@ -2,28 +2,32 @@
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<title>decePubClient</title>
|
||||
<base href="/" />
|
||||
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="css/app.css" rel="stylesheet" />
|
||||
<link href="decePubClient.styles.css" rel="stylesheet" />
|
||||
<link href="manifest.json" rel="manifest" />
|
||||
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<title>Index</title>
|
||||
<base href="/" />
|
||||
<link href="_content/Blazored.Modal/blazored-modal.css" rel="stylesheet"/>
|
||||
<link href="css/style.min.css" rel="stylesheet" />
|
||||
<link href="manifest.json" rel="manifest" />
|
||||
<link rel="apple-touch-icon" sizes="512x512" href="imgs/icon-512.png" />
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="imgs/icon-192.png" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">Loading...</div>
|
||||
<div id="app" class="block relative h-screen w-full">
|
||||
<div class="mx-auto my-auto">Loading...</div>
|
||||
</div>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
An unhandled error has occurred.
|
||||
<a href="" class="reload">Reload</a>
|
||||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
<script src="_framework/blazor.webassembly.js"></script>
|
||||
<script>navigator.serviceWorker.register('service-worker.js');</script>
|
||||
<script src="_framework/blazor.webassembly.js"></script>
|
||||
<script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>
|
||||
<script src="_content/Append.Blazor.Notifications/scripts.js" type="module"></script>
|
||||
<script src="_content/Blazored.Modal/blazored.modal.js"></script>
|
||||
<script src="rxjs.7.4.0.min.js"></script>
|
||||
<script src="_content/DnetIndexedDb/dnet-indexeddb.js"></script>
|
||||
<script src="_content/Toolbelt.Blazor.HeadElement.Services/script.min.js"></script>
|
||||
|
||||
<script>navigator.serviceWorker.register('service-worker.js');</script>
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
164
wwwroot/main.js
164
wwwroot/main.js
@ -0,0 +1,164 @@
|
||||
window.cascadingStateInstanceReference = (dotNetReference) => {
|
||||
try {
|
||||
window.cascadingStateInstance = dotNetReference
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
window.logFromJs = (message, where) => {
|
||||
try {
|
||||
if (!window.cascadingStateInstance ||
|
||||
typeof window.cascadingStateInstance.invokeMethodAsync != "function")
|
||||
return
|
||||
window.cascadingStateInstance.invokeMethodAsync('LogFromJs', message, where)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
window.isServiceWorkerAvailable = () => {
|
||||
try {
|
||||
return navigator.serviceWorker.valueOf().controller != null
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
window.logFromJs(e.message, "isServiceWorkerAvailable")
|
||||
}
|
||||
}
|
||||
window.getLanguage = () => {
|
||||
try {
|
||||
return navigator.language.substr(0, 2)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
window.logFromJs(e.message, "getLanguage")
|
||||
}
|
||||
}
|
||||
window.setLanguage = (lang) => {
|
||||
try {
|
||||
navigator.language = lang
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
window.logFromJs(e.message, "setLanguage")
|
||||
}
|
||||
}
|
||||
window.isOnline = async () => {
|
||||
try {
|
||||
const isOnline = navigator.onLine
|
||||
//const url = "https://contents.nuvola.xyz/api/assets/6d9f5e55-2034-456f-92e7-d9ec51066443"
|
||||
//let isOnlineStatusCode = true
|
||||
|
||||
//try {
|
||||
// const online = await fetch(url)
|
||||
// isOnlineStatusCode = online.status >= 200 && online.status < 399
|
||||
//} catch (_) {
|
||||
// isOnlineStatusCode = false
|
||||
//}
|
||||
|
||||
if (navigator.connection !== undefined &&
|
||||
navigator.connection.effectiveType !== undefined &&
|
||||
navigator.connection.type !== undefined) {
|
||||
if (!isOnline)
|
||||
return isOnline
|
||||
|
||||
return navigator.connection.type === "cellular" ?
|
||||
navigator.connection.effectiveType.indexOf("2g") === -1 /*&& isOnlineStatusCode*/ :
|
||||
true
|
||||
} else
|
||||
return isOnline //&& isOnlineStatusCode
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
window.logFromJs(e.message, "isOnline")
|
||||
}
|
||||
}
|
||||
window.clearCache = async () => {
|
||||
try {
|
||||
const cachesNames = await caches.keys()
|
||||
for (let i = 0; i < cachesNames.length; i++)
|
||||
await caches.delete(cachesNames[i])
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
window.logFromJs(e.message, "clearCache")
|
||||
}
|
||||
}
|
||||
window.clearLocalStorage = () => {
|
||||
try {
|
||||
localStorage.removeItem("PrivateCacheData")
|
||||
localStorage.removeItem("AuthData")
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
window.logFromJs(e.message, "clearLocalStorage")
|
||||
}
|
||||
}
|
||||
window.reloadPage = () => {
|
||||
try {
|
||||
location.reload(true)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
window.logFromJs(e.message, "reloadPage")
|
||||
}
|
||||
}
|
||||
window.getHeight = (classId) => {
|
||||
try {
|
||||
const elements = document.querySelectorAll(classId)
|
||||
let px = 0
|
||||
if (!elements) return 0
|
||||
elements.forEach(v => px += v.offsetHeight)
|
||||
return px
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
window.logFromJs(e.message, "getHeight")
|
||||
}
|
||||
}
|
||||
window.setAppBadge = (counter) => {
|
||||
try {
|
||||
navigator.setAppBadge(counter)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
window.logFromJs(e.message, "setAppBadge")
|
||||
}
|
||||
}
|
||||
window.clearAppBadge = () => {
|
||||
try {
|
||||
navigator.clearAppBadge()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
window.logFromJs(e.message, "clearAppBadge")
|
||||
}
|
||||
}
|
||||
window.isMobileMedia = () => {
|
||||
try {
|
||||
return ((window.innerWidth > 0) ? window.innerWidth : screen.width) <= 768
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
window.logFromJs(e.message, "isMobileMedia")
|
||||
}
|
||||
}
|
||||
window.canShareStuff = () => {
|
||||
try {
|
||||
return navigator.share !== undefined
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
window.logFromJs(e.message, "canShareStuff")
|
||||
}
|
||||
}
|
||||
window.shareTextUrl = (title, text, url) => {
|
||||
try {
|
||||
navigator.share({
|
||||
title: title,
|
||||
text: text,
|
||||
url: url
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
window.logFromJs(e.message, "shareTextUrl")
|
||||
}
|
||||
}
|
||||
window.shareText = (title, text) => {
|
||||
try {
|
||||
navigator.share({
|
||||
title: title,
|
||||
text: text
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
window.logFromJs(e.message, "shareText")
|
||||
}
|
||||
}
|
@ -1,21 +1,42 @@
|
||||
{
|
||||
"name": "decePubClient",
|
||||
"short_name": "decePubClient",
|
||||
"description": "Dece this pub",
|
||||
"dir": "ltr",
|
||||
"start_url": "./",
|
||||
"scope": "/",
|
||||
"id": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#03173d",
|
||||
"background_color": "#FADCC7",
|
||||
"theme_color": "#FADCC7",
|
||||
"prefer_related_applications": false,
|
||||
"icons": [
|
||||
{
|
||||
"src": "icon-512.png",
|
||||
"src": "/imgs/icon-512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
},
|
||||
{
|
||||
"src": "icon-192.png",
|
||||
"src": "/imgs/icon-192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
}
|
||||
]
|
||||
],
|
||||
"categories": [
|
||||
"fediverse",
|
||||
"government",
|
||||
"social",
|
||||
"threads",
|
||||
"discussions",
|
||||
"communication"
|
||||
],
|
||||
"screenshots": [
|
||||
{
|
||||
"src": "/imgs/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"shortcuts": [],
|
||||
"version": "0.1"
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
[
|
||||
{
|
||||
"date": "2018-05-06",
|
||||
"temperatureC": 1,
|
||||
"summary": "Freezing"
|
||||
},
|
||||
{
|
||||
"date": "2018-05-07",
|
||||
"temperatureC": 14,
|
||||
"summary": "Bracing"
|
||||
},
|
||||
{
|
||||
"date": "2018-05-08",
|
||||
"temperatureC": -13,
|
||||
"summary": "Freezing"
|
||||
},
|
||||
{
|
||||
"date": "2018-05-09",
|
||||
"temperatureC": -16,
|
||||
"summary": "Balmy"
|
||||
},
|
||||
{
|
||||
"date": "2018-05-10",
|
||||
"temperatureC": -2,
|
||||
"summary": "Chilly"
|
||||
}
|
||||
]
|
8019
wwwroot/vendor/bulma.css
vendored
8019
wwwroot/vendor/bulma.css
vendored
File diff suppressed because it is too large
Load Diff
6
wwwroot/vendor/fontawesome.css
vendored
6
wwwroot/vendor/fontawesome.css
vendored
@ -1,7 +1,3 @@
|
||||
/*!
|
||||
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
.fa,
|
||||
.fas,
|
||||
.far,
|
||||
@ -187,8 +183,6 @@
|
||||
.fa-inverse {
|
||||
color: #fff; }
|
||||
|
||||
/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
|
||||
readers do not read off random characters that represent icons */
|
||||
.fa-500px:before {
|
||||
content: "\f26e"; }
|
||||
|
||||
|
3626
wwwroot/vendor/ionicons.css
vendored
3626
wwwroot/vendor/ionicons.css
vendored
File diff suppressed because one or more lines are too long
6
wwwroot/vendor/ionicons.min.css
vendored
6
wwwroot/vendor/ionicons.min.css
vendored
File diff suppressed because one or more lines are too long
6
wwwroot/vendor/solid.css
vendored
6
wwwroot/vendor/solid.css
vendored
@ -1,14 +1,10 @@
|
||||
/*!
|
||||
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-solid-900.eot");
|
||||
src: url("../webfonts/fa-solid-900.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.woff") format("woff"), url("../webfonts/fa-solid-900.ttf") format("truetype"), url("../webfonts/fa-solid-900.svg#fontawesome") format("svg"); }
|
||||
src: url("../webfonts/fa-solid-900.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.woff") format("woff"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }
|
||||
|
||||
.fa,
|
||||
.fas {
|
||||
|
1039
wwwroot/vendor/tailwind.css
vendored
1039
wwwroot/vendor/tailwind.css
vendored
File diff suppressed because it is too large
Load Diff
80
wwwroot/vendor/toggle-dark-light-mode.css
vendored
80
wwwroot/vendor/toggle-dark-light-mode.css
vendored
@ -0,0 +1,80 @@
|
||||
.toggle-checkbox {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.toggle-slot {
|
||||
position: relative;
|
||||
height: 2em;
|
||||
width: 3em;
|
||||
border: none;
|
||||
border-radius: 10em;
|
||||
background-color: white;
|
||||
transition: background-color 250ms;
|
||||
}
|
||||
|
||||
.toggle-checkbox:checked ~ .toggle-slot {
|
||||
background-color: #374151;
|
||||
}
|
||||
|
||||
.toggle-button {
|
||||
transform: translate(1.6em, 0.5em);
|
||||
position: absolute;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
border-radius: 50%;
|
||||
background-color: #ffeccf;
|
||||
transition: background-color 250ms, border-color 250ms, transform 500ms cubic-bezier(.26, 2, .46, .71);
|
||||
}
|
||||
|
||||
.toggle-checkbox:checked ~ .toggle-slot .toggle-button {
|
||||
background-color: #485367;
|
||||
transform: translate(0.5em, 0.5em);
|
||||
}
|
||||
|
||||
.sun-icon {
|
||||
position: absolute;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
color: #ffbb52;
|
||||
}
|
||||
|
||||
.sun-icon-wrapper {
|
||||
position: absolute;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
opacity: 1;
|
||||
transform: translate(0.55em, 0.25em) rotate(15deg);
|
||||
transform-origin: 50% 50%;
|
||||
transition: opacity 150ms, transform 500ms cubic-bezier(.26, 2, .46, .71);
|
||||
}
|
||||
|
||||
.toggle-checkbox:checked ~ .toggle-slot .sun-icon-wrapper {
|
||||
opacity: 0;
|
||||
transform: translate(1.6em, 0.5em) rotate(0deg);
|
||||
}
|
||||
|
||||
.moon-icon {
|
||||
position: absolute;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.moon-icon-wrapper {
|
||||
position: absolute;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
opacity: 0;
|
||||
transform: translate(1.6em, .5em) rotate(0deg);
|
||||
transform-origin: 50% 50%;
|
||||
transition: opacity 150ms, transform 500ms cubic-bezier(.26, 2.5, .46, .71);
|
||||
}
|
||||
|
||||
.toggle-checkbox:checked ~ .toggle-slot .moon-icon-wrapper {
|
||||
opacity: 1;
|
||||
transform: translate(1.85em, 0.15em) rotate(-15deg);
|
||||
}
|
Loading…
Reference in New Issue
Block a user