From a209d56f64e611a4ce21a00901cf42887f083b69 Mon Sep 17 00:00:00 2001 From: lcomplete Date: Sun, 11 Jan 2026 03:03:17 +0800 Subject: [PATCH 1/3] fix: Improve mobile layout and responsiveness in Layout and PrimaryNavigation styles --- app/client/src/components/Layout.css | 17 +++++++++++------ .../components/Navigation/PrimaryNavigation.css | 14 ++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/app/client/src/components/Layout.css b/app/client/src/components/Layout.css index 7e1c721..5c7f1e4 100644 --- a/app/client/src/components/Layout.css +++ b/app/client/src/components/Layout.css @@ -75,7 +75,9 @@ @media (max-width: 900px) { .layoutRoot { flex-direction: column; - min-height: 100vh; + height: 100vh; + height: 100dvh; + overflow: hidden; } .primary-nav-container { @@ -91,16 +93,17 @@ .layout-right { width: 100%; - height: auto; + height: 100%; flex: 1; /* Add padding at bottom for fixed nav */ - padding-bottom: 56px; + padding-bottom: calc(50px + env(safe-area-inset-bottom, 0px)); + overflow: hidden; } .content-area { flex-direction: column; - height: auto; - overflow: visible; + height: 100%; + overflow: hidden; } /* Hide secondary sidebar on mobile - will be shown as popup */ @@ -109,7 +112,9 @@ } .main-content { - overflow: visible; + overflow-y: auto; + height: 100%; + -webkit-overflow-scrolling: touch; } /* Old sidebar compatibility */ diff --git a/app/client/src/components/Navigation/PrimaryNavigation.css b/app/client/src/components/Navigation/PrimaryNavigation.css index 1b143bf..9a761ea 100644 --- a/app/client/src/components/Navigation/PrimaryNavigation.css +++ b/app/client/src/components/Navigation/PrimaryNavigation.css @@ -136,6 +136,7 @@ backdrop-filter: saturate(180%) blur(20px); -webkit-backdrop-filter: saturate(180%) blur(20px); border-top: 1px solid rgba(60, 60, 67, 0.18); + justify-content: space-evenly; } .primary-nav-logo { @@ -143,20 +144,13 @@ } .primary-nav-items { - flex: 1; - flex-direction: row; - align-items: stretch; - justify-content: space-evenly; - gap: 0; - overflow-x: visible; - padding: 0 4px; - height: 100%; + display: contents; } .primary-nav-item { flex: 0 1 auto; margin: 0; - padding: 5px 8px 4px; + padding: 5px 4px 4px; min-width: 44px; border-radius: 0; position: relative; @@ -229,7 +223,7 @@ display: flex; flex: 0 1 auto; margin: 0; - padding: 5px 8px 4px; + padding: 5px 4px 4px; min-width: 44px; flex-direction: column; align-items: center; From 136046fb669c6eba0ac1daae3bc7055b6bcb8e9b Mon Sep 17 00:00:00 2001 From: lcomplete Date: Sun, 11 Jan 2026 03:50:35 +0800 Subject: [PATCH 2/3] Optimize static resource caching with custom configuration Refactored web resource caching to use a custom WebResourceCacheConfig for better control over cache headers. Changed ReactAppController to use forward instead of direct path to ensure proper cache handling. Co-Authored-By: Claude Sonnet 4.5 --- .../server/config/WebResourceCacheConfig.java | 50 +++++++++++++++++++ .../server/controller/ReactAppController.java | 2 +- .../src/main/resources/application.yml | 5 -- 3 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 app/server/huntly-server/src/main/java/com/huntly/server/config/WebResourceCacheConfig.java diff --git a/app/server/huntly-server/src/main/java/com/huntly/server/config/WebResourceCacheConfig.java b/app/server/huntly-server/src/main/java/com/huntly/server/config/WebResourceCacheConfig.java new file mode 100644 index 0000000..3c7d879 --- /dev/null +++ b/app/server/huntly-server/src/main/java/com/huntly/server/config/WebResourceCacheConfig.java @@ -0,0 +1,50 @@ +package com.huntly.server.config; + +import java.util.concurrent.TimeUnit; +import org.springframework.boot.autoconfigure.web.WebProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebResourceCacheConfig implements WebMvcConfigurer { + + private final WebProperties.Resources resources; + + public WebResourceCacheConfig(WebProperties webProperties) { + this.resources = webProperties.getResources(); + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + String[] staticLocations = resources.getStaticLocations(); + CacheControl noCache = CacheControl.noCache(); + CacheControl weekCache = CacheControl.maxAge(7, TimeUnit.DAYS).cachePublic(); + CacheControl dayCache = CacheControl.maxAge(1, TimeUnit.DAYS).cachePublic(); + + registry.addResourceHandler("/index.html") + .addResourceLocations(staticLocations) + .setCacheControl(noCache); + + registry.addResourceHandler("/static/media/**") + .addResourceLocations(staticLocations) + .setCacheControl(dayCache); + + registry.addResourceHandler("/static/js/**", "/static/css/**") + .addResourceLocations(staticLocations) + .setCacheControl(weekCache); + + registry.addResourceHandler( + "/*.png", + "/*.jpg", + "/*.jpeg", + "/*.gif", + "/*.webp", + "/*.svg", + "/*.ico" + ) + .addResourceLocations(staticLocations) + .setCacheControl(dayCache); + } +} diff --git a/app/server/huntly-server/src/main/java/com/huntly/server/controller/ReactAppController.java b/app/server/huntly-server/src/main/java/com/huntly/server/controller/ReactAppController.java index 184f434..3d75796 100644 --- a/app/server/huntly-server/src/main/java/com/huntly/server/controller/ReactAppController.java +++ b/app/server/huntly-server/src/main/java/com/huntly/server/controller/ReactAppController.java @@ -12,6 +12,6 @@ public class ReactAppController { @RequestMapping(value = {"/", "/{x:[\\w\\-]+}", "/{x:^(?!api$).*$}/**/{y:[\\w\\-]+}"}) public String getIndex() { - return "/index.html"; + return "forward:/index.html"; } } diff --git a/app/server/huntly-server/src/main/resources/application.yml b/app/server/huntly-server/src/main/resources/application.yml index 5d7642a..11c56df 100644 --- a/app/server/huntly-server/src/main/resources/application.yml +++ b/app/server/huntly-server/src/main/resources/application.yml @@ -16,11 +16,6 @@ spring: mvc: pathmatch: matching-strategy: ant_path_matcher # use this to make springfox work - web: - resources: - cache: - cachecontrol: - max-age: 7d huntly: jwtSecret: MTI2ZTc1NzAtMjJlMy00MmVlLTkwYmQtOTVjNGM4ZTRhN2YzMTI2ZTc1NzAtMjJlMy00MmVlLTkwYmQtOTVjNGM4ZTRhN2Yz From 4d9e36b5828717fdb6143207249d743f0b1502f7 Mon Sep 17 00:00:00 2001 From: lcomplete Date: Sun, 11 Jan 2026 04:42:55 +0800 Subject: [PATCH 3/3] refactor: Simplify static resource caching configuration and remove WebMvcConfigurer implementation --- .../server/config/WebResourceCacheConfig.java | 88 ++++++++++--------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/app/server/huntly-server/src/main/java/com/huntly/server/config/WebResourceCacheConfig.java b/app/server/huntly-server/src/main/java/com/huntly/server/config/WebResourceCacheConfig.java index 3c7d879..8d0b535 100644 --- a/app/server/huntly-server/src/main/java/com/huntly/server/config/WebResourceCacheConfig.java +++ b/app/server/huntly-server/src/main/java/com/huntly/server/config/WebResourceCacheConfig.java @@ -1,50 +1,58 @@ package com.huntly.server.config; -import java.util.concurrent.TimeUnit; -import org.springframework.boot.autoconfigure.web.WebProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.CacheControl; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -public class WebResourceCacheConfig implements WebMvcConfigurer { +import org.springframework.web.filter.OncePerRequestFilter; - private final WebProperties.Resources resources; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.concurrent.TimeUnit; - public WebResourceCacheConfig(WebProperties webProperties) { - this.resources = webProperties.getResources(); +@Configuration +public class WebResourceCacheConfig { + + @Bean + public FilterRegistrationBean staticResourceCacheFilter() { + FilterRegistrationBean registration = new FilterRegistrationBean<>(); + registration.setFilter(new OncePerRequestFilter() { + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + String path = request.getRequestURI(); + String cacheHeader = getCacheControlHeader(path); + if (cacheHeader != null) { + response.setHeader("Cache-Control", cacheHeader); + } + filterChain.doFilter(request, response); + } + }); + registration.addUrlPatterns("/*"); + registration.setOrder(1); + return registration; } - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - String[] staticLocations = resources.getStaticLocations(); - CacheControl noCache = CacheControl.noCache(); - CacheControl weekCache = CacheControl.maxAge(7, TimeUnit.DAYS).cachePublic(); - CacheControl dayCache = CacheControl.maxAge(1, TimeUnit.DAYS).cachePublic(); - - registry.addResourceHandler("/index.html") - .addResourceLocations(staticLocations) - .setCacheControl(noCache); - - registry.addResourceHandler("/static/media/**") - .addResourceLocations(staticLocations) - .setCacheControl(dayCache); - - registry.addResourceHandler("/static/js/**", "/static/css/**") - .addResourceLocations(staticLocations) - .setCacheControl(weekCache); - - registry.addResourceHandler( - "/*.png", - "/*.jpg", - "/*.jpeg", - "/*.gif", - "/*.webp", - "/*.svg", - "/*.ico" - ) - .addResourceLocations(staticLocations) - .setCacheControl(dayCache); + private String getCacheControlHeader(String path) { + // index.html 不缓存 + if (path.endsWith("/index.html") || path.equals("/")) { + return CacheControl.noCache().getHeaderValue(); + } + // 带 hash 的 js/css 文件长期缓存 + if (path.startsWith("/static/js/") || path.startsWith("/static/css/")) { + return CacheControl.maxAge(7, TimeUnit.DAYS).cachePublic().getHeaderValue(); + } + // 媒体文件缓存1天 + if (path.startsWith("/static/media/")) { + return CacheControl.maxAge(1, TimeUnit.DAYS).cachePublic().getHeaderValue(); + } + // 根目录图片缓存1天 + if (path.matches("^/[^/]+\\.(png|jpg|jpeg|gif|webp|svg|ico)$")) { + return CacheControl.maxAge(1, TimeUnit.DAYS).cachePublic().getHeaderValue(); + } + return null; } }