Skip to content

Commit

Permalink
Parse client version-string
Browse files Browse the repository at this point in the history
  • Loading branch information
rottabonus committed Oct 6, 2024
1 parent 5740430 commit 52b20b7
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 71 deletions.
20 changes: 17 additions & 3 deletions src/Screens/Main/Tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import React from 'react';

import * as ReactRedux from 'react-redux';
import * as redux from 'redux';
import { selectFirstQuestion } from '../../state/reducers/questions';

import { selectClientVersion } from '../../state/reducers/minimumVersion';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import * as actions from '../../state/actions';

import { StackScreenProps } from '@react-navigation/stack';
import { CommonActions } from '@react-navigation/native';
import { StackRoutes } from '..';
import { useRefetch } from 'src/lib/use-refetch';
import { useRefetch } from '../../lib/use-refetch';
import { getClient } from '../../lib/isDevice';

import BuddyList, { BuddyListRoute } from './BuddyList';
import MentorList, { MentorListRoute } from './MentorList';
Expand All @@ -30,10 +35,13 @@ const Tab = createBottomTabNavigator<TabRoutes>();
type Props = {} & StackScreenProps<StackRoutes, 'Main/Tabs'>;

const Main = ({ navigation, route }: Props) => {
const dispatch = ReactRedux.useDispatch();

const dispatch = ReactRedux.useDispatch<redux.Dispatch<actions.Action>>();
const initialRouteName = route.params?.initial;

const clientVersion = ReactRedux.useSelector(
selectClientVersion(getClient()),
);

const handleRefetchData = () => {
dispatch({ type: 'feedback/getQuestions/start', payload: undefined });
dispatch({ type: 'mentors/start', payload: undefined });
Expand Down Expand Up @@ -65,6 +73,12 @@ const Main = ({ navigation, route }: Props) => {
});
}, []);

React.useEffect(() => {
dispatch({ type: 'minimumVersion/get/start', payload: undefined });
}, []);

console.log('got minimum client version from API', clientVersion);

