Skip to content

Commit

Permalink
Enables most TSLint rules (#115)
Browse files Browse the repository at this point in the history
* Adds typestrict tslint rules

* Explicit boolean props

* Enables/fixes typestrict and tslint-react rules

* Adds tslint-sonarts rules

* Enables tslint-immutable immutability rules

* Rebased master
  • Loading branch information
cloudify committed May 4, 2018
1 parent fd338ee commit 807b323
Show file tree
Hide file tree
Showing 38 changed files with 202 additions and 179 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@
"tslint-plugin-prettier": "^1.3.0",
"tslint-react": "^3.5.1",
"tslint-sonarts": "^1.6.0",
"typescript": "^2.8.3"
"typescript": "^2.8.3",
"typestrict": "^0.0.8"
},
"jest": {
"preset": "react-native",
Expand Down
4 changes: 2 additions & 2 deletions ts/@types/native-base-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ declare module "native-base/src/Utils/mapPropsToStyleNames" {
}

declare module "native-base/src/theme/components" {
type Variables = {};
type Theme = {};
type Variables = any;
type Theme = any;
export default function(variables: Variables): Theme;
}

Expand Down
9 changes: 7 additions & 2 deletions ts/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ import RootContainer from "./RootContainer";

// Inizialize Mixpanel and configure the global js error handler
Mixpanel.sharedInstanceWithToken(config.mixpanelToken);
configureErrorHandler();
configureErrorHandler()
// tslint:disable-next-line:no-empty
.then(() => {})
// tslint:disable-next-line:no-empty
.catch(() => {});

configurePushNotifications();

const { store, persistor } = configureStoreAndPersistor();
Expand All @@ -29,7 +34,7 @@ export default class App extends React.Component<never, never> {
return (
<StyleProvider style={theme()}>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<PersistGate loading={undefined} persistor={persistor}>
<RootContainer />
</PersistGate>
</Provider>
Expand Down
14 changes: 7 additions & 7 deletions ts/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ export type LoginResult = LoginSuccess | LoginFailure;
const LOGIN_SUCCESS_PREFIX = "/profile.html?token=";
const LOGIN_FAILURE_PREFIX = "/error.html";

export const extractLoginResult = (url: string): LoginResult | null => {
export const extractLoginResult = (url: string): LoginResult | undefined => {
// Check for LOGIN_SUCCESS
let tokenPathPos = url.indexOf(LOGIN_SUCCESS_PREFIX);
const successTokenPathPos = url.indexOf(LOGIN_SUCCESS_PREFIX);
// eslint-disable-next-line no-magic-numbers
if (tokenPathPos !== -1) {
const token = url.substr(tokenPathPos + LOGIN_SUCCESS_PREFIX.length);
if (successTokenPathPos !== -1) {
const token = url.substr(successTokenPathPos + LOGIN_SUCCESS_PREFIX.length);
// eslint-disable-next-line no-magic-numbers
if (token && token.length > 0) {
return {
Expand All @@ -84,16 +84,16 @@ export const extractLoginResult = (url: string): LoginResult | null => {
}

// Check for LOGIN_FAILURE
tokenPathPos = url.indexOf(LOGIN_FAILURE_PREFIX);
const failureTokenPathPos = url.indexOf(LOGIN_FAILURE_PREFIX);
// eslint-disable-next-line no-magic-numbers
if (tokenPathPos !== -1) {
if (failureTokenPathPos !== -1) {
return {
success: false
};
}

// Url is not LOGIN related
return null;
return undefined;
};

// Fetch the profile from the Proxy
Expand Down
9 changes: 6 additions & 3 deletions ts/boot/configureErrorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@ async function customErrorHandler(
isFatal?: boolean
): Promise<void> {
if (isFatal) {
error.stack = await getStackTrace(sourceMapper, options, error);
const errorWithStack = {
...error,
stack: await getStackTrace(sourceMapper, options, error)
};
// Send a remote event that contains the error stack trace
Mixpanel.trackWithProperties("APPLICATION_ERROR", {
ERROR: JSON.stringify(error),
ERROR_STACK_TRACE: JSON.stringify(error.stack),
ERROR: JSON.stringify(errorWithStack),
ERROR_STACK_TRACE: JSON.stringify(errorWithStack.stack),
APP_VERSION: version
});

Expand Down
1 change: 0 additions & 1 deletion ts/boot/configurePushNotification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { debugRemotePushNotification, gcmSenderId } from "../config";

function configurePushNotifications() {
PushNotification.configure({

// Called when a remote or local notification is opened or received
onNotification: notification => {
if (debugRemotePushNotification) {
Expand Down
1 change: 1 addition & 0 deletions ts/boot/configureStoreAndPersistor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ function configureStoreAndPersistor(): {
const persistor = persistStore(store);

if (isDebuggingInChrome) {
// tslint:disable-next-line:no-object-mutation
(window as any).store = store;
}

Expand Down
2 changes: 1 addition & 1 deletion ts/components/ConnectionBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ConnectionBar extends React.PureComponent<Props, never> {
public render() {
const { isConnected } = this.props;
if (isConnected) {
return null;
return undefined;
}
return (
<View style={styles.container}>
Expand Down
3 changes: 2 additions & 1 deletion ts/components/IdpsGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ class IdpsGrid extends React.Component<Props> {
): React.ReactElement<any> => {
const { onIdpSelected } = this.props;
const idp = info.item;
const onPress = () => onIdpSelected(idp);
return (
<View style={styles.gridItem}>
<Button block white onPress={(): void => onIdpSelected(idp)}>
<Button block={true} white={true} onPress={onPress}>
<Image source={idp.logo} style={styles.idpLogo} />
</Button>
</View>
Expand Down
2 changes: 1 addition & 1 deletion ts/components/Pinpad.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const Pinpad: React.SFC<Props> = props => {
const { autofocus, compareWithCode, onFulfill } = props;
return (
<CodeInput
secureTextEntry
secureTextEntry={true}
keyboardType="numeric"
autoFocus={autofocus}
className="border-b"
Expand Down
2 changes: 1 addition & 1 deletion ts/components/forms/SpidInformationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class SpidInformationForm extends React.Component<InjectedFormProps, never> {
component={renderNativeBaseInput}
placeholder={getCurrentFormFieldProperty("email")("placeholder")}
validate={[validators.email]}
showError
showError={true}
/>
</Form>
);
Expand Down
3 changes: 2 additions & 1 deletion ts/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export const apiUrlPrefix = Config.API_URL_PREFIX;
export const mixpanelToken = Config.MIXPANEL_TOKEN;
export const enableTestIdp = Config.ENABLE_TEST_IDP === "YES";
export const gcmSenderId = Config.GCM_SENDER_ID;
export const debugRemotePushNotification = Config.DEBUG_REMOTE_PUSH_NOTIFICATION === "YES";
export const debugRemotePushNotification =
Config.DEBUG_REMOTE_PUSH_NOTIFICATION === "YES";
2 changes: 2 additions & 0 deletions ts/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ const en = require("../locales/en.json");
const it = require("../locales/it.json");

// Should the app fallback to English if user locale doesn't exists
// tslint:disable-next-line:no-object-mutation
I18n.fallbacks = true;

// Define the supported translations
// tslint:disable-next-line:no-object-mutation
I18n.translations = {
en,
it
Expand Down
6 changes: 3 additions & 3 deletions ts/middlewares/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ export function actionTracking(): (_: Dispatch) => (_: Action) => Action {

// gets the current screen from navigation state
// TODO: Need to be fixed
export function getCurrentRouteName(navNode: any): string | null {
export function getCurrentRouteName(navNode: any): string | undefined {
if (!navNode) {
return null;
return undefined;
}

if (navNode.routeName && typeof navNode.routeName === "string") {
Expand All @@ -90,7 +90,7 @@ export function getCurrentRouteName(navNode: any): string | null {
return getCurrentRouteName(route);
}

return null;
return undefined;
}

/*
Expand Down
4 changes: 2 additions & 2 deletions ts/reducers/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const INITIAL_STATE: NavigationState = AppNavigator.router.getStateForAction(
function nextState(
state: NavigationState,
action: Action
): NavigationState | null {
): NavigationState | undefined {
switch (action.type) {
/**
* The getStateForAction method only accepts NavigationActions so we need to
Expand All @@ -24,7 +24,7 @@ function nextState(
return AppNavigator.router.getStateForAction(action, state);

default:
return null;
return undefined;
}
}

Expand Down
6 changes: 2 additions & 4 deletions ts/sagas/onboarding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,8 @@ function* pinCheckSaga(): Iterator<Effect> {
});
yield put(navigateToOnboardingPinScreenAction);

let isPinSaved = false;

// Loop until PIN successfully saved in the Keystore
while (!isPinSaved) {
while (true) {
// Here we wait the user to create a PIN
const action: PinCreateRequest = yield take(PIN_CREATE_REQUEST);

Expand All @@ -69,7 +67,7 @@ function* pinCheckSaga(): Iterator<Effect> {
type: PIN_CREATE_SUCCESS
});

isPinSaved = true;
break;
} catch (error) {
yield put({
type: PIN_CREATE_FAILURE
Expand Down
4 changes: 2 additions & 2 deletions ts/sagas/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import {
import { ProfileUpdateRequest } from "../store/actions/profile";

// A selector to get the token from the state
const getSessionToken = (state: GlobalState): string | null =>
state.session.isAuthenticated ? state.session.token : null;
const getSessionToken = (state: GlobalState): string | undefined =>
state.session.isAuthenticated ? state.session.token : undefined;

// A saga to load the Profile.
function* loadProfile(): Iterator<Effect> {
Expand Down
8 changes: 3 additions & 5 deletions ts/screens/authentication/IdpLoginScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,15 @@ class IdpLoginScreen extends React.Component<Props, never> {
public render() {
const { session } = this.props;
if (isUnauthenticatedWithoutIdpSessionState(session)) {
return null;
return undefined;
}
const loginUri = LOGIN_BASE_URL + session.idp.entityID;
const onPress = () => this.props.navigation.goBack();
return (
<Container>
<AppHeader>
<Left>
<Button
transparent
onPress={(): boolean => this.props.navigation.goBack()}
>
<Button transparent={true} onPress={onPress}>
<Icon name="chevron-left" />
</Button>
</Left>
Expand Down
38 changes: 19 additions & 19 deletions ts/screens/authentication/IdpSelectionScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,7 @@ class IdpSelectionScreen extends React.Component<Props, never> {
<Container>
<AppHeader>
<Left>
<Button
transparent
onPress={(): boolean => this.props.navigation.goBack()}
>
<Button transparent={true} onPress={this.goBack}>
<Icon name="chevron-left" />
</Button>
</Left>
Expand All @@ -105,33 +102,36 @@ class IdpSelectionScreen extends React.Component<Props, never> {
source={require("../../../img/spid.png")}
style={styles.spidLogo}
/>
<View spacer />
<View spacer={true} />
<H1>{I18n.t("authentication.idp_selection.contentTitle")}</H1>
</View>
<Content alternative>
<IdpsGrid
idps={enabledIdps}
onIdpSelected={(idp: IdentityProvider) => {
this.props.dispatch(selectIdp(idp));
}}
/>
<View spacer />
<Content alternative={true}>
<IdpsGrid idps={enabledIdps} onIdpSelected={this.onIdpSelect} />
<View spacer={true} />
<Button
block
light
bordered
onPress={(): boolean => this.props.navigation.goBack()}
block={true}
light={true}
bordered={true}
onPress={this.goBack}
>
<Text>{I18n.t("authentication.idp_selection.cancel")}</Text>
</Button>
</Content>
<View footer>
<Button block transparent>
<View footer={true}>
<Button block={true} transparent={true}>
<Text>{I18n.t("authentication.landing.nospid")}</Text>
</Button>
</View>
</Container>
);
}

private goBack() {
this.props.navigation.goBack();
}

private onIdpSelect(idp: IdentityProvider) {
this.props.dispatch(selectIdp(idp));
}
}
export default connect()(IdpSelectionScreen);
38 changes: 20 additions & 18 deletions ts/screens/authentication/LandingScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,36 +32,38 @@ class LandingScreen extends React.Component<Props, never> {
</Body>
</AppHeader>
<Content />
<View footer>
<View footer={true}>
<Button
block
primary
iconLeft
onPress={(): boolean =>
this.props.navigation.navigate(
ROUTES.AUTHENTICATION_IDP_SELECTION
)
}
block={true}
primary={true}
iconLeft={true}
onPress={this.navigateToIdpSelection}
>
<Icon name="user" />
<Text>{I18n.t("authentication.landing.login")}</Text>
</Button>
<View spacer />
<View spacer={true} />
<Button
block
small
transparent
onPress={(): boolean =>
this.props.navigation.navigate(
ROUTES.AUTHENTICATION_SPID_INFORMATION_REQUEST
)
}
block={true}
small={true}
transparent={true}
onPress={this.navigateToSpidInformationRequest}
>
<Text>{I18n.t("authentication.landing.nospid")}</Text>
</Button>
</View>
</Container>
);
}

private navigateToIdpSelection() {
this.props.navigation.navigate(ROUTES.AUTHENTICATION_IDP_SELECTION);
}

private navigateToSpidInformationRequest() {
this.props.navigation.navigate(
ROUTES.AUTHENTICATION_SPID_INFORMATION_REQUEST
);
}
}
export default connect()(LandingScreen);
Loading

0 comments on commit 807b323

Please sign in to comment.