Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions __mocks__/react-native-localization.ts

This file was deleted.

4 changes: 2 additions & 2 deletions assets/lang/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ const translations = {
},
Language: {
title: 'Select language',
info: 'Restart the application to see the language change.',
info: 'Language changed successfully.',
},
ChangeProfilePicture: {
title: 'Edit photo',
Expand Down Expand Up @@ -1338,7 +1338,7 @@ const translations = {
},
Language: {
title: 'Selecciona idioma',
info: 'Reinicie la aplicación para ver el cambio de idioma.',
info: 'Idioma cambiado correctamente.',
},
ChangeProfilePicture: {
title: 'Editar foto',
Expand Down
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ function AppContent(): JSX.Element {
try {
logger.info(`--- Starting new app session at ${time.getFormattedDate(new Date(), 'dd/LL/yyyy - HH:mm')} ---`);

await dispatch(appThunks.initializeLanguageThunk()).unwrap();

// 1. Get remote updates
await getRemoteUpdateIfAvailable();

Expand Down
18 changes: 10 additions & 8 deletions src/components/BottomTabNavigator/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,24 @@ import { storageThunks } from 'src/store/slices/storage';
import { useTailwind } from 'tailwind-rn';
import strings from '../../../assets/lang/strings';
import useGetColor from '../../hooks/useColor';
import { useLanguage } from '../../hooks/useLanguage';
import { useAppDispatch } from '../../store/hooks';
import { uiActions } from '../../store/slices/ui';
import globalStyle from '../../styles/global';

const tabs = {
Home: { label: strings.tabs.Home, icon: House },
Drive: { label: strings.tabs.Drive, icon: FolderSimple },
Add: { label: strings.tabs.Add, icon: PlusCircle },
Shared: { label: strings.tabs.Shared, icon: Users },
Settings: { label: strings.tabs.Settings, icon: Gear },
};

function BottomTabNavigator(props: BottomTabBarProps): JSX.Element {
const tailwind = useTailwind();
const getColor = useGetColor();
const dispatch = useAppDispatch();
useLanguage();

const tabs = {
Home: { label: strings.tabs.Home, icon: House },
Drive: { label: strings.tabs.Drive, icon: FolderSimple },
Add: { label: strings.tabs.Add, icon: PlusCircle },
Shared: { label: strings.tabs.Shared, icon: Users },
Settings: { label: strings.tabs.Settings, icon: Gear },
};

const items = props.state.routes
.filter((route) => Object.keys(tabs).includes(route.name))
Expand Down
6 changes: 6 additions & 0 deletions src/hooks/useLanguage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { useAppSelector } from '../store/hooks';

export const useLanguage = () => {
const language = useAppSelector((state) => state.app.language);
return language;
};
2 changes: 2 additions & 0 deletions src/screens/HomeScreen/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import { useTailwind } from 'tailwind-rn';
import { useUseCase } from '@internxt-mobile/hooks/common';
import * as useCases from '@internxt-mobile/useCases/drive';
import { SearchInput } from 'src/components/SearchInput';
import { useLanguage } from '../../hooks/useLanguage';

enum HomeTab {
Recents = 'recents',
}

const HomeScreen = (): JSX.Element => {
const tailwind = useTailwind();
useLanguage();
const [searchText, setSearchText] = useState('');

const {
Expand Down
3 changes: 3 additions & 0 deletions src/screens/SettingsScreen/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import AppVersionWidget from '../../components/AppVersionWidget';
import SettingsGroup from '../../components/SettingsGroup';
import UserProfilePicture from '../../components/UserProfilePicture';
import useGetColor from '../../hooks/useColor';
import { useLanguage } from '../../hooks/useLanguage';
import { useScreenProtection } from '../../hooks/useScreenProtection';
import appService from '../../services/AppService';
import { useAppDispatch, useAppSelector } from '../../store/hooks';
Expand All @@ -50,6 +51,8 @@ function SettingsScreen({ navigation }: SettingsScreenProps<'SettingsHome'>): JS
const isDarkMode = theme === 'dark';

const { isEnabled: isScreenProtectionEnabled, setScreenProtection } = useScreenProtection();
useLanguage();

const showBilling = useAppSelector(paymentsSelectors.shouldShowBilling);
const { user } = useAppSelector((state) => state.auth);
const usagePercent = useAppSelector(storageSelectors.usagePercent);
Expand Down
5 changes: 3 additions & 2 deletions src/screens/SignInScreen/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,20 @@ import AppScreen from '../../components/AppScreen';
import AppVersionWidget from '../../components/AppVersionWidget';
import { useTheme } from '../../contexts/Theme/Theme.context';
import useGetColor from '../../hooks/useColor';
import { useLanguage } from '../../hooks/useLanguage';
import analytics, { AnalyticsEventKey } from '../../services/AnalyticsService';
import appService from '../../services/AppService';
import { logger } from '../../services/common';
import errorService from '../../services/ErrorService';
import notificationsService from '../../services/NotificationsService';
import { NotificationType } from '../../types';
import { RootStackScreenProps } from '../../types/navigation';

function SignInScreen({ navigation }: RootStackScreenProps<'SignIn'>): JSX.Element {
function SignInScreen(): JSX.Element {
const tailwind = useTailwind();
const getColor = useGetColor();
const { theme } = useTheme();
const isDark = theme === 'dark';
useLanguage();

const [error, setError] = useState<string>('');
const dimensions = Dimensions.get('screen');
Expand Down
2 changes: 2 additions & 0 deletions src/screens/drive/DriveFolderScreen/DriveFolderScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ import { NotificationType } from '../../../types';
import { DriveItemStatus, DriveListItem } from '../../../types/drive/item';
import { DriveListType, SortDirection, SortType } from '../../../types/drive/ui';
import { DriveScreenProps, DriveStackParamList } from '../../../types/navigation';
import { useLanguage } from '../../../hooks/useLanguage';
import { DriveFolderEmpty } from './DriveFolderEmpty';
import { DriveFolderError } from './DriveFolderError';
import { DriveFolderScreenHeader } from './DriveFolderScreenHeader';

export function DriveFolderScreen({ navigation }: DriveScreenProps<'DriveFolder'>): JSX.Element {
const route = useRoute<RouteProp<DriveStackParamList, 'DriveFolder'>>();
useLanguage();
const [loadingMore, setLoadingMore] = useState(false);
const { isRootFolder, folderUuid, folderName, parentFolderName, parentUuid } = route.params;

Expand Down
2 changes: 2 additions & 0 deletions src/screens/drive/SharedScreen/SharedScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ import DriveItem from '../../../components/drive/lists/items';
import DriveItemSkinSkeleton from '../../../components/DriveItemSkinSkeleton';
import EmptyList from '../../../components/EmptyList';
import useGetColor from '../../../hooks/useColor';
import { useLanguage } from '../../../hooks/useLanguage';
import { DriveItemStatus } from '../../../types/drive/item';

type SharedItem = SharedFolders & SharedFiles;
export const SharedScreen: React.FC<TabExplorerScreenProps<'Shared'>> = (props) => {
const tailwind = useTailwind();
const getColor = useGetColor();
useLanguage();
const { loading: sharedLoading, executeUseCase: getSharedItems } = useUseCase(driveUseCases.getSharedItems);

const [sharedItemsPage, setSharedItemsPage] = useState(1);
Expand Down
1 change: 1 addition & 0 deletions src/services/AsyncStorageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class AsyncStorageService {
AsyncStorageKey.ScreenLockIsEnabled,
AsyncStorageKey.LastScreenLock,
AsyncStorageKey.ThemePreference,
AsyncStorageKey.Language,
];

await AsyncStorage.multiRemove(nonSensitiveKeys);
Expand Down
14 changes: 1 addition & 13 deletions src/services/LanguageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,13 @@ import { Settings } from 'luxon';
import { AsyncStorageKey, Language, NotificationType } from 'src/types';
import asyncStorageService from './AsyncStorageService';
import notificationsService from './NotificationsService';
class LanguageService {
constructor() {
this.initialize();
}
private async initialize() {
const language = await asyncStorageService.getItem(AsyncStorageKey.Language);

Settings.defaultLocale = language ?? strings.getLanguage();

language && strings.setLanguage(language);
}

class LanguageService {
public async setLanguage(language: Language) {
await asyncStorageService.saveItem(AsyncStorageKey.Language, language);
strings.setLanguage(language);

Settings.defaultLocale = language ?? strings.getLanguage();
notificationsService.show({ text1: strings.modals.Language.info, type: NotificationType.Info });
// TODO: ADD WAY TO RESTART THE LANGUAGE IN RUNTIME WHEN IT CHANGES
}
}

Expand Down
43 changes: 39 additions & 4 deletions src/store/slices/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import drive from '@internxt-mobile/services/drive';
import { BiometricAccessType } from '@internxt-mobile/types/app';
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
import strings from 'assets/lang/strings';
import * as Localization from 'expo-localization';
import languageService from 'src/services/LanguageService';
import notificationsService from 'src/services/NotificationsService';
import { Language, NotificationType } from 'src/types';
Expand All @@ -22,6 +23,7 @@ export interface AppState {
screenLocked: boolean;
lastScreenLock: number | null;
initialScreenLocked: boolean;
language: Language;
}

const initialState: AppState = {
Expand All @@ -32,24 +34,48 @@ const initialState: AppState = {
screenLocked: false,
lastScreenLock: null,
initialScreenLocked: false,
language: Language.English,
};

const initializeLanguageThunk = createAsyncThunk<Language, void, { state: RootState }>(
'app/initializeLanguage',
async () => {
const savedLanguage = await asyncStorageService.getItem('language' as any);

if (savedLanguage) {
strings.setLanguage(savedLanguage);
return savedLanguage as Language;
} else {
const deviceLocale = Localization.getLocales()[0]?.languageCode;
const detectedLanguage = deviceLocale === 'es' ? Language.Spanish : Language.English;
strings.setLanguage(detectedLanguage);
await asyncStorageService.saveItem('language' as any, detectedLanguage);
return detectedLanguage;
}
},
);

const initializeThunk = createAsyncThunk<void, void, { state: RootState }>(
'app/initialize',
async (_, { dispatch }) => {
await drive.start();

await dispatch(initializeLanguageThunk()).unwrap();

dispatch(authThunks.initializeThunk());
dispatch(driveThunks.initializeThunk());
dispatch(paymentsThunks.initializeThunk());
dispatch(storageThunks.initializeThunk());
},
);

const changeLanguageThunk = createAsyncThunk<void, Language, { state: RootState }>(
const changeLanguageThunk = createAsyncThunk<Language, Language, { state: RootState }>(
'app/changeLanguage',
async (language) => {
return languageService.setLanguage(language);
console.log('[changeLanguageThunk] Starting with language:', language);
await languageService.setLanguage(language);
console.log('[changeLanguageThunk] Completed, returning:', language);
return language;
},
);

Expand Down Expand Up @@ -125,16 +151,25 @@ export const appSlice = createSlice({
state.isInitializing = false;
});

builder.addCase(changeLanguageThunk.rejected, () => {
notificationsService.show({ type: NotificationType.Error, text1: strings.errors.changeLanguage });
builder.addCase(initializeLanguageThunk.fulfilled, (state, action) => {
state.language = action.payload;
});

builder
.addCase(changeLanguageThunk.fulfilled, (state, action) => {
state.language = action.payload;
})
.addCase(changeLanguageThunk.rejected, () => {
notificationsService.show({ type: NotificationType.Error, text1: strings.errors.changeLanguage });
});
},
});

export const appActions = appSlice.actions;

export const appThunks = {
initializeThunk,
initializeLanguageThunk,
changeLanguageThunk,
initializeUserPreferencesThunk,
lockScreenIfNeededThunk,
Expand Down
Loading