From e69125341943accb9fdbfbaf30d2da417cc5bcc9 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Mon, 17 Jul 2023 20:37:02 -0700 Subject: [PATCH] UI loading improvements Fixed: Caching for dynamically loaded JS files Fixed: Incorrect caching of initialize.js (cherry picked from commit f0cb5b81f140c67fa84162e094cc4e0f3476f5da) --- frontend/build/webpack.config.js | 5 +-- frontend/src/bootstrap.tsx | 21 ++++++++++++ frontend/src/index.ejs | 13 ++++--- frontend/src/index.js | 26 -------------- frontend/src/index.ts | 19 +++++++++++ frontend/tsconfig.json | 4 +-- package.json | 2 +- ...troller.cs => InitializeJsonController.cs} | 34 +++++++++---------- .../Frontend/Mappers/HtmlMapperBase.cs | 4 ++- .../Frontend/Mappers/StaticResourceMapper.cs | 2 +- .../Middleware/CacheableSpecification.cs | 2 +- 11 files changed, 77 insertions(+), 55 deletions(-) create mode 100644 frontend/src/bootstrap.tsx delete mode 100644 frontend/src/index.js create mode 100644 frontend/src/index.ts rename src/Radarr.Http/Frontend/{InitializeJsController.cs => InitializeJsonController.cs} (50%) diff --git a/frontend/build/webpack.config.js b/frontend/build/webpack.config.js index 42723cfe5..77ead1d55 100644 --- a/frontend/build/webpack.config.js +++ b/frontend/build/webpack.config.js @@ -36,7 +36,7 @@ module.exports = (env) => { }, entry: { - index: 'index.js' + index: 'index.ts' }, resolve: { @@ -97,7 +97,8 @@ module.exports = (env) => { new HtmlWebpackPlugin({ template: 'frontend/src/index.ejs', filename: 'index.html', - publicPath: '/' + publicPath: '/', + inject: false }), new FileManagerPlugin({ diff --git a/frontend/src/bootstrap.tsx b/frontend/src/bootstrap.tsx new file mode 100644 index 000000000..c07e581b5 --- /dev/null +++ b/frontend/src/bootstrap.tsx @@ -0,0 +1,21 @@ +import { createBrowserHistory } from 'history'; +import React from 'react'; +import { render } from 'react-dom'; +import createAppStore from 'Store/createAppStore'; +import { fetchTranslations } from 'Utilities/String/translate'; +import App from './App/App'; + +export async function bootstrap() { + const history = createBrowserHistory(); + const store = createAppStore(history); + const hasTranslationsError = !(await fetchTranslations()); + + render( + , + document.getElementById('root') + ); +} diff --git a/frontend/src/index.ejs b/frontend/src/index.ejs index 343249f0b..3266db97a 100644 --- a/frontend/src/index.ejs +++ b/frontend/src/index.ejs @@ -48,7 +48,15 @@ /> - + + + + <% for (key in htmlWebpackPlugin.files.js) { %><% } %> + <% for (key in htmlWebpackPlugin.files.css) { %><% } %> Radarr @@ -77,7 +85,4 @@
- - - diff --git a/frontend/src/index.js b/frontend/src/index.js deleted file mode 100644 index acdfc6517..000000000 --- a/frontend/src/index.js +++ /dev/null @@ -1,26 +0,0 @@ -import { createBrowserHistory } from 'history'; -import React from 'react'; -import { render } from 'react-dom'; -import { fetchTranslations } from 'Utilities/String/translate'; - -import './preload'; -import './polyfills'; -import 'Styles/globals.css'; -import './index.css'; - -const history = createBrowserHistory(); -const hasTranslationsError = !await fetchTranslations(); - -const { default: createAppStore } = await import('Store/createAppStore'); -const { default: App } = await import('./App/App'); - -const store = createAppStore(history); - -render( - , - document.getElementById('root') -); diff --git a/frontend/src/index.ts b/frontend/src/index.ts new file mode 100644 index 000000000..d2e70ad3c --- /dev/null +++ b/frontend/src/index.ts @@ -0,0 +1,19 @@ +import './polyfills'; +import 'Styles/globals.css'; +import './index.css'; + +const initializeUrl = `${ + window.Radarr.urlBase +}/initialize.json?t=${Date.now()}`; +const response = await fetch(initializeUrl); + +window.Radarr = await response.json(); + +/* eslint-disable no-undef, @typescript-eslint/ban-ts-comment */ +// @ts-ignore 2304 +__webpack_public_path__ = `${window.Radarr.urlBase}/`; +/* eslint-enable no-undef, @typescript-eslint/ban-ts-comment */ + +const { bootstrap } = await import('./bootstrap'); + +await bootstrap(); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 354e2a5aa..611c872ed 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,11 +1,11 @@ { "compilerOptions": { - "target": "es6", + "target": "esnext", "allowJs": true, "checkJs": false, "baseUrl": "src", "jsx": "react", - "module": "commonjs", + "module": "esnext", "moduleResolution": "node", "allowSyntheticDefaultImports": true, "forceConsistentCasingInFileNames": true, diff --git a/package.json b/package.json index 8417d20a0..b9ce8037d 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "author": "Team Radarr", "license": "GPL-3.0", "readmeFilename": "readme.md", - "main": "index.js", + "main": "index.ts", "browserslist": [ "defaults" ], diff --git a/src/Radarr.Http/Frontend/InitializeJsController.cs b/src/Radarr.Http/Frontend/InitializeJsonController.cs similarity index 50% rename from src/Radarr.Http/Frontend/InitializeJsController.cs rename to src/Radarr.Http/Frontend/InitializeJsonController.cs index d2b409c06..3089fa6ac 100644 --- a/src/Radarr.Http/Frontend/InitializeJsController.cs +++ b/src/Radarr.Http/Frontend/InitializeJsonController.cs @@ -11,7 +11,7 @@ namespace Radarr.Http.Frontend [Authorize(Policy = "UI")] [ApiController] [ApiExplorerSettings(IgnoreApi = true)] - public class InitializeJsController : Controller + public class InitializeJsonController : Controller { private readonly IConfigFileProvider _configFileProvider; private readonly IAnalyticsService _analyticsService; @@ -20,7 +20,7 @@ namespace Radarr.Http.Frontend private static string _urlBase; private string _generatedContent; - public InitializeJsController(IConfigFileProvider configFileProvider, + public InitializeJsonController(IConfigFileProvider configFileProvider, IAnalyticsService analyticsService) { _configFileProvider = configFileProvider; @@ -30,10 +30,10 @@ namespace Radarr.Http.Frontend _urlBase = configFileProvider.UrlBase; } - [HttpGet("/initialize.js")] + [HttpGet("/initialize.json")] public IActionResult Index() { - return Content(GetContent(), "application/javascript"); + return Content(GetContent(), "application/json"); } private string GetContent() @@ -44,19 +44,19 @@ namespace Radarr.Http.Frontend } var builder = new StringBuilder(); - builder.AppendLine("window.Radarr = {"); - builder.AppendLine($" apiRoot: '{_urlBase}/api/v3',"); - builder.AppendLine($" apiKey: '{_apiKey}',"); - builder.AppendLine($" release: '{BuildInfo.Release}',"); - builder.AppendLine($" version: '{BuildInfo.Version.ToString()}',"); - builder.AppendLine($" instanceName: '{_configFileProvider.InstanceName.ToString()}',"); - builder.AppendLine($" theme: '{_configFileProvider.Theme.ToString()}',"); - builder.AppendLine($" branch: '{_configFileProvider.Branch.ToLower()}',"); - builder.AppendLine($" analytics: {_analyticsService.IsEnabled.ToString().ToLowerInvariant()},"); - builder.AppendLine($" userHash: '{HashUtil.AnonymousToken()}',"); - builder.AppendLine($" urlBase: '{_urlBase}',"); - builder.AppendLine($" isProduction: {RuntimeInfo.IsProduction.ToString().ToLowerInvariant()}"); - builder.AppendLine("};"); + builder.AppendLine("{"); + builder.AppendLine($" \"apiRoot\": \"{_urlBase}/api/v3\","); + builder.AppendLine($" \"apiKey\": \"{_apiKey}\","); + builder.AppendLine($" \"release\": \"{BuildInfo.Release}\","); + builder.AppendLine($" \"version\": \"{BuildInfo.Version.ToString()}\","); + builder.AppendLine($" \"instanceName\": \"{_configFileProvider.InstanceName.ToString()}\","); + builder.AppendLine($" \"theme\": \"{_configFileProvider.Theme.ToString()}\","); + builder.AppendLine($" \"branch\": \"{_configFileProvider.Branch.ToLower()}\","); + builder.AppendLine($" \"analytics\": {_analyticsService.IsEnabled.ToString().ToLowerInvariant()},"); + builder.AppendLine($" \"userHash\": \"{HashUtil.AnonymousToken()}\","); + builder.AppendLine($" \"urlBase\": \"{_urlBase}\","); + builder.AppendLine($" \"isProduction\": {RuntimeInfo.IsProduction.ToString().ToLowerInvariant()}"); + builder.AppendLine("}"); _generatedContent = builder.ToString(); diff --git a/src/Radarr.Http/Frontend/Mappers/HtmlMapperBase.cs b/src/Radarr.Http/Frontend/Mappers/HtmlMapperBase.cs index e64bc9cd8..fd37bbbed 100644 --- a/src/Radarr.Http/Frontend/Mappers/HtmlMapperBase.cs +++ b/src/Radarr.Http/Frontend/Mappers/HtmlMapperBase.cs @@ -62,9 +62,11 @@ namespace Radarr.Http.Frontend.Mappers url = cacheBreakProvider.AddCacheBreakerToPath(match.Groups["path"].Value); } - return string.Format("{0}=\"{1}{2}\"", match.Groups["attribute"].Value, UrlBase, url); + return $"{match.Groups["attribute"].Value}=\"{UrlBase}{url}\""; }); + text = text.Replace("__URL_BASE__", UrlBase); + _generatedContent = text; return _generatedContent; diff --git a/src/Radarr.Http/Frontend/Mappers/StaticResourceMapper.cs b/src/Radarr.Http/Frontend/Mappers/StaticResourceMapper.cs index 785543301..b7f9e7fc1 100644 --- a/src/Radarr.Http/Frontend/Mappers/StaticResourceMapper.cs +++ b/src/Radarr.Http/Frontend/Mappers/StaticResourceMapper.cs @@ -37,7 +37,7 @@ namespace Radarr.Http.Frontend.Mappers } return resourceUrl.StartsWith("/content") || - (resourceUrl.EndsWith(".js") && !resourceUrl.EndsWith("initialize.js")) || + resourceUrl.EndsWith(".js") || resourceUrl.EndsWith(".map") || resourceUrl.EndsWith(".css") || (resourceUrl.EndsWith(".ico") && !resourceUrl.Equals("/favicon.ico")) || diff --git a/src/Radarr.Http/Middleware/CacheableSpecification.cs b/src/Radarr.Http/Middleware/CacheableSpecification.cs index aaa629d17..23e9709d8 100644 --- a/src/Radarr.Http/Middleware/CacheableSpecification.cs +++ b/src/Radarr.Http/Middleware/CacheableSpecification.cs @@ -46,7 +46,7 @@ namespace Radarr.Http.Middleware return false; } - if (path.EndsWith("/initialize.js")) + if (path.EndsWith("/initialize.json")) { return false; }