Skip to content

Commit

Permalink
Merge pull request #277 from sos-lapsikyla/felix/minimum-version-hand…
Browse files Browse the repository at this point in the history
…ling

Minimum version handling
  • Loading branch information
rottabonus authored Oct 11, 2024
2 parents a278ecb + 035ecf3 commit 3246312
Show file tree
Hide file tree
Showing 18 changed files with 299 additions and 69 deletions.
50 changes: 23 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,18 @@ cp config.template.ios.json config.json

You can edit the config file as suited:

| Parameter | Description |
| -------------- | ---------------------------------|
| `baseUrl` | Ylitse API base URL |
| `loginUrl` | Ylitse service login URL |
| `feedBackUrl` | Feedback form URL |
| `termsUrl` | Terms and conditions URL |
| `userGuideUrl` | User's manual URL |
| `apuuUrl` | Apuu-chat URL |
| `sekasinUrl` | Sekasin-chat URL |
| `saferSpaceUrl` | Principals of a safer space URL |
| `messageFetchDelay`| Delay between polling |
| Parameter | Description |
| ----------------- | ---------------------------------------|
| `baseUrl` | Ylitse API base URL |
| `loginUrl` | Ylitse service login URL |
| `feedBackUrl` | Feedback form URL |
| `termsUrl` | Terms and conditions URL |
| `userGuideUrl` | User's manual URL |
| `apuuUrl` | Apuu-chat URL |
| `sekasinUrl` | Sekasin-chat URL |
| `saferSpaceUrl` | Principals of a safer space URL |
| `storeUrl` | App marketplace URL (build specific) |
| `messageFetchDelay`| Delay between polling |


### Running on iOS
Expand Down Expand Up @@ -92,22 +93,7 @@ npx react-native run-android

### Debugging

