diff --git a/BTCPayApp.Server/Pages/_Host.cshtml b/BTCPayApp.Server/Pages/_Host.cshtml
index 9511ed6..fbee1a5 100644
--- a/BTCPayApp.Server/Pages/_Host.cshtml
+++ b/BTCPayApp.Server/Pages/_Host.cshtml
@@ -12,6 +12,7 @@
+
diff --git a/BTCPayApp.UI/Features/UIState.cs b/BTCPayApp.UI/Features/UIState.cs
index f987a4a..a55e575 100644
--- a/BTCPayApp.UI/Features/UIState.cs
+++ b/BTCPayApp.UI/Features/UIState.cs
@@ -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);
+ }
+ }
}
diff --git a/BTCPayApp.UI/Pages/SettingsPage.razor b/BTCPayApp.UI/Pages/SettingsPage.razor
index a41935f..3453b83 100644
--- a/BTCPayApp.UI/Pages/SettingsPage.razor
+++ b/BTCPayApp.UI/Pages/SettingsPage.razor
@@ -8,13 +8,5 @@
Settings
-
-
-@code {
- private Task SetTheme(string theme)
- {
- Dispatcher.Dispatch(new UIState.ApplyTheme(theme));
- return Task.CompletedTask;
- }
-}
+
diff --git a/BTCPayApp.UI/Shared/MainLayout.razor b/BTCPayApp.UI/Shared/MainLayout.razor
index 0e2526b..86a2020 100644
--- a/BTCPayApp.UI/Shared/MainLayout.razor
+++ b/BTCPayApp.UI/Shared/MainLayout.razor
@@ -17,8 +17,10 @@
-
- BTCPay Server
+
+
+ BTCPay Server
+
@@ -29,7 +31,7 @@
-
+
@Body
@@ -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(StateMiddleware.UiStateConfigKey);
+ if (state != null)
{
- var state = await ConfigProvider.Get(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));
}
}
diff --git a/BTCPayApp.UI/Shared/SimpleLayout.razor b/BTCPayApp.UI/Shared/SimpleLayout.razor
index 29f4d4e..a7395a2 100644
--- a/BTCPayApp.UI/Shared/SimpleLayout.razor
+++ b/BTCPayApp.UI/Shared/SimpleLayout.razor
@@ -3,6 +3,7 @@
@inherits Fluxor.Blazor.Web.Components.FluxorLayout
@inject IStateSelection> LoadingStateSelection
@inject IStateSelection DarkModeStateSelection
+@inject IDispatcher Dispatcher
@inject IConfigProvider ConfigProvider
@inject ISystemThemeProvider SystemThemeProvider
@inject IStore Store
@@ -13,7 +14,7 @@
-
+
@Body
@@ -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(StateMiddleware.UiStateConfigKey);
+ if (state != null)
{
- var state = await ConfigProvider.Get(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));
}
}
diff --git a/BTCPayApp.UI/wwwroot/css/mudblazor-overrides.css b/BTCPayApp.UI/wwwroot/css/mudblazor-overrides.css
index f1cafcb..c619302 100644
--- a/BTCPayApp.UI/wwwroot/css/mudblazor-overrides.css
+++ b/BTCPayApp.UI/wwwroot/css/mudblazor-overrides.css
@@ -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;
diff --git a/BTCPayApp.UI/wwwroot/css/site.css b/BTCPayApp.UI/wwwroot/css/site.css
index e260f96..c7ae6e6 100644
--- a/BTCPayApp.UI/wwwroot/css/site.css
+++ b/BTCPayApp.UI/wwwroot/css/site.css
@@ -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;
}
diff --git a/BTCPayApp.UI/wwwroot/index.html b/BTCPayApp.UI/wwwroot/index.html
index dc0ab42..fb4bd9d 100644
--- a/BTCPayApp.UI/wwwroot/index.html
+++ b/BTCPayApp.UI/wwwroot/index.html
@@ -7,6 +7,7 @@
BTCPay Server
+
diff --git a/BTCPayApp.UI/wwwroot/js/site.js b/BTCPayApp.UI/wwwroot/js/site.js
new file mode 100644
index 0000000..0d19083
--- /dev/null
+++ b/BTCPayApp.UI/wwwroot/js/site.js
@@ -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)
+ }
+}