remove unused configs, add others

This commit is contained in:
Anthony Stirling 2025-10-19 22:28:55 +01:00
parent d70ec668f1
commit 82cf8cfde4
13 changed files with 101 additions and 38 deletions

View File

@ -440,21 +440,9 @@ public class ApplicationProperties {
@Data
public static class Ui {
private String appName;
private String homeDescription;
private String appNameNavbar;
private List<String> languages;
public String getAppName() {
return appName != null && appName.trim().length() > 0 ? appName : null;
}
public String getHomeDescription() {
return homeDescription != null && homeDescription.trim().length() > 0
? homeDescription
: null;
}
public String getAppNameNavbar() {
return appNameNavbar != null && appNameNavbar.trim().length() > 0
? appNameNavbar

View File

@ -72,14 +72,6 @@ public class SettingsController {
// Update UI settings
if (settings.containsKey("ui")) {
Map<String, String> ui = (Map<String, String>) settings.get("ui");
if (ui.containsKey("appName")) {
GeneralUtils.saveKeyToSettings("ui.appName", ui.get("appName"));
applicationProperties.getUi().setAppName(ui.get("appName"));
}
if (ui.containsKey("homeDescription")) {
GeneralUtils.saveKeyToSettings("ui.homeDescription", ui.get("homeDescription"));
applicationProperties.getUi().setHomeDescription(ui.get("homeDescription"));
}
if (ui.containsKey("appNameNavbar")) {
GeneralUtils.saveKeyToSettings("ui.appNameNavbar", ui.get("appNameNavbar"));
applicationProperties.getUi().setAppNameNavbar(ui.get("appNameNavbar"));

View File

@ -51,9 +51,7 @@ public class ConfigController {
configData.put("serverPort", appConfig.getServerPort());
// Extract values from ApplicationProperties
configData.put("appName", applicationProperties.getUi().getAppName());
configData.put("appNameNavbar", applicationProperties.getUi().getAppNameNavbar());
configData.put("homeDescription", applicationProperties.getUi().getHomeDescription());
configData.put("languages", applicationProperties.getUi().getLanguages());
// Security settings

View File

@ -153,8 +153,6 @@ system:
cleanupSystemTemp: false # Whether to clean broader system temp directory
ui:
appName: '' # application's visible name
homeDescription: '' # short description or tagline shown on the homepage
appNameNavbar: '' # name displayed on the navigation bar
languages: [] # If empty, all languages are enabled. To display only German and Polish ["de_DE", "pl_PL"]. British English is always enabled.

View File

@ -9,6 +9,7 @@ import { SidebarProvider } from "./contexts/SidebarContext";
import { PreferencesProvider } from "./contexts/PreferencesContext";
import ErrorBoundary from "./components/shared/ErrorBoundary";
import HomePage from "./pages/HomePage";
import AppConfigLoader from "./components/shared/AppConfigLoader";
// Import global styles
import "./styles/tailwind.css";
@ -43,6 +44,7 @@ export default function App() {
<PreferencesProvider>
<RainbowThemeProvider>
<ErrorBoundary>
<AppConfigLoader />
<FileContextProvider enableUrlSync={true} enablePersistence={true}>
<NavigationProvider>
<FilesModalProvider>

View File

@ -6,6 +6,7 @@ import { useFileHandler } from '../../hooks/useFileHandler';
import { useFileState } from '../../contexts/FileContext';
import { useNavigationState, useNavigationActions } from '../../contexts/NavigationContext';
import { useViewer } from '../../contexts/ViewerContext';
import { useAppConfig } from '../../hooks/useAppConfig';
import './Workbench.css';
import TopControls from '../shared/TopControls';
@ -20,6 +21,7 @@ import DismissAllErrorsButton from '../shared/DismissAllErrorsButton';
// No props needed - component uses contexts directly
export default function Workbench() {
const { isRainbowMode } = useRainbowThemeContext();
const { config } = useAppConfig();
// Use context-based hooks to eliminate all prop drilling
const { selectors } = useFileState();
@ -180,7 +182,14 @@ export default function Workbench() {
{renderMainContent()}
</Box>
<Footer analyticsEnabled />
<Footer
analyticsEnabled={config?.enableAnalytics}
termsAndConditions={config?.termsAndConditions}
privacyPolicy={config?.privacyPolicy}
cookiePolicy={config?.cookiePolicy}
impressum={config?.impressum}
accessibilityStatement={config?.accessibilityStatement}
/>
</Box>
);
}

View File

@ -0,0 +1,24 @@
import { useEffect } from 'react';
import { useAppConfig } from '../../hooks/useAppConfig';
import { updateSupportedLanguages } from '../../i18n';
/**
* Component that loads app configuration and applies it to the application.
* This includes:
* - Filtering available languages based on config.languages
*
* Place this component high in the component tree, after i18n has initialized.
*/
export default function AppConfigLoader() {
const { config, loading } = useAppConfig();
useEffect(() => {
if (!loading && config) {
// Update supported languages if config specifies a language filter
updateSupportedLanguages(config.languages);
}
}, [config, loading]);
// This component doesn't render anything
return null;
}

View File

@ -12,14 +12,19 @@ interface FooterProps {
}
export default function Footer({
privacyPolicy = '/privacy',
termsAndConditions = '/terms',
accessibilityStatement = 'accessibility',
privacyPolicy,
termsAndConditions,
accessibilityStatement,
cookiePolicy,
impressum,
analyticsEnabled = false
}: FooterProps) {
const { t } = useTranslation();
const { showCookiePreferences } = useCookieConsent({ analyticsEnabled });
// Helper to check if a value is valid (not null/undefined/empty string)
const isValidLink = (link?: string) => link && link.trim().length > 0;
return (
<div style={{
height: 'var(--footer-height)',
@ -43,7 +48,7 @@ export default function Footer({
>
{t('survey.nav', 'Survey')}
</a>
{privacyPolicy && (
{isValidLink(privacyPolicy) && (
<a
className="footer-link px-3"
target="_blank"
@ -53,7 +58,7 @@ export default function Footer({
{t('legal.privacy', 'Privacy Policy')}
</a>
)}
{termsAndConditions && (
{isValidLink(termsAndConditions) && (
<a
className="footer-link px-3"
target="_blank"
@ -63,7 +68,7 @@ export default function Footer({
{t('legal.terms', 'Terms and Conditions')}
</a>
)}
{accessibilityStatement && (
{isValidLink(accessibilityStatement) && (
<a
className="footer-link px-3"
target="_blank"
@ -73,6 +78,26 @@ export default function Footer({
{t('legal.accessibility', 'Accessibility')}
</a>
)}
{isValidLink(cookiePolicy) && (
<a
className="footer-link px-3"
target="_blank"
rel="noopener noreferrer"
href={cookiePolicy}
>
{t('legal.cookiePolicy', 'Cookie Policy')}
</a>
)}
{isValidLink(impressum) && (
<a
className="footer-link px-3"
target="_blank"
rel="noopener noreferrer"
href={impressum}
>
{t('legal.impressum', 'Impressum')}
</a>
)}
{analyticsEnabled && (
<button
className="footer-link px-3"

View File

@ -7,8 +7,6 @@ import { useRestartServer } from '../useRestartServer';
interface GeneralSettingsData {
ui: {
appName?: string;
homeDescription?: string;
appNameNavbar?: string;
languages?: string[];
};

View File

@ -34,7 +34,6 @@ const Overview: React.FC = () => {
};
const basicConfig = config ? {
appName: config.appName,
appNameNavbar: config.appNameNavbar,
baseUrl: config.baseUrl,
contextPath: config.contextPath,

View File

@ -4,9 +4,7 @@ export interface AppConfig {
baseUrl?: string;
contextPath?: string;
serverPort?: number;
appName?: string;
appNameNavbar?: string;
homeDescription?: string;
languages?: string[];
enableLogin?: boolean;
enableAlphaFunctionality?: boolean;

View File

@ -104,4 +104,34 @@ i18n.on('languageChanged', (lng) => {
document.documentElement.lang = lng;
});
/**
* Updates the supported languages list dynamically based on config
* If configLanguages is null/empty, all languages remain available
* Otherwise, only specified languages plus 'en-GB' fallback are enabled
*/
export function updateSupportedLanguages(configLanguages?: string[] | null) {
if (!configLanguages || configLanguages.length === 0) {
// No filter specified - keep all languages
return;
}
// Ensure fallback language is always included
const languagesToSupport = new Set(['en-GB', ...configLanguages]);
// Filter to only valid language codes that exist in our translations
const validLanguages = Array.from(languagesToSupport).filter(
lang => lang in supportedLanguages
);
if (validLanguages.length > 0) {
i18n.options.supportedLngs = validLanguages;
// If current language is not in the new supported list, switch to fallback
const currentLang = i18n.language;
if (currentLang && !validLanguages.includes(currentLang)) {
i18n.changeLanguage('en-GB');
}
}
}
export default i18n;

View File

@ -42,6 +42,7 @@ export default function HomePage() {
const { openFilesModal } = useFilesModalContext();
const { colorScheme } = useMantineColorScheme();
const { config } = useAppConfig();
const isMobile = useMediaQuery("(max-width: 1024px)");
const sliderRef = useRef<HTMLDivElement | null>(null);
const [activeMobileView, setActiveMobileView] = useState<MobileView>("tools");
@ -138,10 +139,11 @@ export default function HomePage() {
const baseUrl = getBaseUrl();
// Update document meta when tool changes
const appName = config?.appNameNavbar || 'Stirling PDF';
useDocumentMeta({
title: selectedTool ? `${selectedTool.name} - Stirling PDF` : 'Stirling PDF',
title: selectedTool ? `${selectedTool.name} - ${appName}` : appName,
description: selectedTool?.description || t('app.description', 'The Free Adobe Acrobat alternative (10M+ Downloads)'),
ogTitle: selectedTool ? `${selectedTool.name} - Stirling PDF` : 'Stirling PDF',
ogTitle: selectedTool ? `${selectedTool.name} - ${appName}` : appName,
ogDescription: selectedTool?.description || t('app.description', 'The Free Adobe Acrobat alternative (10M+ Downloads)'),
ogImage: selectedToolKey ? `${baseUrl}/og_images/${selectedToolKey}.png` : `${baseUrl}/og_images/home.png`,
ogUrl: selectedTool ? `${baseUrl}${window.location.pathname}` : baseUrl