mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 20:10:25 +00:00
Refactor FeaturedContent logo fetching logic for clarity and efficiency
This update enhances the FeaturedContent component by improving the logo fetching logic. Key changes include clearer variable naming, streamlined ID extraction, and optimized error handling. The logic now better handles logo source preferences and ensures that existing logos are used as fallbacks when necessary. Additionally, the dependency array for the fetch effect has been refined for better performance. Overall, these modifications enhance code readability and maintainability.
This commit is contained in:
parent
86d1492573
commit
bd1d8e30ec
5 changed files with 132 additions and 199 deletions
|
|
@ -122,153 +122,132 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat
|
|||
if (!featuredContent || logoFetchInProgress.current) return;
|
||||
|
||||
const fetchLogo = async () => {
|
||||
// Set fetch in progress flag
|
||||
logoFetchInProgress.current = true;
|
||||
|
||||
try {
|
||||
const contentId = featuredContent.id;
|
||||
const contentData = featuredContent; // Use a clearer variable name
|
||||
const currentLogo = contentData.logo;
|
||||
|
||||
// Get logo source preference from settings
|
||||
const logoPreference = settings.logoSourcePreference || 'metahub'; // Default to metahub if not set
|
||||
const preferredLanguage = settings.tmdbLanguagePreference || 'en'; // Get preferred language
|
||||
// Get preferences
|
||||
const logoPreference = settings.logoSourcePreference || 'metahub';
|
||||
const preferredLanguage = settings.tmdbLanguagePreference || 'en';
|
||||
|
||||
// Check if current logo matches preferences
|
||||
const currentLogo = featuredContent.logo;
|
||||
if (currentLogo) {
|
||||
const isCurrentMetahub = isMetahubUrl(currentLogo);
|
||||
const isCurrentTmdb = isTmdbUrl(currentLogo);
|
||||
|
||||
// If logo already matches preference, use it
|
||||
if ((logoPreference === 'metahub' && isCurrentMetahub) ||
|
||||
(logoPreference === 'tmdb' && isCurrentTmdb)) {
|
||||
setLogoUrl(currentLogo);
|
||||
logoFetchInProgress.current = false;
|
||||
return;
|
||||
}
|
||||
// Reset state for new fetch
|
||||
setLogoUrl(null);
|
||||
setLogoLoadError(false);
|
||||
|
||||
// Extract IDs
|
||||
let imdbId: string | null = null;
|
||||
if (contentData.id.startsWith('tt')) {
|
||||
imdbId = contentData.id;
|
||||
} else if ((contentData as any).imdbId) {
|
||||
imdbId = (contentData as any).imdbId;
|
||||
} else if ((contentData as any).externalIds?.imdb_id) {
|
||||
imdbId = (contentData as any).externalIds.imdb_id;
|
||||
}
|
||||
|
||||
// Extract IMDB ID if available
|
||||
let imdbId = null;
|
||||
if (featuredContent.id.startsWith('tt')) {
|
||||
// If the ID itself is an IMDB ID
|
||||
imdbId = featuredContent.id;
|
||||
} else if ((featuredContent as any).imdbId) {
|
||||
// Try to get IMDB ID from the content object if available
|
||||
imdbId = (featuredContent as any).imdbId;
|
||||
let tmdbId: string | null = null;
|
||||
if (contentData.id.startsWith('tmdb:')) {
|
||||
tmdbId = contentData.id.split(':')[1];
|
||||
} else if ((contentData as any).tmdb_id) {
|
||||
tmdbId = String((contentData as any).tmdb_id);
|
||||
}
|
||||
|
||||
// Extract TMDB ID if available
|
||||
let tmdbId = null;
|
||||
if (contentId.startsWith('tmdb:')) {
|
||||
tmdbId = contentId.split(':')[1];
|
||||
}
|
||||
|
||||
// First source based on preference
|
||||
if (logoPreference === 'metahub' && imdbId) {
|
||||
// Try to get logo from Metahub first
|
||||
const metahubUrl = `https://images.metahub.space/logo/medium/${imdbId}/img`;
|
||||
|
||||
// If we only have IMDB ID, try to find TMDB ID proactively
|
||||
if (imdbId && !tmdbId) {
|
||||
try {
|
||||
const response = await fetch(metahubUrl, { method: 'HEAD' });
|
||||
if (response.ok) {
|
||||
setLogoUrl(metahubUrl);
|
||||
logoFetchInProgress.current = false;
|
||||
return; // Exit if Metahub logo was found
|
||||
const tmdbService = TMDBService.getInstance();
|
||||
const foundData = await tmdbService.findTMDBIdByIMDB(imdbId);
|
||||
if (foundData) {
|
||||
tmdbId = String(foundData);
|
||||
}
|
||||
} catch (error) {
|
||||
// Removed logger.warn
|
||||
} catch (findError) {
|
||||
// logger.warn(`[FeaturedContent] Failed to find TMDB ID for ${imdbId}:`, findError);
|
||||
}
|
||||
|
||||
// Fall back to TMDB if Metahub fails and we have a TMDB ID
|
||||
if (tmdbId) {
|
||||
const tmdbType = featuredContent.type === 'series' ? 'tv' : 'movie';
|
||||
try {
|
||||
const tmdbService = TMDBService.getInstance();
|
||||
const logoUrl = await tmdbService.getContentLogo(tmdbType, tmdbId, preferredLanguage);
|
||||
|
||||
if (logoUrl) {
|
||||
setLogoUrl(logoUrl);
|
||||
} else if (currentLogo) {
|
||||
// If TMDB fails too, use existing logo if any
|
||||
setLogoUrl(currentLogo);
|
||||
}
|
||||
} catch (error) {
|
||||
// Removed logger.error
|
||||
if (currentLogo) setLogoUrl(currentLogo);
|
||||
}
|
||||
} else if (currentLogo) {
|
||||
// Use existing logo if we don't have TMDB ID
|
||||
setLogoUrl(currentLogo);
|
||||
}
|
||||
} else if (logoPreference === 'tmdb') {
|
||||
// Try to get logo from TMDB first
|
||||
if (tmdbId) {
|
||||
const tmdbType = featuredContent.type === 'series' ? 'tv' : 'movie';
|
||||
try {
|
||||
const tmdbService = TMDBService.getInstance();
|
||||
const logoUrl = await tmdbService.getContentLogo(tmdbType, tmdbId, preferredLanguage);
|
||||
|
||||
if (logoUrl) {
|
||||
setLogoUrl(logoUrl);
|
||||
logoFetchInProgress.current = false;
|
||||
return; // Exit if TMDB logo was found
|
||||
}
|
||||
} catch (error) {
|
||||
// Removed logger.error
|
||||
}
|
||||
} else if (imdbId) {
|
||||
// If we have IMDB ID but no TMDB ID, try to find TMDB ID
|
||||
try {
|
||||
const tmdbService = TMDBService.getInstance();
|
||||
const foundTmdbId = await tmdbService.findTMDBIdByIMDB(imdbId);
|
||||
|
||||
if (foundTmdbId) {
|
||||
const tmdbType = featuredContent.type === 'series' ? 'tv' : 'movie';
|
||||
const logoUrl = await tmdbService.getContentLogo(tmdbType, foundTmdbId.toString(), preferredLanguage);
|
||||
|
||||
if (logoUrl) {
|
||||
setLogoUrl(logoUrl);
|
||||
logoFetchInProgress.current = false;
|
||||
return; // Exit if TMDB logo was found
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Removed logger.error
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to Metahub if TMDB fails and we have an IMDB ID
|
||||
}
|
||||
|
||||
const tmdbType = contentData.type === 'series' ? 'tv' : 'movie';
|
||||
let finalLogoUrl: string | null = null;
|
||||
let primaryAttempted = false;
|
||||
let fallbackAttempted = false;
|
||||
|
||||
// --- Logo Fetching Logic ---
|
||||
|
||||
if (logoPreference === 'metahub') {
|
||||
// Primary: Metahub (needs imdbId)
|
||||
if (imdbId) {
|
||||
primaryAttempted = true;
|
||||
const metahubUrl = `https://images.metahub.space/logo/medium/${imdbId}/img`;
|
||||
|
||||
try {
|
||||
const response = await fetch(metahubUrl, { method: 'HEAD' });
|
||||
if (response.ok) {
|
||||
setLogoUrl(metahubUrl);
|
||||
} else if (currentLogo) {
|
||||
// If Metahub fails too, use existing logo if any
|
||||
setLogoUrl(currentLogo);
|
||||
finalLogoUrl = metahubUrl;
|
||||
}
|
||||
} catch (error) {
|
||||
// Removed logger.warn
|
||||
if (currentLogo) setLogoUrl(currentLogo);
|
||||
}
|
||||
} else if (currentLogo) {
|
||||
// Use existing logo if we don't have IMDB ID
|
||||
setLogoUrl(currentLogo);
|
||||
} catch (error) { /* Log if needed */ }
|
||||
}
|
||||
|
||||
// Fallback: TMDB (needs tmdbId)
|
||||
if (!finalLogoUrl && tmdbId) {
|
||||
fallbackAttempted = true;
|
||||
try {
|
||||
const tmdbService = TMDBService.getInstance();
|
||||
const logoUrl = await tmdbService.getContentLogo(tmdbType, tmdbId, preferredLanguage);
|
||||
if (logoUrl) {
|
||||
finalLogoUrl = logoUrl;
|
||||
}
|
||||
} catch (error) { /* Log if needed */ }
|
||||
}
|
||||
|
||||
} else { // logoPreference === 'tmdb'
|
||||
// Primary: TMDB (needs tmdbId)
|
||||
if (tmdbId) {
|
||||
primaryAttempted = true;
|
||||
try {
|
||||
const tmdbService = TMDBService.getInstance();
|
||||
const logoUrl = await tmdbService.getContentLogo(tmdbType, tmdbId, preferredLanguage);
|
||||
if (logoUrl) {
|
||||
finalLogoUrl = logoUrl;
|
||||
}
|
||||
} catch (error) { /* Log if needed */ }
|
||||
}
|
||||
|
||||
// Fallback: Metahub (needs imdbId)
|
||||
if (!finalLogoUrl && imdbId) {
|
||||
fallbackAttempted = true;
|
||||
const metahubUrl = `https://images.metahub.space/logo/medium/${imdbId}/img`;
|
||||
try {
|
||||
const response = await fetch(metahubUrl, { method: 'HEAD' });
|
||||
if (response.ok) {
|
||||
finalLogoUrl = metahubUrl;
|
||||
}
|
||||
} catch (error) { /* Log if needed */ }
|
||||
}
|
||||
}
|
||||
|
||||
// --- Set Final Logo ---
|
||||
if (finalLogoUrl) {
|
||||
setLogoUrl(finalLogoUrl);
|
||||
} else if (currentLogo) {
|
||||
// Use existing logo only if primary and fallback failed or weren't applicable
|
||||
setLogoUrl(currentLogo);
|
||||
} else {
|
||||
// No logo found from any source
|
||||
setLogoLoadError(true);
|
||||
// logger.warn(`[FeaturedContent] No logo found for ${contentData.name} (${contentId}) with preference ${logoPreference}. Primary attempted: ${primaryAttempted}, Fallback attempted: ${fallbackAttempted}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// Removed logger.error
|
||||
// Optionally set a fallback logo or handle the error state
|
||||
setLogoUrl(featuredContent.logo ?? null); // Fallback to initial logo or null
|
||||
// logger.error('[FeaturedContent] Error in fetchLogo:', error);
|
||||
setLogoLoadError(true);
|
||||
} finally {
|
||||
logoFetchInProgress.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Trigger fetch when content changes
|
||||
fetchLogo();
|
||||
}, [featuredContent?.id, settings.logoSourcePreference, settings.tmdbLanguagePreference]);
|
||||
}, [featuredContent, settings.logoSourcePreference, settings.tmdbLanguagePreference]);
|
||||
|
||||
// Load poster and logo
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -286,9 +286,10 @@ const HeroSection: React.FC<HeroSectionProps> = ({
|
|||
}));
|
||||
|
||||
const parallaxImageStyle = useAnimatedStyle(() => ({
|
||||
width: '100%',
|
||||
height: '120%',
|
||||
width: '120%',
|
||||
height: '100%',
|
||||
top: '-10%',
|
||||
left: '-10%',
|
||||
transform: [
|
||||
{
|
||||
translateY: interpolate(
|
||||
|
|
@ -302,7 +303,7 @@ const HeroSection: React.FC<HeroSectionProps> = ({
|
|||
scale: interpolate(
|
||||
dampedScrollY.value,
|
||||
[0, 150, 300],
|
||||
[1.1, 1.02, 0.95],
|
||||
[1.05, 1.02, 0.99],
|
||||
Extrapolate.CLAMP
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,7 +196,15 @@ export const useMetadataAssets = (
|
|||
else if (shouldFetchLogo && logoFetchInProgress.current) {
|
||||
logger.log(`[useMetadataAssets:Logo] Skipping logo fetch because logoFetchInProgress is true.`);
|
||||
}
|
||||
}, [id, type, metadata, setMetadata, imdbId, settings.logoSourcePreference, settings.tmdbLanguagePreference]); // Added tmdbLanguagePreference dependency
|
||||
}, [
|
||||
id,
|
||||
type,
|
||||
imdbId,
|
||||
metadata?.logo, // Depend on the logo value itself, not the whole object
|
||||
settings.logoSourcePreference,
|
||||
settings.tmdbLanguagePreference,
|
||||
setMetadata // Keep setMetadata, but ensure it's memoized in parent
|
||||
]);
|
||||
|
||||
// Fetch banner image based on logo source preference - optimized version
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -686,7 +686,8 @@ const AppNavigator = () => {
|
|||
/>
|
||||
<Stack.Screen
|
||||
name="Metadata"
|
||||
component={MetadataScreen as any}
|
||||
component={MetadataScreen}
|
||||
options={{ headerShown: false, animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Streams"
|
||||
|
|
@ -700,22 +701,27 @@ const AppNavigator = () => {
|
|||
<Stack.Screen
|
||||
name="Player"
|
||||
component={VideoPlayer as any}
|
||||
options={{ animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Catalog"
|
||||
component={CatalogScreen as any}
|
||||
options={{ animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Addons"
|
||||
component={AddonsScreen as any}
|
||||
options={{ animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Search"
|
||||
component={SearchScreen as any}
|
||||
options={{ animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="CatalogSettings"
|
||||
component={CatalogSettingsScreen as any}
|
||||
options={{ animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="HomeScreenSettings"
|
||||
|
|
@ -765,10 +771,12 @@ const AppNavigator = () => {
|
|||
<Stack.Screen
|
||||
name="Calendar"
|
||||
component={CalendarScreen as any}
|
||||
options={{ animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="NotificationSettings"
|
||||
component={NotificationSettingsScreen as any}
|
||||
options={{ animation: Platform.OS === 'ios' ? 'slide_from_right' : 'default' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="MDBListSettings"
|
||||
|
|
|
|||
|
|
@ -348,48 +348,11 @@ const LogoSourceSettings = () => {
|
|||
settings.logoSourcePreference || 'metahub'
|
||||
);
|
||||
|
||||
// TMDB Language Preference
|
||||
const [selectedTmdbLanguage, setSelectedTmdbLanguage] = useState<string>(
|
||||
settings.tmdbLanguagePreference || 'en'
|
||||
);
|
||||
|
||||
// Make sure logoSource stays in sync with settings
|
||||
useEffect(() => {
|
||||
setLogoSource(settings.logoSourcePreference || 'metahub');
|
||||
}, [settings.logoSourcePreference]);
|
||||
|
||||
// Keep selectedTmdbLanguage in sync with settings
|
||||
useEffect(() => {
|
||||
setSelectedTmdbLanguage(settings.tmdbLanguagePreference || 'en');
|
||||
}, [settings.tmdbLanguagePreference]);
|
||||
|
||||
// Force reload settings from AsyncStorage when component mounts
|
||||
useEffect(() => {
|
||||
const loadSettingsFromStorage = async () => {
|
||||
try {
|
||||
const settingsJson = await AsyncStorage.getItem('app_settings');
|
||||
if (settingsJson) {
|
||||
const storedSettings = JSON.parse(settingsJson);
|
||||
|
||||
// Update local state to match stored settings
|
||||
if (storedSettings.logoSourcePreference) {
|
||||
setLogoSource(storedSettings.logoSourcePreference);
|
||||
}
|
||||
|
||||
if (storedSettings.tmdbLanguagePreference) {
|
||||
setSelectedTmdbLanguage(storedSettings.tmdbLanguagePreference);
|
||||
}
|
||||
|
||||
logger.log('[LogoSourceSettings] Successfully loaded settings from AsyncStorage');
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('[LogoSourceSettings] Error loading settings from AsyncStorage:', error);
|
||||
}
|
||||
};
|
||||
|
||||
loadSettingsFromStorage();
|
||||
}, []);
|
||||
|
||||
// Selected example show
|
||||
const [selectedShow, setSelectedShow] = useState(EXAMPLE_SHOWS[0]);
|
||||
|
||||
|
|
@ -429,6 +392,9 @@ const LogoSourceSettings = () => {
|
|||
|
||||
logger.log(`[LogoSourceSettings] Fetching ${show.name} with TMDB ID: ${tmdbId}, IMDB ID: ${imdbId}`);
|
||||
|
||||
// Get preferred language directly from settings
|
||||
const preferredTmdbLanguage = settings.tmdbLanguagePreference || 'en';
|
||||
|
||||
// Get TMDB logo and banner
|
||||
try {
|
||||
const apiKey = TMDB_API_KEY;
|
||||
|
|
@ -451,15 +417,15 @@ const LogoSourceSettings = () => {
|
|||
|
||||
// Find initial logo (prefer selectedTmdbLanguage, then 'en')
|
||||
let initialLogoPath: string | null = null;
|
||||
let initialLanguage = selectedTmdbLanguage;
|
||||
let initialLanguage = preferredTmdbLanguage;
|
||||
|
||||
// First try to find a logo in the user's preferred language
|
||||
const preferredLogo = imagesData.logos.find((logo: { iso_639_1: string; file_path: string }) => logo.iso_639_1 === selectedTmdbLanguage);
|
||||
const preferredLogo = imagesData.logos.find((logo: { iso_639_1: string; file_path: string }) => logo.iso_639_1 === preferredTmdbLanguage);
|
||||
|
||||
if (preferredLogo) {
|
||||
initialLogoPath = preferredLogo.file_path;
|
||||
initialLanguage = selectedTmdbLanguage;
|
||||
logger.log(`[LogoSourceSettings] Found initial ${selectedTmdbLanguage} TMDB logo for ${show.name}`);
|
||||
initialLanguage = preferredTmdbLanguage;
|
||||
logger.log(`[LogoSourceSettings] Found initial ${preferredTmdbLanguage} TMDB logo for ${show.name}`);
|
||||
} else {
|
||||
// Fallback to English logo
|
||||
const englishLogo = imagesData.logos.find((logo: { iso_639_1: string; file_path: string }) => logo.iso_639_1 === 'en');
|
||||
|
|
@ -478,7 +444,6 @@ const LogoSourceSettings = () => {
|
|||
|
||||
if (initialLogoPath) {
|
||||
setTmdbLogo(`https://image.tmdb.org/t/p/original${initialLogoPath}`);
|
||||
setSelectedTmdbLanguage(initialLanguage); // Set selected language based on found logo
|
||||
} else {
|
||||
logger.warn(`[LogoSourceSettings] No valid initial TMDB logo found for ${show.name}`);
|
||||
}
|
||||
|
|
@ -588,9 +553,6 @@ const LogoSourceSettings = () => {
|
|||
|
||||
// Handle TMDB language selection
|
||||
const handleTmdbLanguageSelect = (languageCode: string) => {
|
||||
// First set local state for immediate UI updates
|
||||
setSelectedTmdbLanguage(languageCode);
|
||||
|
||||
// Update the preview logo if possible
|
||||
if (tmdbLogosData) {
|
||||
const selectedLogoData = tmdbLogosData.find(logo => logo.iso_639_1 === languageCode);
|
||||
|
|
@ -606,6 +568,9 @@ const LogoSourceSettings = () => {
|
|||
saveLanguagePreference(languageCode);
|
||||
};
|
||||
|
||||
// Get preferred language directly from settings for UI rendering
|
||||
const preferredTmdbLanguage = settings.tmdbLanguagePreference || 'en';
|
||||
|
||||
// Save language preference with proper persistence
|
||||
const saveLanguagePreference = async (languageCode: string) => {
|
||||
logger.log(`[LogoSourceSettings] Saving TMDB language preference: ${languageCode}`);
|
||||
|
|
@ -614,34 +579,6 @@ const LogoSourceSettings = () => {
|
|||
// First use the settings hook to update the setting - this is crucial
|
||||
updateSetting('tmdbLanguagePreference', languageCode);
|
||||
|
||||
// For extra assurance, also save directly to AsyncStorage
|
||||
// Get current settings from AsyncStorage
|
||||
const settingsJson = await AsyncStorage.getItem('app_settings');
|
||||
|
||||
if (settingsJson) {
|
||||
const currentSettings = JSON.parse(settingsJson);
|
||||
|
||||
// Update the language preference
|
||||
const updatedSettings = {
|
||||
...currentSettings,
|
||||
tmdbLanguagePreference: languageCode
|
||||
};
|
||||
|
||||
// Save back to AsyncStorage using await to ensure it completes
|
||||
await AsyncStorage.setItem('app_settings', JSON.stringify(updatedSettings));
|
||||
logger.log(`[LogoSourceSettings] Successfully saved TMDB language preference '${languageCode}' to AsyncStorage`);
|
||||
} else {
|
||||
// If no settings exist yet, create new settings object with this preference
|
||||
const newSettings = {
|
||||
...DEFAULT_SETTINGS,
|
||||
tmdbLanguagePreference: languageCode
|
||||
};
|
||||
|
||||
// Save to AsyncStorage
|
||||
await AsyncStorage.setItem('app_settings', JSON.stringify(newSettings));
|
||||
logger.log(`[LogoSourceSettings] Created new settings with TMDB language preference '${languageCode}'`);
|
||||
}
|
||||
|
||||
// Clear any cached logo data
|
||||
await AsyncStorage.removeItem('_last_logos_');
|
||||
|
||||
|
|
@ -875,7 +812,7 @@ const LogoSourceSettings = () => {
|
|||
key={langCode} // Use the unique code as key
|
||||
style={[
|
||||
styles.languageItem,
|
||||
selectedTmdbLanguage === langCode && styles.selectedLanguageItem
|
||||
preferredTmdbLanguage === langCode && styles.selectedLanguageItem
|
||||
]}
|
||||
onPress={() => handleTmdbLanguageSelect(langCode)}
|
||||
activeOpacity={0.7}
|
||||
|
|
@ -884,7 +821,7 @@ const LogoSourceSettings = () => {
|
|||
<Text
|
||||
style={[
|
||||
styles.languageItemText,
|
||||
selectedTmdbLanguage === langCode && styles.selectedLanguageItemText
|
||||
preferredTmdbLanguage === langCode && styles.selectedLanguageItemText
|
||||
]}
|
||||
>
|
||||
{(langCode || '').toUpperCase() || '??'}
|
||||
|
|
|
|||
Loading…
Reference in a new issue