return (
<>
<Tab.Navigator
Expand Down
46 changes: 0 additions & 46 deletions src/Screens/VersionCheck/index.tsx

This file was deleted.

4 changes: 1 addition & 3 deletions src/Screens/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@ import DeleteAccount, {
import PasswordChange, { PasswordChangeRoute } from './Main/Settings/Password';
import EmailChange, { EmailChangeRoute } from './Main/Settings/Email/Email';
import UserReport, { UserReportRoute } from './Main/UserReport';
import { VersionCheckRoute } from './VersionCheck';

export type StackRoutes = SplashRoute &
VersionCheckRoute &
WelcomeRoute &
MentorListRoute &
SignRoute &
Expand All @@ -56,7 +54,7 @@ const Stack = reactNavigationStack.createStackNavigator<StackRoutes>();
export default () => (
<NavigationContainer>
<Stack.Navigator
initialRouteName="VersionCheck"
initialRouteName="Splash"
screenOptions={{ headerShown: false }}
>
<Stack.Screen name="Splash" component={Splash} />
Expand Down
40 changes: 33 additions & 7 deletions src/api/minimumVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,47 @@ import * as TE from 'fp-ts/lib/TaskEither';
import * as http from '../lib/http';

import * as config from './config';
import * as authApi from './auth';
import { ValidVersion } from '../lib/validators';

const clientVersions = t.strict({
ylitse_ios: t.string,
ylitse_android: t.string,
const client = t.strict({
client: t.string,
version: ValidVersion,
});

type Client = t.TypeOf<typeof client>;

const clientVersions = t.array(client);

const versionsResponse = t.strict({
resources: clientVersions,
});

export type ClientVersions = t.TypeOf<typeof clientVersions>;
export type Version = {
client: string;
major: number;
minor: number;
patch: number;
};

const toAppVersion = (value: Client): Version => {
const splitted = value.version.split('.').map(Number);

return {
client: value.client,
major: splitted[0],
minor: splitted[1],
patch: splitted[2],
};
};

export const fetchVersions: () => TE.TaskEither<string, ClientVersions> = () =>
export const fetchVersions = (
token: authApi.AccessToken,
): TE.TaskEither<string, Array<Version>> =>
http.validateResponse(
http.get(`${config.baseUrl}/version/clients`),
http.get(`${config.baseUrl}/version/clients`, {
headers: authApi.authHeader(token),
}),
versionsResponse,
response => response.resources,
response => response.resources.map(toAppVersion),
);
40 changes: 38 additions & 2 deletions src/lib/__tests__/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,51 @@ describe('Validate The valid password to be true', () => {
});
});

describe('Validate The invalid password to be true', () => {
describe('Validate The invalid password', () => {
[
'',
'password22',
'PasswordSalasana',
'ÄteritsiputeRitsipuolilautatsijänk',
].forEach(password => {
it(`decodes valid password ${password}`, () => {
it(`decodes inValid password ${password}`, () => {
expect(isLeft(validators.ValidPassword.decode(password))).toEqual(true);
});
});
});

describe('Validate correct versions', () => {
['1.23.3', '10.20.0', '3.99.99', '2.11.1'].forEach(version => {
it(`decodes valid version ${version}`, () => {
expect(isRight(validators.ValidVersion.decode(version))).toEqual(true);
});
});
});

describe('Validate invalid versions', () => {
[
'1.2',
'1.2.3.4',
'123.45.6',
'1.234.5',
'01.2.3',
'1.2.03',
'a.b.c',
'1.2.a',
'',
'1..2',
'.1.2.3',
'1.2.3.',
'1.2.',
'1..',
'1',
null,
undefined,
123,
{},
].forEach(version => {
it(`invalidates invalid version ${version}`, () => {
expect(isLeft(validators.ValidVersion.decode(version))).toEqual(true);
});
});
});
5 changes: 5 additions & 0 deletions src/lib/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ const decode =
TE.fromEither,
);

// const log = <A>(value: A) => {
// console.log(value);
// return value;
// };

export const validateResponse = <A, B, C>(
task: TE.TaskEither<string, Response>,
model: t.Type<A, B, unknown>,
Expand Down
8 changes: 8 additions & 0 deletions src/lib/isDevice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,11 @@ export const isDevice = (platform: PlatformOSType) =>
platform === RN.Platform.OS;

export const hasNotch = () => Device.hasNotch();

export const getClient = () => {
if (RN.Platform.OS === 'android') {
return 'ylitse_android';
}

return 'ylitse_ios';
};
20 changes: 20 additions & 0 deletions src/lib/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ export const isValidDescription = (value: string): boolean => {
return /^(?!\s*$).+/.test(value);
};

const isValidVersion = (value: string): boolean => {
return /^\d{1,2}\.\d{1,2}\.\d{1,2}$/.test(value);
};

export const ValidEmail = new t.Type<string, string, unknown>(
'ValidEmail',
(input: unknown): input is string => typeof input === 'string',
Expand Down Expand Up @@ -91,3 +95,19 @@ export const ValidPassword = new t.Type<string, string, unknown>(
: t.failure(input, context),
t.identity,
);

export const ValidVersion = new t.Type<string, string, unknown>(
'ValidVersion',
(input: unknown): input is string => typeof input === 'string',
(input, context) =>
either.chain(t.string.validate(input, context), str => {
const noLeadingZeros = str
.split('.')
.every(part => part === '0' || !part.startsWith('0'));

return isValidVersion(str) && noLeadingZeros
? t.success(str)
: t.failure(input, context);
}),
t.identity,
);
26 changes: 17 additions & 9 deletions src/state/reducers/minimumVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import { Reducer } from 'redux-automaton';
import * as RD from '@devexperts/remote-data-ts';
import * as automaton from 'redux-automaton';
import * as actions from '../actions';
import * as T from 'fp-ts/lib/Task';

import * as minimumVersionApi from '../../api/minimumVersion';
import { cmd } from '../middleware';

import { Action } from '../actions';
import { AppState } from '../types';
import { withToken } from './accessToken';

import { pipe } from 'fp-ts/lib/function';

Expand All @@ -26,11 +25,9 @@ export const reducer: Reducer<AppState['minimumVersion'], Action> = (

return automaton.loop(
RD.pending,
cmd(
pipe(
minimumVersionApi.fetchVersions(),
T.map(actions.make('minimumVersion/get/end')),
),
withToken(
minimumVersionApi.fetchVersions,
actions.make('minimumVersion/get/end'),
),
);
}
Expand All @@ -45,5 +42,16 @@ export const reducer: Reducer<AppState['minimumVersion'], Action> = (
}
};

export const selectClientVersions = ({ minimumVersion }: AppState) =>
minimumVersion;
export const selectClientVersion =
(client: string) =>
({ minimumVersion }: AppState) => {
return pipe(
minimumVersion,
RD.fold(
() => undefined, // NotAsked
() => undefined, // Loading
() => undefined, // Failure
clients => clients.find(p => p.client === client),
),
);
};
2 changes: 1 addition & 1 deletion src/state/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,5 @@ export type AppState = {
sendDeviceToken: RemoteAction;
};
feedbackQuestions: RemoteData<Array<feedbackApi.Question>>;
minimumVersion: RemoteData<minimumVersionApi.ClientVersions>;
minimumVersion: RemoteData<Array<minimumVersionApi.Version>>;
};

0 comments on commit 52b20b7

Please sign in to comment.