...
 
Commits (2)
......@@ -4,5 +4,11 @@ then
echo "Trying to commit test code."
exit 1
else
exit 0
if git diff --cached locales/en.json | grep '\\n' >/dev/null 2>&1
then
echo "New line characters are forbiden in en.json, please split the lines in different translation terms."
exit 1
else
exit 0
fi
fi
\ No newline at end of file
......@@ -4,18 +4,49 @@ stages:
- build
- e2e
- deploy
- i18n
test:jest:
image: node:10.16.3
stage: test
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .jest/cache/
script:
- yarn install
- yarn test
image: node:10.16.3
stage: test
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .jest/cache/
script:
- yarn install
- yarn test
i18n:upload:
image: node:10.16.3
stage: i18n
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .jest/cache/
script:
- yarn install
- yarn locale upload --poeditor-key=${CI_POEDITOR_KEY} --overwrite=1
only:
refs:
- /^release-*/
i18n:uploadsync:
image: node:10.16.3
stage: i18n
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .jest/cache/
script:
- yarn install
- yarn locale upload --poeditor-key=${CI_POEDITOR_KEY} --overwrite=1 --sync_terms=1
only:
refs:
- /^stable-*/
build:android:
image: circleci/android:api-28-node
......@@ -46,7 +77,7 @@ deploy:s3:
- build:android
only:
refs:
- /^release-*/
- /^stable-*/
- /^test-*/
deploy:google_play:
......
......@@ -62,6 +62,7 @@ import commentStorageService from './src/comments/CommentStorageService';
import * as Sentry from '@sentry/react-native';
import apiService from './src/common/services/api.service';
import boostedContentService from './src/common/services/boosted-content.service';
import translationService from './src/common/services/translation.service';
let deepLinkUrl = '';
......@@ -143,7 +144,6 @@ sessionService.onLogin(async () => {
//on app logout
sessionService.onLogout(() => {
// clear app badge
badgeService.setUnreadConversations(0);
badgeService.setUnreadNotifications(0);
......@@ -152,6 +152,7 @@ sessionService.onLogout(() => {
feedsStorage.removeAll();
stores.notifications.clearLocal();
stores.groupsBar.clearLocal();
translationService.purgeLanguagesCache();
});
// disable yellow boxes
......
import {
Alert,
} from 'react-native';
import {Alert} from 'react-native';
import {
setNativeExceptionHandler,
setJSExceptionHandler
setJSExceptionHandler,
} from 'react-native-exception-handler';
import { onError } from "mobx-react";
import {onError} from 'mobx-react';
import logService from './src/common/services/log.service';
import * as Sentry from '@sentry/react-native';
import { isAbort, isNetworkFail } from './src/common/helpers/abortableFetch';
import { isApiError } from './src/common/services/api.service';
import { isUserError } from './src/common/UserError';
import shouldReportToSentry from './src/common/helpers/errors';
// Init Sentry (if not running test)
if (process.env.JEST_WORKER_ID === undefined) {
......@@ -23,25 +18,8 @@ if (process.env.JEST_WORKER_ID === undefined) {
'Non-Error exception captured with keys: code, domain, localizedDescription', // ignore initial error of sdk
],
beforeSend(event, hint) {
if (hint.originalException) {
// ignore network request failed
if (isNetworkFail(hint.originalException)) {
return null;
}
// ignore aborts
if (isAbort(hint.originalException)) {
return null;
}
// ignore user errors
if (isUserError(hint.originalException)) {
return null;
}
// only log api 500 errors
if (isApiError(hint.originalException) &&
(isNaN(hint.originalException.status) || hint.originalException.status < 500)
) {
if (!shouldReportToSentry(hint.originalException)) {
return null;
}
}
......@@ -53,7 +31,7 @@ if (process.env.JEST_WORKER_ID === undefined) {
}
return event;
}
},
});
}
......
......@@ -2,6 +2,7 @@
exports[`Activity component renders correctly 1`] = `
<View
onLayout={[Function]}
style={
Array [
Object {
......
......@@ -600,6 +600,11 @@ exports[`LoginForm component should renders correctly 1`] = `
"textColor": undefined,
"value": "zh",
},
Object {
"label": "Slovak",
"textColor": undefined,
"value": "sk",
},
]
}
onChange={[Function]}
......
......@@ -7,9 +7,12 @@ import blogFakeFactory from '../../__mocks__/fake/blogs/BlogFactory'
import BlogModel from '../../src/blogs/BlogModel';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
import Actions from '../../src/newsfeed/activity/Actions';
Linking.openURL = jest.fn();
jest.mock('../../src/newsfeed/activity/Actions', () => 'Actions');
/**
* Tests
*/
......@@ -44,7 +47,7 @@ describe('blog card component', () => {
wrapper.instance().navToBlog();
// expect fn to be called once
expect(navigation.push).toBeCalledWith('BlogView', {blog:blogEntity});
expect(navigation.push).toBeCalledWith('BlogView', {blog: blogEntity});
done();
} catch(e) {
done.fail(e);
......
......@@ -190,5 +190,160 @@ exports[`blog card component should renders correctly 1`] = `
</View>
</View>
</View>
<Actions
entity={
BlogModel {
"__list": null,
"access_id": "2",
"allow_comments": true,
"boost_rejection_reason": -1,
"categories": Array [],
"category": "education",
"container_guid": "100",
"custom_meta": Object {
"author": "",
"description": "",
"title": "",
},
"description": "Some Blog",
"draft_access_id": "0",
"excerpt": "Some description",
"featured": false,
"featured_id": false,
"guid": 1,
"header_bg": "1",
"header_top": "0",
"impressions": 100,
"isOwner": [Function],
"last_save": "1524843907",
"last_updated": "1524838665",
"license": "attribution-noncommercial-cc",
"mature": false,
"monetized": false,
"ownerObj": UserModel {
"__list": null,
"access_id": "2",
"allow_comments": true,
"banned": "no",
"blocked": false,
"boostProPlus": false,
"boost_autorotate": true,
"boost_rating": "2",
"briefdescription": "",
"categories": Array [
"music",
"nature",
"news",
"politics",
"science",
"technology",
"education",
"art",
"health",
],
"chat": true,
"city": "",
"container_guid": "0",
"deleted": "0",
"disabled_boost": false,
"dob": "",
"eth_wallet": "0x6fffffdadae350ddd5a1777d5a1777a4f39f0317",
"fb": false,
"feature_flags": false,
"featured_id": "725165605782560768",
"founder": false,
"gender": "private",
"getAvatarSource": [MockFunction] {
"calls": Array [
Array [],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
],
},
"guid": 100,
"icontime": "1523515420",
"impressions": 100,
"isOwner": [Function],
"language": "en",
"legacy_guid": false,
"mature": "1",
"mature_channel": "0",
"mature_visibility": false,
"merchant": Object {
"exclusive": Object {
"amount": "10",
"enabled": true,
},
"id": "acct_Dsdfjdlsaf34243",
"service": "stripe",
},
"mode": 0,
"monetized": "",
"name": "Some User",
"owner_guid": "0",
"pending_subscribe": false,
"permissions": Object {},
"pinned_posts": Array [],
"plus": true,
"programs": Array [
"affiliate",
],
"rating": "1",
"rewards": true,
"signup_method": false,
"site_guid": false,
"social_profiles": Array [],
"spam": "0",
"subscribed": false,
"subscriber": true,
"subscribers_count": 50,
"subtype": false,
"time_created": "1468113204",
"time_updated": false,
"type": "user",
"username": "someusermane",
"verified": true,
"website": "",
"wire_rewards": Object {
"description": "",
"rewards": Object {
"money": Array [],
"points": Array [],
},
},
},
"owner_guid": "1",
"paywall": false,
"perma_url": "https://www.minds.com/blog/view/836607056186159104",
"permissions": Object {},
"published": true,
"rating": 1,
"reminds": 58,
"route": "blog/view/836607056186159104",
"slug": "",
"subtype": "blog",
"thumbnail_src": "https://www.minds.com/fs/v1/banners/836607056186159104/1524838665",
"thumbs:down:count": 1,
"thumbs:down:user_guids": Array [
"3",
],
"thumbs:up:count": 2,
"thumbs:up:user_guids": Array [
"1",
"2",
],
"time_created": "1524838665",
"time_published": "1524838665",
"time_updated": "1524862542",
"title": "Some Title",
"type": "object",
"wire_threshold": "",
}
}
/>
</View>
`;
......@@ -208,7 +208,7 @@ exports[`channel header component owner should render correctly 1`] = `
}
>
<View
accessibilityLabel="Subscribe to this channel"
accessibilityLabel="Edit your channel settings"
accessible={true}
focusable={true}
isTVSelectable={true}
......@@ -246,56 +246,7 @@ exports[`channel header component owner should render correctly 1`] = `
}
>
Subscribe
</Text>
</View>
<View
accessibilityLabel="More"
accessible={true}
focusable={true}
isTVSelectable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"alignItems": "center",
"backgroundColor": "white",
"borderColor": "#4690D6",
"borderRadius": 20,
"borderWidth": 1,
"flexDirection": "row",
"justifyContent": "center",
"margin": 4,
"opacity": 1,
"padding": 4,
}
}
>
<Text
style={
Array [
Object {
"color": "#4690D6",
},
Array [
Object {
"marginLeft": 5,
},
Object {
"marginRight": 5,
},
],
]
}
>
More
Edit
</Text>
</View>
......
import {ApiError} from '../../src/common/services/api.service';
import shouldReportToSentry from '../../src/common/helpers/errors';
import { UserError } from '../../src/common/UserError';
import { Abort } from '../../src/common/helpers/abortableFetch';
describe('error handling', () => {
beforeEach(() => {});
it('should log random errors', () => {
const error = new Error();
expect(shouldReportToSentry(error)).toBe(true);
});
it('should log 500s', () => {
const error = new ApiError('Api Error');
error.status = 500;
expect(shouldReportToSentry(error)).toBe(true);
});
it('should not log 400s', () => {
const error = new ApiError('Api Error');
error.status = 400;
expect(shouldReportToSentry(error)).toBe(false);
});
it('should not log non errors', () => {
const error = {
message: 'hey, log me!',
code: 500,
};
expect(shouldReportToSentry(error)).toBe(false);
});
it('should not log Api errors', () => {
const error = new ApiError('Api Error');
error.status = 200;
expect(shouldReportToSentry(error)).toBe(false);
});
it('should not log User errors', () => {
const error = new UserError('Sorry');
expect(shouldReportToSentry(error)).toBe(false);
});
it('should not log Network errors', () => {
const error = new TypeError('Network request failed');
expect(shouldReportToSentry(error)).toBe(false);
});
it('should not log Abort errors', () => {
const error = new Abort('Aborted');
expect(shouldReportToSentry(error)).toBe(false);
});
it('should not log NaN errors', () => {
const error = new ApiError('Api Error');
error.status = 'error';
expect(shouldReportToSentry(error)).toBe(false);
});
});
......@@ -2214,7 +2214,7 @@ exports[`notification component renders correctly for every type 24`] = `
}
>
<Text>
[missing "en.notifications.welcomeChat" translation]
Chat securely with your mutual subscriptions.
</Text>
</View>
<Text
......
......@@ -10,7 +10,7 @@ exports[`renders correctly 1`] = `
}
>
<Text>
[missing "en.notifications.welcomeChat" translation]
Chat securely with your mutual subscriptions.
</Text>
</View>
`;
......@@ -72,7 +72,7 @@ exports[`renders correctly for owner and subscribed 1`] = `
<Text
onPress={[Function]}
>
[missing "en.notification.wiredSubscribedYou" translation]
You have subscribed to wire 1000/month to
<Text
onPress={[Function]}
......
......@@ -30,7 +30,8 @@ exports[`WithdrawScreen renders correctly 1`] = `
OnChain
</Text>
wallet.
<Text
style={
Object {
......@@ -51,7 +52,6 @@ exports[`WithdrawScreen renders correctly 1`] = `
and may take up to 72 hours to complete
</Text>
</Text>
<View
style={
......@@ -168,7 +168,8 @@ exports[`WithdrawScreen renders correctly 1`] = `
OnChain
</Text>
wallet.
<Text
style={
Object {
......@@ -189,7 +190,6 @@ exports[`WithdrawScreen renders correctly 1`] = `
and may take up to 72 hours to complete
</Text>
</Text>
<View
style={
......
......@@ -26,10 +26,10 @@ org.gradle.jvmargs=-Xmx2048m
systemProp.org.gradle.internal.http.connectionTimeout=180000
systemProp.org.gradle.internal.http.socketTimeout=180000
versionName=3.12.1
versionName=3.14.0
# CUSTOM
versionCode=1050000017
versionCode=1050000018
# PLAY STORE
# versionCode=310035
# versionCode=310036
......@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.12.1</string>
<string>3.14.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
......
......@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>3.12.1</string>
<string>3.14.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
......
......@@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.12.1</string>
<string>3.14.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
......
......@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>3.12.1</string>
<string>3.14.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -8,6 +8,7 @@
"start": "react-native start",
"lint": "eslint .",
"validate-locales": "node tasks/validate-locales.js",
"unused-locales": "ts-node tasks/unused-locales.js",
"update-settings": "ts-node tasks/update-settings.js",
"test": "jest",
"locale": "ts-node tasks/poeditor.js",
......
......@@ -17,6 +17,7 @@ import FastImage from 'react-native-fast-image';
import formatDate from '../common/helpers/date';
import { CommonStyle as CS} from '../styles/Common';
import { FLAG_VIEW } from '../common/Permissions';
import Actions from '../newsfeed/activity/Actions';
/**
* Blog Card
......@@ -57,6 +58,10 @@ export default class BlogCard extends PureComponent {
</View>
</View>
</View>
<Actions
entity={blog}
navigation={this.props.navigation}
/>
</TouchableOpacity>
)
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -108,7 +108,7 @@ export default class UserModel extends BaseModel {
/**
* current user is owner of the channel
*/
isOwner() {
isOwner = () => {
return sessionService.getUser().guid === this.guid;
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.