Theme setting updates
This commit is contained in:
parent
c1715aecc6
commit
77f5bb227e
@ -12,6 +12,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<script src="~/_framework/blazor.server.js" asp-append-version="true"></script>
|
||||
<script src="~/_content/MudBlazor/MudBlazor.min.js" asp-append-version="true"></script>
|
||||
<script src="./_content/BTCPayApp.UI/js/site.js" asp-append-version="true"></script>
|
||||
<link href="~/_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" asp-append-version="true"/>
|
||||
<link href="./_content/BTCPayApp.UI/css/mudblazor-overrides.css" rel="stylesheet" asp-append-version="true"/>
|
||||
<link href="./_content/BTCPayApp.UI/css/theme.css" rel="stylesheet" asp-append-version="true"/>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Fluxor;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BTCPayApp.UI.Features;
|
||||
|
||||
@ -9,11 +10,12 @@ public class UIState
|
||||
private string SystemTheme { get; init; } = Constants.LightTheme;
|
||||
public bool IsDarkMode { get; set; }
|
||||
|
||||
public record ApplySystemPreference(string Theme);
|
||||
public record ApplyTheme(string Theme);
|
||||
public record ApplyUserTheme(string Theme);
|
||||
public record SetSystemPreference(string Theme);
|
||||
public record SetUserTheme(string Theme);
|
||||
|
||||
[ReducerMethod]
|
||||
public static UIState Reduce(UIState state, ApplySystemPreference action)
|
||||
public static UIState Reduce(UIState state, SetSystemPreference action)
|
||||
{
|
||||
var effectiveTheme = state.SelectedTheme == Constants.SystemTheme ? action.Theme : state.SelectedTheme;
|
||||
return new UIState
|
||||
@ -25,7 +27,7 @@ public class UIState
|
||||
}
|
||||
|
||||
[ReducerMethod]
|
||||
public static UIState Reduce(UIState state, ApplyTheme action)
|
||||
public static UIState Reduce(UIState state, SetUserTheme action)
|
||||
{
|
||||
var effectiveTheme = action.Theme == Constants.SystemTheme ? state.SystemTheme : action.Theme;
|
||||
return new UIState
|
||||
@ -35,4 +37,23 @@ public class UIState
|
||||
IsDarkMode = effectiveTheme == Constants.DarkTheme
|
||||
};
|
||||
}
|
||||
|
||||
public class UIEffects
|
||||
{
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
|
||||
public UIEffects(IJSRuntime jsRuntime)
|
||||
{
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
[EffectMethod]
|
||||
public async Task ApplyUserThemeEffect(ApplyUserTheme action, IDispatcher dispatcher)
|
||||
{
|
||||
// store
|
||||
dispatcher.Dispatch(new SetUserTheme(action.Theme));
|
||||
// ui
|
||||
await _jsRuntime.InvokeVoidAsync("setTheme", action.Theme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,13 +8,5 @@
|
||||
|
||||
<MudText Typo="Typo.h5" Class="mb-8">Settings</MudText>
|
||||
|
||||
<Settings Theme="@UiState.Value.SelectedTheme" OnThemeChange="SetTheme" />
|
||||
|
||||
@code {
|
||||
private Task SetTheme(string theme)
|
||||
{
|
||||
Dispatcher.Dispatch(new UIState.ApplyTheme(theme));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
<Settings Theme="@UiState.Value.SelectedTheme" OnThemeChange="theme => Dispatcher.Dispatch(new UIState.ApplyUserTheme(theme))" />
|
||||
|
||||
|
||||
@ -17,8 +17,10 @@
|
||||
<MudLayout>
|
||||
<MudAppBar Elevation="1">
|
||||
<MudIconButton Icon="@BTCPayIcons.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@DrawerToggle"/>
|
||||
<MudImage Src="_content/BTCPayApp.UI/img/logo.svg" Height="24" Alt="BTCPay Server" Class="ml-3" />
|
||||
<MudText Typo="Typo.h6" Class="ml-3">BTCPay Server</MudText>
|
||||
<MudLink Href="@Routes.Home" Underline="Underline.None" Color="Color.Inherit" Class="d-flex align-center ml-2">
|
||||
<MudImage Src="_content/BTCPayApp.UI/img/logo.svg" Height="24" Alt="BTCPay Server" />
|
||||
<MudText Typo="Typo.h6" Class="ml-3">BTCPay Server</MudText>
|
||||
</MudLink>
|
||||
</MudAppBar>
|
||||
<MudDrawer @bind-Open="_isDrawerOpen" ClipMode="DrawerClipMode.Always" Class="py-5">
|
||||
<MudNavMenu Color="Color.Success" Bordered="true">
|
||||
@ -29,7 +31,7 @@
|
||||
</MudNavMenu>
|
||||
</MudDrawer>
|
||||
<MudMainContent>
|
||||
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Size="Size.Small" Class="@(LoadingStateSelection.Value.Count > 0 ? null : "invisible")"/>
|
||||
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Size="Size.Small" Class="@(LoadingStateSelection.Value.Any() ? null : "invisible")"/>
|
||||
<MudContainer Fixed="true" Class="py-5">
|
||||
@Body
|
||||
</MudContainer>
|
||||
@ -52,16 +54,19 @@
|
||||
{
|
||||
LoadingStateSelection.Select(state => state.Loading);
|
||||
DarkModeStateSelection.Select(state => state.IsDarkMode);
|
||||
if (_darkMode is null && !Store.Initialized.IsCompleted)
|
||||
var state = await ConfigProvider.Get<UIState>(StateMiddleware.UiStateConfigKey);
|
||||
if (state != null)
|
||||
{
|
||||
var state = await ConfigProvider.Get<UIState>(StateMiddleware.UiStateConfigKey);
|
||||
if (state != null)
|
||||
if (_darkMode is null && !Store.Initialized.IsCompleted)
|
||||
{
|
||||
_darkMode = state.IsDarkMode;
|
||||
_ = Store.Initialized.ContinueWith(task => _darkMode = null);
|
||||
_ = Store.Initialized.ContinueWith(_ => _darkMode = null);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(state.SelectedTheme))
|
||||
{
|
||||
Dispatcher.Dispatch(new UIState.ApplyUserTheme(state.SelectedTheme));
|
||||
}
|
||||
}
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
@ -79,6 +84,6 @@
|
||||
private void ApplySystemPreference(object? sender, bool isDarkMode)
|
||||
{
|
||||
var systemTheme = isDarkMode ? Constants.DarkTheme : Constants.LightTheme;
|
||||
Dispatcher.Dispatch(new UIState.ApplySystemPreference(systemTheme));
|
||||
Dispatcher.Dispatch(new UIState.SetSystemPreference(systemTheme));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
@inherits Fluxor.Blazor.Web.Components.FluxorLayout
|
||||
@inject IStateSelection<RootState, HashSet<RootState.LoadingHandles>> LoadingStateSelection
|
||||
@inject IStateSelection<UIState, bool> DarkModeStateSelection
|
||||
@inject IDispatcher Dispatcher
|
||||
@inject IConfigProvider ConfigProvider
|
||||
@inject ISystemThemeProvider SystemThemeProvider
|
||||
@inject IStore Store
|
||||
@ -13,7 +14,7 @@
|
||||
<MudSnackbarProvider/>
|
||||
|
||||
<MudLayout>
|
||||
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Size="Size.Small" Class="@(LoadingStateSelection.Value.Count > 0 ? null : "invisible")"/>
|
||||
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Size="Size.Small" Class="@(LoadingStateSelection.Value.Any() ? null : "invisible")"/>
|
||||
<MudMainContent>
|
||||
@Body
|
||||
</MudMainContent>
|
||||
@ -28,26 +29,36 @@
|
||||
{
|
||||
LoadingStateSelection.Select(state => state.Loading);
|
||||
DarkModeStateSelection.Select(state => state.IsDarkMode);
|
||||
if (_darkMode is null && !Store.Initialized.IsCompleted)
|
||||
var state = await ConfigProvider.Get<UIState>(StateMiddleware.UiStateConfigKey);
|
||||
if (state != null)
|
||||
{
|
||||
var state = await ConfigProvider.Get<UIState>(StateMiddleware.UiStateConfigKey);
|
||||
if (state != null)
|
||||
if (_darkMode is null && !Store.Initialized.IsCompleted)
|
||||
{
|
||||
_darkMode = state.IsDarkMode;
|
||||
_ = Store.Initialized.ContinueWith(_ => _darkMode = null);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(state.SelectedTheme))
|
||||
{
|
||||
Dispatcher.Dispatch(new UIState.ApplyUserTheme(state.SelectedTheme));
|
||||
}
|
||||
}
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
protected override Task OnAfterRenderAsync(bool firstRender)
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
if (SystemThemeProvider is MudBlazorThemeProvider mudThemeProvider)
|
||||
mudThemeProvider.MudThemeProvider = _mudThemeProvider;
|
||||
ApplySystemPreference(this, await _mudThemeProvider.GetSystemPreference());
|
||||
SystemThemeProvider.SystemThemeChanged += ApplySystemPreference;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void ApplySystemPreference(object? sender, bool isDarkMode)
|
||||
{
|
||||
var theme = isDarkMode ? Constants.DarkTheme : Constants.LightTheme;
|
||||
Dispatcher.Dispatch(new UIState.SetSystemPreference(theme));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
:root {
|
||||
}
|
||||
|
||||
.mud-icon-button,
|
||||
.mud-button-text {
|
||||
--btcpay-btn-disabled-opacity: 0.65;
|
||||
|
||||
@ -12,16 +11,13 @@
|
||||
padding: var(--btcpay-btn-padding-y) var(--btcpay-btn-padding-x) !important;
|
||||
}
|
||||
|
||||
.mud-icon-button:hover,
|
||||
.mud-button-text:hover,
|
||||
.mud-icon-button:focus-visible,
|
||||
.mud-button-text:focus-visible {
|
||||
color: var(--btcpay-btn-hover-color) !important;
|
||||
background-color: var(--btcpay-btn-hover-bg) !important;
|
||||
border-color: var(--btcpay-btn-hover-border-color) !important;
|
||||
}
|
||||
|
||||
.mud-icon-button:disabled,
|
||||
.mud-button-text:disabled {
|
||||
color: var(--btcpay-btn-disabled-color) !important;
|
||||
background-color: var(--btcpay-btn-disabled-bg) !important;
|
||||
@ -45,7 +41,7 @@
|
||||
--btcpay-btn-disabled-bg: var(--btcpay-primary);
|
||||
--btcpay-btn-disabled-border-color: var(--btcpay-primary-border);
|
||||
}
|
||||
.mud-icon-button.mud-button-outlined,
|
||||
|
||||
.mud-button-text-default,
|
||||
.mud-button-text-secondary,
|
||||
.mud-button-filled-secondary {
|
||||
@ -64,7 +60,6 @@
|
||||
--btcpay-btn-disabled-border-color: var(--btcpay-secondary-border);
|
||||
}
|
||||
|
||||
.mud-icon-button-size-large,
|
||||
.mud-button-text-size-large {
|
||||
--btcpay-btn-padding-y: 1rem;
|
||||
--btcpay-btn-padding-x: 2rem;
|
||||
@ -72,7 +67,6 @@
|
||||
--btcpay-btn-border-radius: var(--btcpay-border-radius-l);
|
||||
}
|
||||
|
||||
.mud-icon-button-size-medium,
|
||||
.mud-button-text-size-medium {
|
||||
--btcpay-btn-padding-x: 1.5rem;
|
||||
--btcpay-btn-padding-y: 0.5rem;
|
||||
@ -80,7 +74,6 @@
|
||||
--btcpay-btn-border-radius: var(--btcpay-border-radius);
|
||||
}
|
||||
|
||||
.mud-icon-button-size-small,
|
||||
.mud-button-text-size-small {
|
||||
--btcpay-btn-padding-y: 0.5rem;
|
||||
--btcpay-btn-padding-x: 1.5rem;
|
||||
|
||||
@ -1,33 +1,4 @@
|
||||
#blazor-error-ui {
|
||||
background: lightyellow;
|
||||
bottom: 0;
|
||||
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
||||
display: none;
|
||||
left: 0;
|
||||
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#blazor-error-ui .dismiss {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
top: 0.5rem;
|
||||
}
|
||||
|
||||
.blazor-error-boundary {
|
||||
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
|
||||
padding: 1rem 1rem 1rem 3.7rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.blazor-error-boundary::after {
|
||||
content: "An error has occurred."
|
||||
}
|
||||
|
||||
@media (min-width: 0px) {
|
||||
@media (min-width: 0px) {
|
||||
#OnboardingCarousel {
|
||||
min-height: 430px;
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
<title>BTCPay Server</title>
|
||||
<script src="./_framework/blazor.webview.js"></script>
|
||||
<script src="./_content/MudBlazor/MudBlazor.min.js"></script>
|
||||
<script src="./_content/BTCPayApp.UI/js/site.js"></script>
|
||||
<link href="./_content/MudBlazor/MudBlazor.min.css" rel="stylesheet"/>
|
||||
<link href="./_content/BTCPayApp.UI/css/mudblazor-overrides.css" rel="stylesheet"/>
|
||||
<link href="./_content/BTCPayApp.UI/css/theme.css" rel="stylesheet"/>
|
||||
|
||||
11
BTCPayApp.UI/wwwroot/js/site.js
Normal file
11
BTCPayApp.UI/wwwroot/js/site.js
Normal file
@ -0,0 +1,11 @@
|
||||
// Theme Switch
|
||||
const THEMES = ['light', 'dark']
|
||||
const THEME_ATTR = 'data-btcpay-theme'
|
||||
const systemColorMode = window.matchMedia('(prefers-color-scheme: dark)').matches ? THEMES[1] : THEMES[0]
|
||||
function setTheme(mode) {
|
||||
if (THEMES.includes(mode) && mode !== systemColorMode) {
|
||||
document.documentElement.setAttribute(THEME_ATTR, mode)
|
||||
} else {
|
||||
document.documentElement.removeAttribute(THEME_ATTR)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user