We are using [Flipper](https://fbflipper.com/) for debugging, so install the [Desktop application](https://fbflipper.com/docs/getting-started/) to your environment

1. Run the application
2. Open [Flipper](https://fbflipper.com/docs/getting-started/#setup-your-react-native-app)
3. Install plugin [redux-debugger](https://github.com/jk-gan/flipper-plugin-redux-debugger)
4. Now you should see all logs, networking and redux things in the Flipper tool

For troubleshooting, please refer to the [Flipper documentation](https://fbflipper.com/docs/getting-started/troubleshooting/)

#### Fixing issue when running API locally
Setup adb reverse connections:
```sh
adb reverse tcp:8081 tcp:8081; adb reverse tcp:8080 tcp:8080
```


Follow the instructions to use the [hermes debugger](https://reactnative.dev/docs/debugging?js-debugger=hermes)

### Running end-to-end tests

Expand Down Expand Up @@ -163,3 +149,13 @@ Replace the moustached values with your values
- If you get error ==ReferenceError: fetch is not defined== then you are not running the tests with npm 18, which ships with fetch. Refer to the _Node version_ section of this README

- If you get error ==ECONNREFUSED== then you are probably using localhost as a value for the ${YLITSE_API_URL} env-variable. Replace localhost with 127.0.0.1

- If you get error connecting to local API:

Setup adb reverse connections:
```sh
adb reverse tcp:8081 tcp:8081; adb reverse tcp:8080 tcp:8080
```



4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ android {
applicationId "com.ylitse"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 48
versionName "2.14.0"
versionCode 49
versionName "2.15.0"
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
Expand Down
1 change: 1 addition & 0 deletions appcenter-pre-build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ tee config.json > /dev/null <<EOF
"userGuideUrl": "$YLITSE_USERGUIDEURL",
"apuuUrl": "$YLITSE_APUUURL",
"sekasinUrl": "$YLITSE_SEKASINURL",
"storeUrl": "$YLITSE_STOREURL",
"saferSpaceUrl": "$YLITSE_SAFERSPACEURL",
"messageFetchDelay": $YLITSE_MESSAGEFETCHDELAY
}
Expand Down
1 change: 1 addition & 0 deletions config.template.android.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
"apuuUrl": "http://10.0.2.2:3000",
"sekasinUrl": "http://10.0.2.2:3000",
"saferSpaceUrl": "http://10.0.2.2:3000",
"storeUrl":"http://localhost:3000",
"messageFetchDelay": 2000
}
1 change: 1 addition & 0 deletions config.template.ios.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"userGuideUrl": "http://localhost:3000",
"apuuUrl": "http://localhost:3000",
"sekasinUrl": "http://localhost:3000",
"storeUrl":"http://localhost:3000",
"saferSpaceUrl": "http://10.0.2.2:3000",
"messageFetchDelay": 2000
}
33 changes: 30 additions & 3 deletions src/Screens/Main/Tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import React from 'react';
import * as RN from 'react-native';

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

import { selectIsVersionBigEnough } from '../../state/reducers/minimumVersion';
import { storeUrl } from '../../api/config';
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 @@ -16,6 +23,7 @@ import Settings, { SettingsRoute } from './Settings';
import QuestionModal from '../components/QuestionModal';
import { Answer } from '../../api/feedback';
import { TabBar } from './TabBar';
import Modal from '../components/Modal';

export type TabsRoute = {
'Main/Tabs': {
Expand All @@ -30,9 +38,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 appClient = getClient();

const isAppVersionBigEnough = ReactRedux.useSelector(
selectIsVersionBigEnough(appClient),
);

const handleRefetchData = () => {
dispatch({ type: 'feedback/getQuestions/start', payload: undefined });
Expand All @@ -53,6 +65,8 @@ const Main = ({ navigation, route }: Props) => {
callback: handleRefetchData,
});

const openStore = () => RN.Linking.openURL(storeUrl);

React.useEffect(() => {
navigation.dispatch(state => {
const routes = state.routes.filter(r => !r.name.includes('Onboarding'));
Expand All @@ -65,6 +79,10 @@ const Main = ({ navigation, route }: Props) => {
});
}, []);

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

return (
<>
<Tab.Navigator
Expand All @@ -83,6 +101,15 @@ const Main = ({ navigation, route }: Props) => {
onAnswer={handleAnswer}
/>
)}
{!isAppVersionBigEnough && (
<Modal
modalType="danger"
title="main.version.not.big.enough.title"
messageId="main.version.not.big.enough.text"
primaryButtonMessage="main.version.not.big.enough.button"
onPrimaryPress={openStore}
/>
)}
</>
);
};
Expand Down
65 changes: 30 additions & 35 deletions src/Screens/components/Modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,49 +36,44 @@ const Modal: React.FC<Props> = props => {
} as const;

return (
<RN.View style={styles.container}>
<RN.Modal animationType="fade" transparent={true} visible={true}>
<RN.View style={styles.background}>
<RN.View style={[props.style, styles.modalContainer]}>
<RN.View style={[styles.titleContainer, { backgroundColor }]}>
<RN.Image style={styles.image} source={icon} />
<Message id={props.title} style={styles.title} />
</RN.View>
<RN.Modal animationType="fade" transparent={true} visible={true}>
<RN.View style={styles.background}>
<RN.View style={[props.style, styles.modalContainer]}>
<RN.View style={[styles.titleContainer, { backgroundColor }]}>
<RN.Image style={styles.image} source={icon} />
<Message id={props.title} style={styles.title} />
</RN.View>

<RN.View style={styles.textContainer}>
<Message id={props.messageId} style={styles.text} />
</RN.View>
<RN.View style={styles.textContainer}>
<Message id={props.messageId} style={styles.text} />
</RN.View>

<RN.View style={[styles.buttonContainer, justifyButtons]}>
{props.onSecondaryPress && (
<Button
onPress={props.onSecondaryPress}
messageId={props.secondaryButtonMessage ?? 'meta.ok'}
style={[styles.button, styles.secondaryButton]}
emphasis="medium"
/>
)}
{props.onPrimaryPress && (
<Button
onPress={props.onPrimaryPress}
messageId={
props.primaryButtonMessage ?? 'components.remoteData.retry'
}
style={[styles.button, styles.primaryButton]}
/>
)}
</RN.View>
<RN.View style={[styles.buttonContainer, justifyButtons]}>
{props.onSecondaryPress && (
<Button
onPress={props.onSecondaryPress}
messageId={props.secondaryButtonMessage ?? 'meta.ok'}
style={[styles.button, styles.secondaryButton]}
emphasis="medium"
/>
)}
{props.onPrimaryPress && (
<Button
onPress={props.onPrimaryPress}
messageId={
props.primaryButtonMessage ?? 'components.remoteData.retry'
}
style={[styles.button, styles.primaryButton]}
/>
)}
</RN.View>
</RN.View>
</RN.Modal>
</RN.View>
</RN.View>
</RN.Modal>
);
};

const styles = RN.StyleSheet.create({
container: {
flex: 1,
},
background: {
backgroundColor: colors.transparentBlack,
position: 'absolute',
Expand Down
1 change: 1 addition & 0 deletions src/api/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export const apuuUrl = config.apuuUrl;
export const sekasinUrl = config.sekasinUrl;
export const saferSpaceUrl = config.saferSpaceUrl;
export const messageFetchDelay = config.messageFetchDelay;
export const storeUrl = config.storeUrl;
50 changes: 50 additions & 0 deletions src/api/minimumVersion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as t from 'io-ts';
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 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 AppClient = {
client: string;
major: number;
minor: number;
patch: number;
};

export const toAppClient = (value: Client): AppClient => {
const [major, minor, patch] = value.version.split('.').map(Number);

return {
client: value.client,
major,
minor,
patch,
};
};

export const fetchVersions = (
token: authApi.AccessToken,
): TE.TaskEither<string, Array<AppClient>> =>
http.validateResponse(
http.get(`${config.baseUrl}/version/clients`, {
headers: authApi.authHeader(token),
}),
versionsResponse,
response => response.resources.map(toAppClient),
);
38 changes: 36 additions & 2 deletions src/lib/__tests__/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,49 @@ 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', '3.110.5'].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',
'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);
});
});
});
8 changes: 8 additions & 0 deletions src/lib/isDevice.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import RN from 'react-native';
import { PlatformOSType } from 'react-native/types';
import Device from 'react-native-device-info';
import { toAppClient } from '../api/minimumVersion';

export const isDevice = (platform: PlatformOSType) =>
platform === RN.Platform.OS;

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

export const getClient = () => {
const version = Device.getVersion();
const client = RN.Platform.OS === 'android' ? 'ylitse_android' : 'ylitse_ios';

return toAppClient({ version, client });
};
Loading

0 comments on commit 3246312

Please sign in to comment.