mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-03-04 02:20:19 +01:00
Fix SAML login "something went wrong" when language list = 1 (#5750)
# Description of Changes <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details.
This commit is contained in:
@@ -67,7 +67,7 @@ springBoot {
|
||||
|
||||
allprojects {
|
||||
group = 'stirling.software'
|
||||
version = '2.5.0'
|
||||
version = '2.5.1'
|
||||
|
||||
configurations.configureEach {
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||
"productName": "Stirling-PDF",
|
||||
"version": "2.5.0",
|
||||
"version": "2.5.1",
|
||||
"identifier": "stirling.pdf.dev",
|
||||
"build": {
|
||||
"frontendDist": "../dist",
|
||||
|
||||
@@ -52,12 +52,15 @@ const LanguageItem: React.FC<LanguageItemProps> = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const labelText = option.label;
|
||||
const comingSoonText = t('comingSoon', 'Coming soon');
|
||||
|
||||
const label = disabled ? (
|
||||
<Tooltip content={t('comingSoon', 'Coming soon')} position="left" arrow>
|
||||
<p>{option.label}</p>
|
||||
<Tooltip content={comingSoonText} position="left" arrow>
|
||||
<p>{labelText}</p>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<p>{option.label}</p>
|
||||
<p>{labelText}</p>
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -157,12 +160,27 @@ const LanguageSelector: React.FC<LanguageSelectorProps> = ({
|
||||
compact = false,
|
||||
tooltip
|
||||
}) => {
|
||||
const { i18n } = useTranslation();
|
||||
const { i18n, ready } = useTranslation();
|
||||
const [opened, setOpened] = useState(false);
|
||||
const [animationTriggered, setAnimationTriggered] = useState(false);
|
||||
const [pendingLanguage, setPendingLanguage] = useState<string | null>(null);
|
||||
const [rippleEffect, setRippleEffect] = useState<RippleEffect | null>(null);
|
||||
|
||||
// Trigger animation when dropdown opens
|
||||
useEffect(() => {
|
||||
if (opened) {
|
||||
setAnimationTriggered(false);
|
||||
// Small delay to ensure DOM is ready
|
||||
setTimeout(() => setAnimationTriggered(true), 20);
|
||||
}
|
||||
}, [opened]);
|
||||
|
||||
// Don't render until i18n is ready to prevent race condition
|
||||
// during SAML auth where components render before i18n initializes
|
||||
if (!ready || !i18n.language) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the filtered list of supported languages from i18n
|
||||
// This respects server config (ui.languages) applied by AppConfigLoader
|
||||
const allowedLanguages = (i18n.options.supportedLngs as string[] || [])
|
||||
@@ -176,12 +194,6 @@ const LanguageSelector: React.FC<LanguageSelectorProps> = ({
|
||||
label: name,
|
||||
}));
|
||||
|
||||
// Hide the language selector if there's only one language option
|
||||
// (no point showing a selector when there's nothing to select)
|
||||
if (languageOptions.length <= 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Calculate dropdown width and grid columns based on number of languages
|
||||
// 2-4: 300px/2 cols, 5-9: 400px/3 cols, 10+: 600px/4 cols
|
||||
const dropdownWidth = languageOptions.length <= 4 ? 300
|
||||
@@ -225,16 +237,14 @@ const LanguageSelector: React.FC<LanguageSelectorProps> = ({
|
||||
};
|
||||
|
||||
const currentLanguage = supportedLanguages[i18n.language as keyof typeof supportedLanguages] ||
|
||||
supportedLanguages['en-GB'];
|
||||
supportedLanguages['en-GB'] ||
|
||||
'English'; // Fallback if supportedLanguages lookup fails
|
||||
|
||||
// Trigger animation when dropdown opens
|
||||
useEffect(() => {
|
||||
if (opened) {
|
||||
setAnimationTriggered(false);
|
||||
// Small delay to ensure DOM is ready
|
||||
setTimeout(() => setAnimationTriggered(true), 20);
|
||||
}
|
||||
}, [opened]);
|
||||
// Hide the language selector if there's only one language option
|
||||
// (no point showing a selector when there's nothing to select)
|
||||
if (languageOptions.length <= 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -24,9 +24,8 @@ const ToolChain: React.FC<ToolChainProps> = ({
|
||||
size = 'xs',
|
||||
color = 'var(--mantine-color-blue-7)'
|
||||
}) => {
|
||||
if (!toolChain || toolChain.length === 0) return null;
|
||||
|
||||
const { t } = useTranslation();
|
||||
if (!toolChain || toolChain.length === 0) return null;
|
||||
|
||||
const toolIds = toolChain.map(tool => tool.toolId);
|
||||
|
||||
|
||||
@@ -74,6 +74,11 @@ const CompareDocumentPane = ({
|
||||
}
|
||||
}, [zoom]);
|
||||
|
||||
const renderedPageNumbers = useMemo(
|
||||
() => new Set(pages.map((p) => p.pageNumber)),
|
||||
[pages]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="compare-pane">
|
||||
<div className="compare-header">
|
||||
@@ -88,7 +93,7 @@ const CompareDocumentPane = ({
|
||||
placeholder={dropdownPlaceholder ?? null}
|
||||
className={pane === 'comparison' ? 'compare-changes-select--comparison' : undefined}
|
||||
onNavigate={onNavigateChange}
|
||||
renderedPageNumbers={useMemo(() => new Set(pages.map(p => p.pageNumber)), [pages])}
|
||||
renderedPageNumbers={renderedPageNumbers}
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
|
||||
@@ -43,6 +43,16 @@ interface EditTableOfContentsWorkbenchViewProps {
|
||||
const EditTableOfContentsWorkbenchView = ({ data }: EditTableOfContentsWorkbenchViewProps) => {
|
||||
const { t } = useTranslation();
|
||||
const terminology = useFileActionTerminology();
|
||||
const files = data?.files ?? [];
|
||||
const thumbnails = data?.thumbnails ?? [];
|
||||
const previewFiles = useMemo(
|
||||
() =>
|
||||
files.map((file, index) => ({
|
||||
file,
|
||||
thumbnail: thumbnails[index],
|
||||
})),
|
||||
[files, thumbnails]
|
||||
);
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
@@ -63,8 +73,6 @@ const EditTableOfContentsWorkbenchView = ({ data }: EditTableOfContentsWorkbench
|
||||
bookmarks,
|
||||
selectedFileName,
|
||||
disabled,
|
||||
files,
|
||||
thumbnails,
|
||||
downloadUrl,
|
||||
downloadFilename,
|
||||
errorMessage,
|
||||
@@ -78,15 +86,6 @@ const EditTableOfContentsWorkbenchView = ({ data }: EditTableOfContentsWorkbench
|
||||
onFileClick,
|
||||
} = data;
|
||||
|
||||
const previewFiles = useMemo(
|
||||
() =>
|
||||
files?.map((file, index) => ({
|
||||
file,
|
||||
thumbnail: thumbnails[index],
|
||||
})) ?? [],
|
||||
[files, thumbnails]
|
||||
);
|
||||
|
||||
const showResults = Boolean(
|
||||
previewFiles.length > 0 || downloadUrl || errorMessage
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import FileStatusIndicator from '@app/components/tools/shared/FileStatusIndicator';
|
||||
import { StirlingFile } from '@app/types/fileContext';
|
||||
import i18n from '@app/i18n';
|
||||
|
||||
export interface FilesToolStepProps {
|
||||
selectedFiles: StirlingFile[];
|
||||
@@ -14,9 +14,7 @@ export function createFilesToolStep(
|
||||
createStep: (title: string, props: any, children?: React.ReactNode) => React.ReactElement,
|
||||
props: FilesToolStepProps
|
||||
): React.ReactElement {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return createStep(t("files.title", "Files"), {
|
||||
return createStep(i18n.t("files.title", "Files"), {
|
||||
isVisible: true,
|
||||
isCollapsed: props.isCollapsed,
|
||||
onCollapsedClick: props.onCollapsedClick
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useFileActionIcons } from "@app/hooks/useFileActionIcons";
|
||||
import { saveOperationResults } from "@app/services/operationResultsSaveService";
|
||||
import { useFileActions, useFileState } from "@app/contexts/FileContext";
|
||||
import { FileId } from "@app/types/fileContext";
|
||||
import i18n from "@app/i18n";
|
||||
|
||||
export interface ReviewToolStepProps<TParams = unknown> {
|
||||
isVisible: boolean;
|
||||
@@ -151,10 +152,8 @@ export function createReviewToolStep<TParams = unknown>(
|
||||
) => React.ReactElement,
|
||||
props: ReviewToolStepProps<TParams>
|
||||
): React.ReactElement {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return createStep(
|
||||
t("review", "Review"),
|
||||
i18n.t("review", "Review"),
|
||||
{
|
||||
isVisible: props.isVisible,
|
||||
isCollapsed: props.isCollapsed,
|
||||
|
||||
@@ -80,8 +80,6 @@ const ToolStep = ({
|
||||
alwaysShowTooltip = false,
|
||||
tooltip
|
||||
}: ToolStepProps) => {
|
||||
if (!isVisible) return null;
|
||||
|
||||
const parent = useContext(ToolStepContext);
|
||||
|
||||
// Auto-detect if we should show numbers based on sibling count or force option
|
||||
@@ -91,6 +89,8 @@ const ToolStep = ({
|
||||
return parent ? parent.visibleStepCount >= 3 : false; // Auto-detect
|
||||
}, [showNumber, parent]);
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
const stepNumber = _stepNumber;
|
||||
|
||||
return (
|
||||
|
||||
@@ -38,7 +38,7 @@ const FREE_LICENSE_INFO: LicenseInfo = {
|
||||
|
||||
const BASE_NO_LOGIN_CONFIG: AppConfig = {
|
||||
enableAnalytics: true,
|
||||
appVersion: '2.5.0',
|
||||
appVersion: '2.5.1',
|
||||
serverCertificateEnabled: false,
|
||||
enableAlphaFunctionality: false,
|
||||
serverPort: 8080,
|
||||
|
||||
@@ -48,7 +48,7 @@ const FREE_LICENSE_INFO: LicenseInfo = {
|
||||
|
||||
const BASE_NO_LOGIN_CONFIG: AppConfig = {
|
||||
enableAnalytics: true,
|
||||
appVersion: '2.5.0',
|
||||
appVersion: '2.5.1',
|
||||
serverCertificateEnabled: false,
|
||||
enableAlphaFunctionality: false,
|
||||
enableDesktopInstallSlide: true,
|
||||
|
||||
@@ -74,7 +74,7 @@ services:
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
SECURITY_LOGINMETHOD: "${SECURITY_LOGINMETHOD:-all}"
|
||||
SYSTEM_DEFAULTLOCALE: en-US
|
||||
SYSTEM_DEFAULTLOCALE: "${SYSTEM_DEFAULTLOCALE:-en-US}"
|
||||
SYSTEM_BACKENDURL: "http://localhost:8080"
|
||||
|
||||
# Enterprise License (required for SAML)
|
||||
|
||||
@@ -13,24 +13,48 @@ echo -e "${BLUE}╚════════════════════
|
||||
echo ""
|
||||
|
||||
AUTO_LOGIN=false
|
||||
DEFAULT_LANGUAGE="en-US"
|
||||
COMPOSE_UP_ARGS=(-d --build)
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--auto)
|
||||
AUTO_LOGIN=true
|
||||
shift
|
||||
;;
|
||||
--nobuild)
|
||||
COMPOSE_UP_ARGS=(-d)
|
||||
shift
|
||||
;;
|
||||
--language)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo -e "${RED}Missing value for --language${NC}"
|
||||
exit 1
|
||||
fi
|
||||
DEFAULT_LANGUAGE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--language=*)
|
||||
DEFAULT_LANGUAGE="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
-l)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo -e "${RED}Missing value for -l${NC}"
|
||||
exit 1
|
||||
fi
|
||||
DEFAULT_LANGUAGE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [--auto] [--nobuild]"
|
||||
echo "Usage: $0 [--auto] [--nobuild] [--language <locale>]"
|
||||
echo ""
|
||||
echo " --auto Enable SSO auto-login and force SAML-only login method"
|
||||
echo " --nobuild Skip building images (use existing images)"
|
||||
echo " --language Set system default locale (e.g. de-DE, sv-SE)"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Unknown option: $arg${NC}"
|
||||
echo -e "${RED}Unknown option: $1${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -65,6 +89,10 @@ if [ "$AUTO_LOGIN" = true ]; then
|
||||
echo ""
|
||||
fi
|
||||
|
||||
export SYSTEM_DEFAULTLOCALE="$DEFAULT_LANGUAGE"
|
||||
echo -e "${GREEN}✓ Default locale set to: ${SYSTEM_DEFAULTLOCALE}${NC}"
|
||||
echo ""
|
||||
|
||||
echo -e "${YELLOW}▶ Starting Keycloak (SAML) containers...${NC}"
|
||||
docker-compose -f docker-compose-keycloak-saml.yml up "${COMPOSE_UP_ARGS[@]}" keycloak-saml-db keycloak-saml
|
||||
|
||||
|
||||
Reference in New Issue
Block a user