mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 09:42:07 +08:00
UX: Introduces a splash screen behind a hidden site setting (#17094)
This PR introduces a new hidden site setting that allows admins to display a splash screen while site assets load.
The splash screen can be enabled via the `splash_screen` hidden site setting.
This is what the splash screen currently looks like
5ceb72f085
.mp4
Once site assets load, the splash screen is automatically removed.
To control the loading text that shows in the splash screen, you can change the preloader_text translation string in admin > customize > text
This commit is contained in:
parent
624c684d51
commit
e82a2ce9ae
|
@ -52,6 +52,9 @@ const Discourse = Application.extend({
|
||||||
start() {
|
start() {
|
||||||
document.querySelector("noscript")?.remove();
|
document.querySelector("noscript")?.remove();
|
||||||
|
|
||||||
|
// The app booted. Remove the splash screen
|
||||||
|
document.querySelector("#d-splash")?.remove();
|
||||||
|
|
||||||
if (Error.stackTraceLimit) {
|
if (Error.stackTraceLimit) {
|
||||||
// We need Errors to have full stack traces for `lib/source-identifier`
|
// We need Errors to have full stack traces for `lib/source-identifier`
|
||||||
Error.stackTraceLimit = Infinity;
|
Error.stackTraceLimit = Infinity;
|
||||||
|
|
59
app/assets/stylesheets/d_splash.scss
Normal file
59
app/assets/stylesheets/d_splash.scss
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
html {
|
||||||
|
background: var(--secondary);
|
||||||
|
// needed because this sheet loads early and we want no scroll bars until
|
||||||
|
// the splash is removed.
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#d-splash {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
position: relative;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
|
||||||
|
.preloader-image {
|
||||||
|
max-width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
object-fit: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preloader-text {
|
||||||
|
padding-top: 5em;
|
||||||
|
position: absolute;
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
place-items: center;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
animation: loading-text 3s infinite;
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 5em;
|
||||||
|
margin: 0 0.1em;
|
||||||
|
left: 100%;
|
||||||
|
// TODO: this needs R2 RTL magic
|
||||||
|
.rtl & {
|
||||||
|
left: 0;
|
||||||
|
right: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading-text {
|
||||||
|
0% {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
25% {
|
||||||
|
content: ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
content: "..";
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
content: "...";
|
||||||
|
}
|
||||||
|
}
|
|
@ -429,6 +429,11 @@ module ApplicationHelper
|
||||||
", app-argument=discourse://new?siteUrl=#{Discourse.base_url}" : ""
|
", app-argument=discourse://new?siteUrl=#{Discourse.base_url}" : ""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def include_splash_screen?
|
||||||
|
# A bit basic for now but will be expanded later
|
||||||
|
SiteSetting.splash_screen
|
||||||
|
end
|
||||||
|
|
||||||
def allow_plugins?
|
def allow_plugins?
|
||||||
!request.env[ApplicationController::NO_PLUGINS]
|
!request.env[ApplicationController::NO_PLUGINS]
|
||||||
end
|
end
|
||||||
|
|
17
app/views/common/_discourse_splash.html.erb
Normal file
17
app/views/common/_discourse_splash.html.erb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<%- unless customization_disabled? %>
|
||||||
|
<section id="d-splash">
|
||||||
|
<%= discourse_stylesheet_link_tag 'd_splash', theme_id: nil %>
|
||||||
|
|
||||||
|
<img
|
||||||
|
class="preloader-image"
|
||||||
|
src="/images/preloader.svg"
|
||||||
|
alt="<%=SiteSetting.title%>"
|
||||||
|
>
|
||||||
|
|
||||||
|
<div class="preloader-text">
|
||||||
|
<span>
|
||||||
|
<%= I18n.t("js.preloader_text") %>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<%- end %>
|
|
@ -9,6 +9,10 @@
|
||||||
<%= render partial: "layouts/head" %>
|
<%= render partial: "layouts/head" %>
|
||||||
<%= discourse_csrf_tags %>
|
<%= discourse_csrf_tags %>
|
||||||
|
|
||||||
|
<%- if include_splash_screen? %>
|
||||||
|
<link rel="preload" as="image" href="/images/preloader.svg">
|
||||||
|
<%- end %>
|
||||||
|
|
||||||
<%- if SiteSetting.enable_escaped_fragments? %>
|
<%- if SiteSetting.enable_escaped_fragments? %>
|
||||||
<meta name="fragment" content="!">
|
<meta name="fragment" content="!">
|
||||||
<%- end %>
|
<%- end %>
|
||||||
|
@ -70,6 +74,10 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="<%= body_classes %>">
|
<body class="<%= body_classes %>">
|
||||||
|
<%- if include_splash_screen? %>
|
||||||
|
<%= render partial: "common/discourse_splash" %>
|
||||||
|
<%- end %>
|
||||||
|
|
||||||
<discourse-assets>
|
<discourse-assets>
|
||||||
<discourse-assets-stylesheets>
|
<discourse-assets-stylesheets>
|
||||||
<%= render partial: "common/discourse_stylesheet" %>
|
<%= render partial: "common/discourse_stylesheet" %>
|
||||||
|
|
|
@ -3691,6 +3691,8 @@ en:
|
||||||
create_post: "Reply / See"
|
create_post: "Reply / See"
|
||||||
readonly: "See"
|
readonly: "See"
|
||||||
|
|
||||||
|
preloader_text: "Loading"
|
||||||
|
|
||||||
lightbox:
|
lightbox:
|
||||||
download: "download"
|
download: "download"
|
||||||
open: "original image"
|
open: "original image"
|
||||||
|
|
|
@ -2369,6 +2369,8 @@ en:
|
||||||
use_name_for_username_suggestions: "Use a user's full name when suggesting usernames."
|
use_name_for_username_suggestions: "Use a user's full name when suggesting usernames."
|
||||||
suggest_weekends_in_date_pickers: "Include weekends (Saturday and Sunday) in date picker suggestions (disable this if you use Discourse only on weekdays, Monday through Friday)."
|
suggest_weekends_in_date_pickers: "Include weekends (Saturday and Sunday) in date picker suggestions (disable this if you use Discourse only on weekdays, Monday through Friday)."
|
||||||
|
|
||||||
|
splash_screen: "Displays a temporary loading screen while site assets load"
|
||||||
|
|
||||||
errors:
|
errors:
|
||||||
invalid_css_color: "Invalid color. Enter a color name or hex value."
|
invalid_css_color: "Invalid color. Enter a color name or hex value."
|
||||||
invalid_email: "Invalid email address."
|
invalid_email: "Invalid email address."
|
||||||
|
@ -2422,7 +2424,7 @@ en:
|
||||||
google_oauth2_hd_groups: "You must first set 'google oauth2 hd' before enabling this setting."
|
google_oauth2_hd_groups: "You must first set 'google oauth2 hd' before enabling this setting."
|
||||||
search_tokenize_chinese_enabled: "You must disable 'search_tokenize_chinese' before enabling this setting."
|
search_tokenize_chinese_enabled: "You must disable 'search_tokenize_chinese' before enabling this setting."
|
||||||
search_tokenize_japanese_enabled: "You must disable 'search_tokenize_japanese' before enabling this setting."
|
search_tokenize_japanese_enabled: "You must disable 'search_tokenize_japanese' before enabling this setting."
|
||||||
discourse_connect_cannot_be_enabled_if_second_factor_enforced: "You cannot enable DiscourseConnect if 2FA is enforced."
|
discourse_connect_cannot_be_enabled_if_second_factor_enforced: "You cannot enable DiscourseConnect if 2FA is enforced."
|
||||||
|
|
||||||
placeholder:
|
placeholder:
|
||||||
discourse_connect_provider_secrets:
|
discourse_connect_provider_secrets:
|
||||||
|
|
|
@ -2424,6 +2424,10 @@ uncategorized:
|
||||||
default: true
|
default: true
|
||||||
hidden: true
|
hidden: true
|
||||||
|
|
||||||
|
splash_screen:
|
||||||
|
default: false
|
||||||
|
hidden: true
|
||||||
|
|
||||||
suggest_weekends_in_date_pickers:
|
suggest_weekends_in_date_pickers:
|
||||||
client: true
|
client: true
|
||||||
default: true
|
default: true
|
||||||
|
|
85
public/images/preloader.svg
Normal file
85
public/images/preloader.svg
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
height="2000"
|
||||||
|
width="2000"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
/* these need to be injected dynamicly to match theme colors */
|
||||||
|
--primary: #222222;
|
||||||
|
--secondary: #ffffff;
|
||||||
|
--tertiary: #f15c21;
|
||||||
|
--highlight: #f0ea88;
|
||||||
|
--quaternary: #65ccff;
|
||||||
|
--success: #009900;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* these styles need to live here because the SVG has a different scope */
|
||||||
|
.dots {
|
||||||
|
animation-name: loader;
|
||||||
|
animation-timing-function: ease-in-out;
|
||||||
|
animation-duration: 3s;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
stroke: #fff;
|
||||||
|
stroke-width: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dots:first-child {
|
||||||
|
fill: var(--tertiary);
|
||||||
|
animation-delay: 1.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dots:nth-child(2) {
|
||||||
|
fill: var(--tertiary);
|
||||||
|
animation-delay: 1.3s;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dots:nth-child(3) {
|
||||||
|
fill: var(--highlight);
|
||||||
|
animation-delay: 1.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dots:nth-child(4) {
|
||||||
|
fill: var(--quaternary);
|
||||||
|
animation-delay: 1.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dots:nth-child(5) {
|
||||||
|
fill: var(--quaternary);
|
||||||
|
animation-delay: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
transform: translateX(-125px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loader {
|
||||||
|
15% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
45% {
|
||||||
|
transform: translatex(calc(250px));
|
||||||
|
}
|
||||||
|
|
||||||
|
65% {
|
||||||
|
transform: translatex(calc(250px));
|
||||||
|
}
|
||||||
|
|
||||||
|
95% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<g class="container">
|
||||||
|
<circle class="dots" cy="50%" r="10" cx="50vw"></circle>
|
||||||
|
<circle class="dots" cy="50%" r="10" cx="50vw"></circle>
|
||||||
|
<circle class="dots" cy="50%" r="10" cx="50vw"></circle>
|
||||||
|
<circle class="dots" cy="50%" r="10" cx="50vw"></circle>
|
||||||
|
<circle class="dots" cy="50%" r="10" cx="50vw"></circle>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -11,7 +11,7 @@ describe Stylesheet::Compiler do
|
||||||
|
|
||||||
it "can compile '#{path}' css" do
|
it "can compile '#{path}' css" do
|
||||||
css, _map = Stylesheet::Compiler.compile_asset(path)
|
css, _map = Stylesheet::Compiler.compile_asset(path)
|
||||||
expect(css.length).to be > 1000
|
expect(css.length).to be > 500
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -529,6 +529,28 @@ RSpec.describe ApplicationController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "splash_screen" do
|
||||||
|
let(:admin) { Fabricate(:admin) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
admin
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds a preloader splash screen when enabled' do
|
||||||
|
get '/'
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.body).not_to include("d-splash")
|
||||||
|
|
||||||
|
SiteSetting.splash_screen = true
|
||||||
|
|
||||||
|
get '/'
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.body).to include("d-splash")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'Delegated auth' do
|
describe 'Delegated auth' do
|
||||||
let :public_key do
|
let :public_key do
|
||||||
<<~TXT
|
<<~TXT
|
||||||
|
|
Loading…
Reference in New Issue
Block a user