...
 
Commits (26)
......@@ -9,6 +9,7 @@ import {
import { onError } from "mobx-react";
import logService from './src/common/services/log.service';
import Sentry from 'react-native-sentry';
onError(error => {
console.log(error);
......@@ -48,7 +49,9 @@ if (!__DEV__) {
* Native Errors
*/
setNativeExceptionHandler((exceptionString) => {
Sentry.captureException(new Error(exceptionString), {
logger: 'NativeExceptionHandler',
});
console.log(exceptionString);
logService.exception(exceptionString);
});
}
import api from '../../../src/common/services/api.service';
import api, { ApiError } from '../../../src/common/services/api.service';
import session from '../../../src/common/services/session.service';
import abortableFetch, {abort} from '../../../src/common/helpers/abortableFetch';
import { MINDS_API_URI } from '../../../src/config/Config';
......@@ -46,14 +46,14 @@ describe('api service POST', () => {
const res = await api.post('api/path', params);
} catch (err) {
// assert on the error
expect(err).toEqual(response);
expect(err).toBeInstanceOf(ApiError);
}
});
it('should return api error', async () => {
const response = { json: jest.fn(), ok: true };
const respBody = { status: 'error', error: 'some error' };
const respBody = { status: 500, error: 'some error' };
response.json.mockResolvedValue(respBody);
const params = {p1: 1, p2: 2};
......@@ -63,7 +63,7 @@ describe('api service POST', () => {
const res = await api.post('api/path', params);
} catch (err) {
// assert on the error
expect(err).toEqual(respBody);
expect(err).toBeInstanceOf(ApiError);
}
});
});
......@@ -105,7 +105,7 @@ describe('api service GET', () => {
const res = await api.get('api/path', params, null);
} catch (err){
// assert on the error
expect(err).toEqual(response);
expect(err).toBeInstanceOf(ApiError);
}
});
......@@ -122,7 +122,7 @@ describe('api service GET', () => {
const res = await api.get('api/path', params);
} catch (err) {
// assert on the error
expect(err).toEqual(respBody);
expect(err).toBeInstanceOf(ApiError);
}
});
});
......@@ -167,7 +167,7 @@ describe('api service DELETE', () => {
const res = await api.delete('api/path', params);
} catch (err) {
// assert on the error
expect(err).toEqual(response);
expect(err).toBeInstanceOf(ApiError);
}
});
......@@ -184,7 +184,7 @@ describe('api service DELETE', () => {
const res = await api.delete('api/path', params);
} catch (err) {
// assert on the error
expect(err).toEqual(respBody);
expect(err).toBeInstanceOf(ApiError);
}
});
});
......@@ -229,7 +229,7 @@ describe('api service PUT', () => {
const res = await api.put('api/path', params);
} catch (err) {
// assert on the error
expect(err).toEqual(response);
expect(err).toBeInstanceOf(ApiError);
}
});
......@@ -245,7 +245,7 @@ describe('api service PUT', () => {
const res = await api.put('api/path', params);
} catch (err) {
// assert on the error
expect(err).toEqual(respBody);
expect(err).toBeInstanceOf(ApiError);
}
});
});
......
......@@ -22,10 +22,10 @@ org.gradle.jvmargs=-Xmx2048m
systemProp.org.gradle.internal.http.connectionTimeout=180000
systemProp.org.gradle.internal.http.socketTimeout=180000
versionName=3.9.0
versionName=3.9.1
# CUSTOM
versionCode=1050000012
versionCode=1050000013
# PLAY STORE
#versionCode=310030
# versionCode=310031
......@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.9.0</string>
<string>3.9.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
......
......@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>3.9.0</string>
<string>3.9.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>Minds</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleGetInfoString</key>
<string/>
<key>CFBundleIdentifier</key>
<string>com.minds.mobile</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.9.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>minds</string>
<key>CFBundleURLSchemes</key>
<array>
<string>minds</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>5</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
<string/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>dev.minds.io</key>
<dict>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
<key>localhost</key>
<dict>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
<key>NSCalendarsUsageDescription</key>
<string>$(PRODUCT_NAME) need access to the calendar to schedule your gatherings</string>
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to use your camera in order to upload images or videos</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string/>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to your microphone in order to record videos </string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to your Photo Library in order to upload images</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>$(PRODUCT_NAME) would like access to your photo gallery so that you can upload images</string>
<key>UIAppFonts</key>
<array>
<string>Entypo.ttf</string>
<string>EvilIcons.ttf</string>
<string>Feather.ttf</string>
<string>FontAwesome.ttf</string>
<string>Foundation.ttf</string>
<string>Ionicons.ttf</string>
<string>MaterialCommunityIcons.ttf</string>
<string>MaterialIcons.ttf</string>
<string>Octicons.ttf</string>
<string>SimpleLineIcons.ttf</string>
<string>Zocial.ttf</string>
<string>Roboto-Black.ttf</string>
<string>Roboto-BlackItalic.ttf</string>
<string>Roboto-Bold.ttf</string>
<string>Roboto-BoldItalic.ttf</string>
<string>Roboto-Italic.ttf</string>
<string>Roboto-Light.ttf</string>
<string>Roboto-LightItalic.ttf</string>
<string>Roboto-Medium.ttf</string>
<string>Roboto-MediumItalic.ttf</string>
<string>Roboto-Regular.ttf</string>
<string>Roboto-Thin.ttf</string>
<string>Roboto-ThinItalic.ttf</string>
<string>AntDesign.ttf</string>
<string>FontAwesome5_Brands.ttf</string>
<string>FontAwesome5_Regular.ttf</string>
<string>FontAwesome5_Solid.ttf</string>
<string>Fontisto.ttf</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>voip</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>Minds</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleGetInfoString</key>
<string/>
<key>CFBundleIdentifier</key>
<string>com.minds.mobile</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.9.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>minds</string>
<key>CFBundleURLSchemes</key>
<array>
<string>minds</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>5</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
<string/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>dev.minds.io</key>
<dict>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
<key>localhost</key>
<dict>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
<key>NSCalendarsUsageDescription</key>
<string>$(PRODUCT_NAME) need access to the calendar to schedule your gatherings</string>
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to use your camera in order to upload images or videos</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string/>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to your microphone in order to record videos </string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to your Photo Library in order to upload images</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>$(PRODUCT_NAME) would like access to your photo gallery so that you can upload images</string>
<key>UIAppFonts</key>
<array>
<string>Entypo.ttf</string>
<string>EvilIcons.ttf</string>
<string>Feather.ttf</string>
<string>FontAwesome.ttf</string>
<string>Foundation.ttf</string>
<string>Ionicons.ttf</string>
<string>MaterialCommunityIcons.ttf</string>
<string>MaterialIcons.ttf</string>
<string>Octicons.ttf</string>
<string>SimpleLineIcons.ttf</string>
<string>Zocial.ttf</string>
<string>Roboto-Black.ttf</string>
<string>Roboto-BlackItalic.ttf</string>
<string>Roboto-Bold.ttf</string>
<string>Roboto-BoldItalic.ttf</string>
<string>Roboto-Italic.ttf</string>
<string>Roboto-Light.ttf</string>
<string>Roboto-LightItalic.ttf</string>
<string>Roboto-Medium.ttf</string>
<string>Roboto-MediumItalic.ttf</string>
<string>Roboto-Regular.ttf</string>
<string>Roboto-Thin.ttf</string>
<string>Roboto-ThinItalic.ttf</string>
<string>AntDesign.ttf</string>
<string>FontAwesome5_Brands.ttf</string>
<string>FontAwesome5_Regular.ttf</string>
<string>FontAwesome5_Solid.ttf</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>voip</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
......@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>3.9.0</string>
<string>3.9.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
......
......@@ -8,6 +8,15 @@ import abortableFetch from '../helpers/abortableFetch';
import { Version } from '../../config/Version';
import logService from './log.service';
/**
* Api Error
*/
export class ApiError extends Error {
constructor(...args) {
super(...args)
}
}
/**
* Api service
*/
......@@ -48,6 +57,22 @@ class ApiService {
}).join('&');
}
/**
* Throw an error
* @param {any} err
* @param {string} url
*/
_throwError(err, url) {
if (err instanceof Error) {
throw err;
}
const error = new ApiError(err.message || err.responseText || `Request error on: ${url}`);
if (err.status) {
error.status = err.status;
}
throw error;
}
/**
* Api get with abort support
* @param {string} url
......@@ -76,10 +101,12 @@ class ApiService {
// Bad authorization
if (err.status && err.status == 401) {
const refreshed = await session.badAuthorization(); //not actually a logout
if (refreshed) return await this.get(url, params, tag);
if (refreshed) {
return await this.get(url, params, tag);
}
session.logout();
}
throw err;
this._throwError(err, url);
}
}
......@@ -104,11 +131,13 @@ class ApiService {
} catch(err) {
if (err.status && err.status == 401) {
const refreshed = await session.badAuthorization(); //not actually a logout
if (refreshed) return await this.post(url, body);
if (refreshed) {
return await this.post(url, body);
}
logService.log('[ApiService] Token refresh failed: logout');
session.logout();
}
throw err;
this._throwError(err, url);
}
}
......@@ -133,19 +162,21 @@ class ApiService {
} catch(err) {
if (err.status && err.status == 401) {
const refreshed = await session.badAuthorization(); //not actually a logout
if (refreshed) return await this.post(url, body);
if (refreshed) {
return await this.put(url, body);
}
logService.log('[ApiService] Token refresh failed: logout');
session.logout();
}
throw err;
this._throwError(err, url);
}
}
/**
* Upload file to s3, differences with generic upload are headers and formData (wich is not necessary)
* @param {any} lease
* @param {any} file
* @param {function} progress
* @param {any} lease
* @param {any} file
* @param {function} progress
*/
uploadToS3(lease, file, progress) {
......@@ -207,11 +238,13 @@ class ApiService {
} catch(err) {
if (err.status && err.status == 401) {
const refreshed = await session.badAuthorization(); //not actually a logout
if (refreshed) return await this.post(url, body);
if (refreshed) {
return await this.delete(url, body);
}
logService.log('[ApiService] Token refresh failed: logout');
session.logout();
}
throw err;
this._throwError(err, url);
}
}
......
......@@ -312,7 +312,8 @@ export default class FeedsService {
}
if (!await this.fetchLocal()) {
throw new Error('Error fetching feed from remote or local');
// if there is no local data rethrow the exception
throw err;
}
showMessage({
......
......@@ -7,6 +7,7 @@ import settingsService from '../../settings/SettingsService'
import settingsStore from '../../settings/SettingsStore';
import { Sentry } from 'react-native-sentry';
import { isNetworkFail } from '../helpers/abortableFetch';
import { ApiError } from './api.service';
const parseErrorStack = error => {
if (!error || !error.stack) {
......@@ -67,7 +68,7 @@ class LogService {
prepend = null;
}
if (!isNetworkFail(error)) {
if (!isNetworkFail(error) && !(error instanceof ApiError && error.status === 401)) {
// report the issue to sentry
Sentry.captureException(error);
}
......
......@@ -15,7 +15,6 @@ export default class IosPlatfom extends AbstractPlatform {
NotificationsIOS.addEventListener('remoteNotificationsRegistrationFailed', this._onPushRegistrationFailed.bind(this));
NotificationsIOS.addEventListener('notificationOpened', this._onNotificationOpened.bind(this));
NotificationsIOS.requestPermissions();
NotificationsIOS.consumeBackgroundQueue();
}
/**
......
......@@ -61,6 +61,13 @@ class SessionService {
const [accessToken, refreshToken, user] = await this.sessionStorage.getAll();
// if there is no session active we clean up and return;
if (!accessToken) {
this.setToken(null);
this.setRefreshToken(null);
return null;
}
const { access_token, access_token_expires } = accessToken;
const { refresh_token, refresh_token_expires } = refreshToken;
......
......@@ -135,7 +135,11 @@ class DiscoveryStore {
reset() {
this.onFilterChangeDisposer && this.onFilterChangeDisposer();
this.onSearchChangeDisposer && this.onSearchChangeDisposer();
this.filters.clear();
if (this.filters) {
this.filters.clear();
}
this.feedStore.reset();
this.listStore.clear();
this.listenChanges();
......
......@@ -26,6 +26,7 @@ import Colors from '../../styles/Colors';
import stylesheet from '../../onboarding/stylesheet';
import { CommonStyle as CS } from '../../styles/Common';
import i18n from '../../common/services/i18n.service';
import logService from '../../common/services/log.service';
@inject('user', 'wallet')
@observer
......
......@@ -47,7 +47,7 @@ class WalletStore {
if (addresses && addresses.length > 0) {
addresses.forEach(async address => {
if (address.label.toLowerCase() != 'offchain') {
if (address.label.toLowerCase() != 'offchain' && address.address !== '') {
address.ethBalance = await web3Service.getBalance(address.address);
}
});
......
......@@ -7605,10 +7605,10 @@ react-native-elements@^0.19.1:
opencollective "^1.0.3"
prop-types "^15.5.8"
react-native-exception-handler@^2.10.7:
version "2.10.7"
resolved "https://registry.yarnpkg.com/react-native-exception-handler/-/react-native-exception-handler-2.10.7.tgz#3ce2f10d6a8add35fdc036af62c2859c4b7f505c"
integrity sha512-e2zv0BiP9SRdr1vLDUyC2WbWHfgNV1a3BhRxK1ENjXVRY8mu+dfaaIHhFXdvYue//MEuGUQstu61NZoiO1u2KA==
react-native-exception-handler@^2.10.8:
version "2.10.8"
resolved "https://registry.yarnpkg.com/react-native-exception-handler/-/react-native-exception-handler-2.10.8.tgz#1a7f846592d888d23adaf797e31802585f1d9ff0"
integrity sha512-ZN+jwpADRkCUNdad/50k0mZdMoICGrGdtaxgvRU+pNcWRRBAXJhuo4+jY0eQaoVpx1ghycGE6tBu9ka8gL2NOQ==
react-native-exit-app@^1.0.0:
version "1.0.0"
......