mirror of https://gitlab.com/m3f_usm/SmartStopAPK
feat: some code reafactor + change page and buses list interval is added
parent
465a220a97
commit
5782b43b3b
|
@ -3,6 +3,7 @@ import useDevices from '../useDevices';
|
||||||
import AuthRepositoryImpl from '../../repositories/AuthRepositoryImpl';
|
import AuthRepositoryImpl from '../../repositories/AuthRepositoryImpl';
|
||||||
import DevicesRepositoryImpl from '../../repositories/DevicesRepositoryImpl';
|
import DevicesRepositoryImpl from '../../repositories/DevicesRepositoryImpl';
|
||||||
|
|
||||||
|
jest.useFakeTimers();
|
||||||
describe('useDevices tests', () => {
|
describe('useDevices tests', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
jest.spyOn(AuthRepositoryImpl.prototype, 'auth').mockResolvedValue('token');
|
jest.spyOn(AuthRepositoryImpl.prototype, 'auth').mockResolvedValue('token');
|
||||||
|
@ -96,7 +97,6 @@ describe('useDevices tests', () => {
|
||||||
const mockDate = new Date('2023-01-01T14:00:00Z');
|
const mockDate = new Date('2023-01-01T14:00:00Z');
|
||||||
const spy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate);
|
const spy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate);
|
||||||
const {result} = renderHook(() => useDevices());
|
const {result} = renderHook(() => useDevices());
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(result.current.state.lines).toMatchObject([
|
expect(result.current.state.lines).toMatchObject([
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,15 +6,10 @@ import AuthRepositoryImpl from '../repositories/AuthRepositoryImpl';
|
||||||
import AuthAPI from '../api/clients/AuthAPI';
|
import AuthAPI from '../api/clients/AuthAPI';
|
||||||
import LineDetail from '../../domain/repositories/LineDetail';
|
import LineDetail from '../../domain/repositories/LineDetail';
|
||||||
|
|
||||||
import {Line} from '../../presentation/screens/BusStopInfoScreen';
|
|
||||||
import BusStopInfoService from '../../domain/services/BusStopInfoService';
|
import BusStopInfoService from '../../domain/services/BusStopInfoService';
|
||||||
import RemoteConfigKeys from '../../utils/RemoteConfigKeys';
|
import RemoteConfigKeys from '../../utils/RemoteConfigKeys';
|
||||||
|
import {Line} from '../../presentation/components/Table';
|
||||||
export enum Status {
|
import Status from '../../utils/Status';
|
||||||
LOADING = 'LOADING',
|
|
||||||
SUCCESS = 'SUCCESS',
|
|
||||||
ERROR = 'ERROR',
|
|
||||||
}
|
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
status: Status;
|
status: Status;
|
||||||
|
@ -40,8 +35,11 @@ const useDevices = () => {
|
||||||
const baseURL = remoteConfig().getString(RemoteConfigKeys.BASE_URL);
|
const baseURL = remoteConfig().getString(RemoteConfigKeys.BASE_URL);
|
||||||
const username = remoteConfig().getString(RemoteConfigKeys.USER);
|
const username = remoteConfig().getString(RemoteConfigKeys.USER);
|
||||||
const password = remoteConfig().getString(RemoteConfigKeys.PASS);
|
const password = remoteConfig().getString(RemoteConfigKeys.PASS);
|
||||||
const updateInterval = remoteConfig().getNumber(
|
const changePageInterval = remoteConfig().getNumber(
|
||||||
RemoteConfigKeys.UPDATE_INTERVAL,
|
RemoteConfigKeys.CHANGE_PAGE_INTERVAL,
|
||||||
|
);
|
||||||
|
const updateBusesListInterval = remoteConfig().getNumber(
|
||||||
|
RemoteConfigKeys.UPDATE_BUSES_LIST_INTERVAL,
|
||||||
);
|
);
|
||||||
|
|
||||||
const deviceApi = useMemo(() => new DevicesAPI(baseURL), [baseURL]);
|
const deviceApi = useMemo(() => new DevicesAPI(baseURL), [baseURL]);
|
||||||
|
@ -182,7 +180,19 @@ const useDevices = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
init();
|
init();
|
||||||
}, [authRepository, devicesRepository, password, username]);
|
|
||||||
|
const interval = setInterval(init, updateBusesListInterval);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
}, [
|
||||||
|
authRepository,
|
||||||
|
devicesRepository,
|
||||||
|
password,
|
||||||
|
username,
|
||||||
|
updateBusesListInterval,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDisplayedLines(state.lines, state.stopMessage, state.stopName);
|
setDisplayedLines(state.lines, state.stopMessage, state.stopName);
|
||||||
|
@ -201,12 +211,12 @@ const useDevices = () => {
|
||||||
: (prevState.currentIndex || 1 + 21) % state.lines.length,
|
: (prevState.currentIndex || 1 + 21) % state.lines.length,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}, 5000);
|
}, changePageInterval);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
};
|
};
|
||||||
}, [state.lines, state.status, updateInterval]);
|
}, [state.lines, state.status, changePageInterval]);
|
||||||
|
|
||||||
return {state};
|
return {state};
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import {useCallback, useEffect, useState} from 'react';
|
||||||
|
import remoteConfig from '@react-native-firebase/remote-config';
|
||||||
|
import Status from '../../utils/Status';
|
||||||
|
|
||||||
|
const useRemoteConfig = () => {
|
||||||
|
const [status, setStatus] = useState(Status.LOADING);
|
||||||
|
|
||||||
|
const getString = useCallback(
|
||||||
|
(key: string) => remoteConfig().getString(key),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const init = async () => {
|
||||||
|
try {
|
||||||
|
await remoteConfig().setConfigSettings({
|
||||||
|
minimumFetchIntervalMillis: 3600000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Add default values for remote config
|
||||||
|
await remoteConfig().setDefaults({
|
||||||
|
BASE_URL: '',
|
||||||
|
HEADER_IMAGE_URL: '',
|
||||||
|
USER: '',
|
||||||
|
PASS: '',
|
||||||
|
CHANGE_PAGE_INTERVAL: 0,
|
||||||
|
UPDATE_BUSES_LIST_INTERVAL: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const isActivated = await remoteConfig().fetchAndActivate();
|
||||||
|
|
||||||
|
if (isActivated) {
|
||||||
|
setStatus(Status.SUCCESS);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setStatus(Status.ERROR);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
init();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {status, getString};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useRemoteConfig;
|
|
@ -0,0 +1,24 @@
|
||||||
|
import {View, ActivityIndicator, StyleProp} from 'react-native';
|
||||||
|
import {ViewStyle} from 'react-native';
|
||||||
|
import Table, {Line} from './Table';
|
||||||
|
import Status from '../../utils/Status';
|
||||||
|
|
||||||
|
interface BusListProps {
|
||||||
|
buses: Line[];
|
||||||
|
status: Status;
|
||||||
|
style?: StyleProp<ViewStyle>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BusList = ({buses, status, style}: BusListProps) => {
|
||||||
|
return (
|
||||||
|
<View style={style}>
|
||||||
|
{status === Status.LOADING ? (
|
||||||
|
<ActivityIndicator testID="LOADING" />
|
||||||
|
) : (
|
||||||
|
<Table data={buses} />
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BusList;
|
|
@ -21,7 +21,7 @@ const Header = ({title, subTitle, style, image}: HeaderProps) => {
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
<Image
|
<Image
|
||||||
source={{
|
source={{
|
||||||
uri: 'https://cdn-icons-png.flaticon.com/512/8371/8371047.png',
|
uri: image,
|
||||||
}}
|
}}
|
||||||
style={styles.icon}
|
style={styles.icon}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
import {View, Text, StyleSheet} from 'react-native';
|
||||||
|
|
||||||
|
export interface Line {
|
||||||
|
lineNumber: string;
|
||||||
|
lineLetter: string;
|
||||||
|
letterColor: string;
|
||||||
|
backgroundColor: string;
|
||||||
|
estimatedArrivalTimeInMinutes: string;
|
||||||
|
lineDescription: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableProps {
|
||||||
|
data: Line[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Table = ({data}: TableProps) => {
|
||||||
|
const rows = 7;
|
||||||
|
const columns = 3;
|
||||||
|
|
||||||
|
const baseTableData: Line[][] = Array.from({length: rows}, () =>
|
||||||
|
new Array(columns).fill({
|
||||||
|
lineNumber: '',
|
||||||
|
lineLetter: '',
|
||||||
|
letterColor: '',
|
||||||
|
backgroundColor: '',
|
||||||
|
estimatedArrivalTimeInMinutes: '',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
data.map((item, index) => {
|
||||||
|
const row = Math.floor(index / columns);
|
||||||
|
const column = index % columns;
|
||||||
|
baseTableData[row][column] = item;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={tableStyles.table}>
|
||||||
|
{baseTableData.map((row, rowIndex) => (
|
||||||
|
<View key={rowIndex} style={tableStyles.row}>
|
||||||
|
{row.map((cell, cellIndex) => {
|
||||||
|
if (cell.lineNumber === '') {
|
||||||
|
return <View style={tableStyles.cell} key={cellIndex} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [first, second] = cell.lineDescription.split('-');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[tableStyles.cell]} key={cellIndex}>
|
||||||
|
<View style={tableStyles.lineInformationContainer}>
|
||||||
|
<Text style={tableStyles.lineNumber}>{cell.lineNumber}</Text>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
tableStyles.letterContainer,
|
||||||
|
{backgroundColor: `#${cell.backgroundColor}` || 'grey'},
|
||||||
|
]}>
|
||||||
|
<Text style={tableStyles.lineLetter}>
|
||||||
|
{cell.lineLetter}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
tableStyles.timeContainer,
|
||||||
|
{backgroundColor: `#${cell.backgroundColor}` || 'grey'},
|
||||||
|
]}>
|
||||||
|
<Text style={tableStyles.time} numberOfLines={1}>
|
||||||
|
{first}
|
||||||
|
</Text>
|
||||||
|
<Text style={tableStyles.time} numberOfLines={1}>
|
||||||
|
{second}
|
||||||
|
</Text>
|
||||||
|
<Text style={tableStyles.time}>
|
||||||
|
{cell.estimatedArrivalTimeInMinutes}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableStyles = StyleSheet.create({
|
||||||
|
table: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: 'white',
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
cell: {
|
||||||
|
flex: 1,
|
||||||
|
borderWidth: 1,
|
||||||
|
flexDirection: 'column',
|
||||||
|
borderColor: 'grey',
|
||||||
|
},
|
||||||
|
lineInformationContainer: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
timeContainer: {
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: 'blue',
|
||||||
|
},
|
||||||
|
lineNumber: {
|
||||||
|
fontSize: 20,
|
||||||
|
marginRight: 8,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: 'grey',
|
||||||
|
},
|
||||||
|
lineLetter: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: 'white',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
letterContainer: {
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 8,
|
||||||
|
borderRadius: 50,
|
||||||
|
},
|
||||||
|
time: {
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
lineDescription: {
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Table;
|
|
@ -0,0 +1,149 @@
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
backgroundColor: 'b10086',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'N',
|
||||||
|
lineNumber: '10',
|
||||||
|
lineDescription: 'Vía Láctea - Los Bloques',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: '0d7215',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'O',
|
||||||
|
lineNumber: '10',
|
||||||
|
lineDescription: 'Vía Láctea - Pobl. Porvenir',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: '0071ca',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'P',
|
||||||
|
lineNumber: '10',
|
||||||
|
lineDescription: 'Vía Láctea - Leonera',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: '4e0963',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'L',
|
||||||
|
lineNumber: '10',
|
||||||
|
lineDescription: 'Vía Láctea - Los Bloques',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: 'ad0101',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'Q',
|
||||||
|
lineNumber: '11',
|
||||||
|
lineDescription: 'Vía Futuro - Los Bloques',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: 'b10086',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'R',
|
||||||
|
lineNumber: '11',
|
||||||
|
lineDescription: 'Vía Futuro - Porvenir',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: 'ce5504',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'S',
|
||||||
|
lineNumber: '13',
|
||||||
|
lineDescription: 'Vía Siglo XXI - Leonera',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: 'ce5504',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'T',
|
||||||
|
lineNumber: '14',
|
||||||
|
lineDescription: 'Chiguayante Sur - Coquimbo',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: 'cc9b00',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'U',
|
||||||
|
lineNumber: '14',
|
||||||
|
lineDescription: 'Chiguayante Sur - Los Altos',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: 'ad0101',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'V',
|
||||||
|
lineNumber: '16',
|
||||||
|
lineDescription: 'Vía Universo - Leonera',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: '0d7215',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'W',
|
||||||
|
lineNumber: '17',
|
||||||
|
lineDescription: 'Expresos Chiguayante - Leonera',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: 'ce5504',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'X',
|
||||||
|
lineNumber: '17',
|
||||||
|
lineDescription: 'Expresos Chiguayante - Leonera',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: '0071ca',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'Y',
|
||||||
|
lineNumber: '18',
|
||||||
|
lineDescription: 'Buses Palomares - Hualqui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: 'b10086',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'M',
|
||||||
|
lineNumber: '20',
|
||||||
|
lineDescription: 'Nueva Llacolén - San Pedro Costa',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: '0071ca',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'A',
|
||||||
|
lineNumber: '20',
|
||||||
|
lineDescription: 'Nueva Llacolén - Boca Sur',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: 'ce5504',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'B',
|
||||||
|
lineNumber: '21',
|
||||||
|
lineDescription: 'Riviera Biobío - Candelaria',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: '0071ca',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'F',
|
||||||
|
lineNumber: '24',
|
||||||
|
lineDescription: 'San Remo - Candelaria',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: '0d7215',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'Q',
|
||||||
|
lineNumber: '30',
|
||||||
|
lineDescription: 'Ruta Las Playas - Higueras',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: '049684',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'R',
|
||||||
|
lineNumber: '30',
|
||||||
|
lineDescription: 'Ruta Las Playas - Higueras',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: 'ce5504',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'Y',
|
||||||
|
lineNumber: '57',
|
||||||
|
lineDescription: 'Denavi Sur - Cosmito',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: 'ce5504',
|
||||||
|
estimatedArrivalTimeInMinutes: 'Más de 10 minutos',
|
||||||
|
lineLetter: 'P',
|
||||||
|
lineNumber: '57',
|
||||||
|
lineDescription: 'Denavi Sur - San Vicente',
|
||||||
|
},
|
||||||
|
];
|
|
@ -0,0 +1,27 @@
|
||||||
|
import {render} from '@testing-library/react-native';
|
||||||
|
import BusList from '../BusList';
|
||||||
|
import Status from '../../../utils/Status';
|
||||||
|
|
||||||
|
describe('BusList tests', () => {
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(BusList).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render correctly with status LOADING', () => {
|
||||||
|
const {toJSON, getByTestId} = render(
|
||||||
|
<BusList buses={[]} status={Status.LOADING} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(toJSON()).toMatchSnapshot();
|
||||||
|
expect(getByTestId('LOADING')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render correctly with status SUCCESS', () => {
|
||||||
|
const {toJSON, queryByTestId} = render(
|
||||||
|
<BusList buses={[]} status={Status.SUCCESS} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(toJSON()).toMatchSnapshot();
|
||||||
|
expect(queryByTestId('LOADING')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
|
@ -7,13 +7,17 @@ describe('Header tests', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render correctly', () => {
|
it('should render correctly', () => {
|
||||||
const {toJSON} = render(<Header title="Title" subTitle="Subtitle" />);
|
const {toJSON} = render(
|
||||||
|
<Header title="Title" subTitle="Subtitle" image="https://urlimage.cl" />,
|
||||||
|
);
|
||||||
|
|
||||||
expect(toJSON()).toMatchSnapshot();
|
expect(toJSON()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render a title and a subtitle', () => {
|
it('should render a title and a subtitle', () => {
|
||||||
const {getByText} = render(<Header title="Title" subTitle="Subtitle" />);
|
const {getByText} = render(
|
||||||
|
<Header title="Title" subTitle="Subtitle" image="https://urlimage.cl" />,
|
||||||
|
);
|
||||||
|
|
||||||
expect(getByText('Title')).toBeDefined();
|
expect(getByText('Title')).toBeDefined();
|
||||||
expect(getByText('Subtitle')).toBeDefined();
|
expect(getByText('Subtitle')).toBeDefined();
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import {render} from '@testing-library/react-native';
|
||||||
|
import Table, {Line} from '../Table';
|
||||||
|
import lineListMock from '../__mocks__/LineList';
|
||||||
|
|
||||||
|
describe('Table tests', () => {
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(Table).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render correctly', () => {
|
||||||
|
const {toJSON} = render(<Table data={lineListMock as Line[]} />);
|
||||||
|
|
||||||
|
expect(toJSON()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render correctly with empty data', () => {
|
||||||
|
const {toJSON} = render(<Table data={[]} />);
|
||||||
|
|
||||||
|
expect(toJSON()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,296 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`BusList tests should render correctly with status LOADING 1`] = `
|
||||||
|
<View>
|
||||||
|
<ActivityIndicator
|
||||||
|
testID="LOADING"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`BusList tests should render correctly with status SUCCESS 1`] = `
|
||||||
|
<View>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"backgroundColor": "white",
|
||||||
|
"flex": 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "row",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "row",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "row",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "row",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "row",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "row",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "row",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"borderColor": "grey",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"flex": 1,
|
||||||
|
"flexDirection": "column",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
`;
|
|
@ -29,7 +29,7 @@ exports[`Header tests should render correctly 1`] = `
|
||||||
<Image
|
<Image
|
||||||
source={
|
source={
|
||||||
{
|
{
|
||||||
"uri": "https://cdn-icons-png.flaticon.com/512/8371/8371047.png",
|
"uri": "https://urlimage.cl",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
style={
|
style={
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,131 +1,25 @@
|
||||||
import {ActivityIndicator, StyleSheet, View} from 'react-native';
|
import {ActivityIndicator, StyleSheet, View} from 'react-native';
|
||||||
import Container from '../components/Container';
|
import Container from '../components/Container';
|
||||||
import Header from '../components/Header';
|
import Header from '../components/Header';
|
||||||
import {Text} from 'react-native';
|
import useDevices from '../../infraestructure/hooks/useDevices';
|
||||||
import useDevices, {Status} from '../../infraestructure/hooks/useDevices';
|
|
||||||
import Banner from '../components/Banner';
|
import Banner from '../components/Banner';
|
||||||
import {useEffect, useState} from 'react';
|
|
||||||
import remoteConfig from '@react-native-firebase/remote-config';
|
|
||||||
import RemoteConfigKeys from '../../utils/RemoteConfigKeys';
|
import RemoteConfigKeys from '../../utils/RemoteConfigKeys';
|
||||||
|
import BusList from '../components/BusList';
|
||||||
|
import useRemoteConfig from '../../infraestructure/hooks/useRemoteConfig';
|
||||||
|
import Status from '../../utils/Status';
|
||||||
|
|
||||||
export interface Line {
|
const BANNER_TEXT = 'Buses que se detienen en esta parada';
|
||||||
lineNumber: string;
|
|
||||||
lineLetter: string;
|
|
||||||
letterColor: string;
|
|
||||||
backgroundColor: string;
|
|
||||||
estimatedArrivalTimeInMinutes: string;
|
|
||||||
lineDescription: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TableProps {
|
|
||||||
data: Line[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const Table = ({data}: TableProps) => {
|
|
||||||
const rows = 7;
|
|
||||||
const columns = 3;
|
|
||||||
|
|
||||||
const baseTableData: Line[][] = Array.from({length: rows}, () =>
|
|
||||||
new Array(columns).fill({
|
|
||||||
lineNumber: '',
|
|
||||||
lineLetter: '',
|
|
||||||
letterColor: '',
|
|
||||||
backgroundColor: '',
|
|
||||||
estimatedArrivalTimeInMinutes: '',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
data.map((item, index) => {
|
|
||||||
const row = Math.floor(index / columns);
|
|
||||||
const column = index % columns;
|
|
||||||
baseTableData[row][column] = item;
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={tableStyles.table}>
|
|
||||||
{baseTableData.map((row, rowIndex) => (
|
|
||||||
<View key={rowIndex} style={tableStyles.row}>
|
|
||||||
{row.map((cell, cellIndex) => {
|
|
||||||
if (cell.lineNumber === '') {
|
|
||||||
return <View style={tableStyles.cell} key={cellIndex} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [first, second] = cell.lineDescription.split('-');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={[tableStyles.cell]} key={cellIndex}>
|
|
||||||
<View style={tableStyles.lineInformationContainer}>
|
|
||||||
<Text style={tableStyles.lineNumber}>{cell.lineNumber}</Text>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
tableStyles.letterContainer,
|
|
||||||
{backgroundColor: `#${cell.backgroundColor}` || 'grey'},
|
|
||||||
]}>
|
|
||||||
<Text style={tableStyles.lineLetter}>
|
|
||||||
{cell.lineLetter}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
tableStyles.timeContainer,
|
|
||||||
{backgroundColor: `#${cell.backgroundColor}` || 'grey'},
|
|
||||||
]}>
|
|
||||||
<Text style={tableStyles.time} numberOfLines={1}>
|
|
||||||
{first}
|
|
||||||
</Text>
|
|
||||||
<Text style={tableStyles.time} numberOfLines={1}>
|
|
||||||
{second}
|
|
||||||
</Text>
|
|
||||||
<Text style={tableStyles.time}>
|
|
||||||
{cell.estimatedArrivalTimeInMinutes}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const BusStopInfoScreen = () => {
|
const BusStopInfoScreen = () => {
|
||||||
const {
|
const {status, displayedLines, stopName} = useDevices().state;
|
||||||
state: {status, displayedLines, stopName},
|
const {status: remoteConfigStatus, getString} = useRemoteConfig();
|
||||||
} = useDevices();
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
const image = remoteConfig().getString(RemoteConfigKeys.HEADER_IMAGE_URL);
|
|
||||||
|
|
||||||
useEffect(() => {
|
const image = getString(RemoteConfigKeys.HEADER_IMAGE_URL);
|
||||||
const init = async () => {
|
const [first, second] = stopName.split('-');
|
||||||
await remoteConfig().setConfigSettings({
|
const title = first ? first.trim() : '';
|
||||||
minimumFetchIntervalMillis: 3600000,
|
const subTitle = second ? second.trim() : '';
|
||||||
});
|
|
||||||
|
|
||||||
await remoteConfig().setDefaults({
|
if (remoteConfigStatus === Status.LOADING) {
|
||||||
BASE_URL: '',
|
|
||||||
HEADER_IMAGE_URL: '',
|
|
||||||
UPDATE_INTERVAL: 0,
|
|
||||||
USER: '',
|
|
||||||
PASS: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const isActivated = await remoteConfig().fetchAndActivate();
|
|
||||||
|
|
||||||
if (isActivated) {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
init();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const splitStopName = stopName.split('-');
|
|
||||||
let title = splitStopName[0] ? `${splitStopName[0].trim()}` : '';
|
|
||||||
const subTitle = splitStopName[1] ? `${splitStopName[1].trim()}` : '';
|
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return (
|
return (
|
||||||
<Container style={styles.container}>
|
<Container style={styles.container}>
|
||||||
<ActivityIndicator />
|
<ActivityIndicator />
|
||||||
|
@ -141,18 +35,12 @@ const BusStopInfoScreen = () => {
|
||||||
title={title}
|
title={title}
|
||||||
image={image}
|
image={image}
|
||||||
/>
|
/>
|
||||||
<Banner
|
<Banner style={styles.bannerContainer} text={BANNER_TEXT} />
|
||||||
style={styles.bannerContainer}
|
<BusList
|
||||||
text="Buses que se detienen en esta parada"
|
buses={displayedLines}
|
||||||
|
style={styles.bodyContainer}
|
||||||
|
status={status}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View style={styles.bodyContainer}>
|
|
||||||
{status === Status.LOADING ? (
|
|
||||||
<ActivityIndicator />
|
|
||||||
) : (
|
|
||||||
<Table data={displayedLines} />
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
<View style={styles.footerContainer}>
|
<View style={styles.footerContainer}>
|
||||||
{/* {TODO implement footer} */}
|
{/* {TODO implement footer} */}
|
||||||
</View>
|
</View>
|
||||||
|
@ -176,67 +64,10 @@ const styles = StyleSheet.create({
|
||||||
flex: 10,
|
flex: 10,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
busContainer: {
|
|
||||||
backgroundColor: 'brown',
|
|
||||||
flexDirection: 'column',
|
|
||||||
},
|
|
||||||
footerContainer: {
|
footerContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: 'grey',
|
backgroundColor: 'grey',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const tableStyles = StyleSheet.create({
|
|
||||||
table: {
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: 'white',
|
|
||||||
},
|
|
||||||
row: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
},
|
|
||||||
cell: {
|
|
||||||
flex: 1,
|
|
||||||
borderWidth: 1,
|
|
||||||
flexDirection: 'column',
|
|
||||||
borderColor: 'grey',
|
|
||||||
},
|
|
||||||
lineInformationContainer: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
timeContainer: {
|
|
||||||
alignItems: 'center',
|
|
||||||
backgroundColor: 'blue',
|
|
||||||
},
|
|
||||||
lineNumber: {
|
|
||||||
fontSize: 20,
|
|
||||||
marginRight: 8,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: 'grey',
|
|
||||||
},
|
|
||||||
lineLetter: {
|
|
||||||
fontSize: 14,
|
|
||||||
color: 'white',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
},
|
|
||||||
letterContainer: {
|
|
||||||
paddingHorizontal: 16,
|
|
||||||
paddingVertical: 8,
|
|
||||||
borderRadius: 50,
|
|
||||||
},
|
|
||||||
time: {
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: 'white',
|
|
||||||
},
|
|
||||||
lineDescription: {
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: 'white',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default BusStopInfoScreen;
|
export default BusStopInfoScreen;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
enum RemoteConfigKeys {
|
enum RemoteConfigKeys {
|
||||||
BASE_URL = 'BASE_URL',
|
BASE_URL = 'BASE_URL',
|
||||||
HEADER_IMAGE_URL = 'HEADER_IMAGE_URL',
|
HEADER_IMAGE_URL = 'HEADER_IMAGE_URL',
|
||||||
UPDATE_INTERVAL = 'UPDATE_INTERVAL',
|
CHANGE_PAGE_INTERVAL = 'CHANGE_PAGE_INTERVAL',
|
||||||
|
UPDATE_BUSES_LIST_INTERVAL = 'UPDATE_BUSES_LIST_INTERVAL',
|
||||||
USER = 'USER',
|
USER = 'USER',
|
||||||
PASS = 'PASSWORD',
|
PASS = 'PASSWORD',
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RemoteConfigKeys;
|
export default RemoteConfigKeys;
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
enum Status {
|
||||||
|
LOADING,
|
||||||
|
SUCCESS,
|
||||||
|
ERROR,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Status;
|
Loading…
Reference in New Issue