...
 
Commits (27)
......@@ -39,7 +39,9 @@ if (process.env.JEST_WORKER_ID === undefined) {
return null;
}
// only log api 500 errors
if (isApiError(hint.originalException) && hint.originalException.status < 500) {
if (isApiError(hint.originalException) &&
(isNaN(hint.originalException.status) || hint.originalException.status < 500)
) {
return null;
}
}
......
......@@ -29,6 +29,7 @@ exports[`Activity component renders correctly 1`] = `
"description": "Congratulations! ",
"edited": "",
"guid": "activityguid0",
"isOwner": [Function],
"is_visible": true,
"mature": false,
"mature_visibility": false,
......@@ -36,6 +37,7 @@ exports[`Activity component renders correctly 1`] = `
"ownerObj": UserModel {
"__list": null,
"guid": "824853017709780997",
"isOwner": [Function],
"subtype": false,
"time_created": "1522036284",
"type": "user",
......@@ -87,6 +89,7 @@ exports[`Activity component renders correctly 1`] = `
"description": "Congratulations! ",
"edited": "",
"guid": "activityguid0",
"isOwner": [Function],
"is_visible": true,
"mature": false,
"mature_visibility": false,
......@@ -94,6 +97,7 @@ exports[`Activity component renders correctly 1`] = `
"ownerObj": UserModel {
"__list": null,
"guid": "824853017709780997",
"isOwner": [Function],
"subtype": false,
"time_created": "1522036284",
"type": "user",
......@@ -161,6 +165,7 @@ exports[`Activity component renders correctly 1`] = `
"description": "Congratulations! ",
"edited": "",
"guid": "activityguid0",
"isOwner": [Function],
"is_visible": true,
"mature": false,
"mature_visibility": false,
......@@ -168,6 +173,7 @@ exports[`Activity component renders correctly 1`] = `
"ownerObj": UserModel {
"__list": null,
"guid": "824853017709780997",
"isOwner": [Function],
"subtype": false,
"time_created": "1522036284",
"type": "user",
......@@ -263,6 +269,7 @@ exports[`Activity component renders correctly 1`] = `
"description": "Congratulations! ",
"edited": "",
"guid": "activityguid0",
"isOwner": [Function],
"is_visible": true,
"mature": false,
"mature_visibility": false,
......@@ -270,6 +277,7 @@ exports[`Activity component renders correctly 1`] = `
"ownerObj": UserModel {
"__list": null,
"guid": "824853017709780997",
"isOwner": [Function],
"subtype": false,
"time_created": "1522036284",
"type": "user",
......@@ -332,6 +340,7 @@ exports[`Activity component renders correctly 1`] = `
"description": "Congratulations! ",
"edited": "",
"guid": "activityguid0",
"isOwner": [Function],
"is_visible": true,
"mature": false,
"mature_visibility": false,
......@@ -339,6 +348,7 @@ exports[`Activity component renders correctly 1`] = `
"ownerObj": UserModel {
"__list": null,
"guid": "824853017709780997",
"isOwner": [Function],
"subtype": false,
"time_created": "1522036284",
"type": "user",
......@@ -397,6 +407,7 @@ exports[`Activity component renders correctly 1`] = `
"description": "Congratulations! ",
"edited": "",
"guid": "activityguid0",
"isOwner": [Function],
"is_visible": true,
"mature": false,
"mature_visibility": false,
......@@ -404,6 +415,7 @@ exports[`Activity component renders correctly 1`] = `
"ownerObj": UserModel {
"__list": null,
"guid": "824853017709780997",
"isOwner": [Function],
"subtype": false,
"time_created": "1522036284",
"type": "user",
......@@ -468,6 +480,7 @@ exports[`Activity component renders correctly 1`] = `
"description": "Congratulations! ",
"edited": "",
"guid": "activityguid0",
"isOwner": [Function],
"is_visible": true,
"mature": false,
"mature_visibility": false,
......@@ -475,6 +488,7 @@ exports[`Activity component renders correctly 1`] = `
"ownerObj": UserModel {
"__list": null,
"guid": "824853017709780997",
"isOwner": [Function],
"subtype": false,
"time_created": "1522036284",
"type": "user",
......@@ -532,6 +546,7 @@ exports[`Activity component renders correctly 1`] = `
"description": "Congratulations! ",
"edited": "",
"guid": "activityguid0",
"isOwner": [Function],
"is_visible": true,
"mature": false,
"mature_visibility": false,
......@@ -539,6 +554,7 @@ exports[`Activity component renders correctly 1`] = `
"ownerObj": UserModel {
"__list": null,
"guid": "824853017709780997",
"isOwner": [Function],
"subtype": false,
"time_created": "1522036284",
"type": "user",
......
......@@ -30,6 +30,7 @@ exports[`Activity screen component renders correctly with an entity as param 2`]
"description": "Congratulations! ",
"edited": "",
"guid": "activityguid0",
"isOwner": [Function],
"is_visible": true,
"mature": false,
"mature_visibility": false,
......@@ -37,6 +38,7 @@ exports[`Activity screen component renders correctly with an entity as param 2`]
"ownerObj": UserModel {
"__list": null,
"guid": "824853017709780997",
"isOwner": [Function],
"subtype": false,
"time_created": "1522036284",
"type": "user",
......@@ -79,6 +81,7 @@ exports[`Activity screen component renders correctly with an entity as param 2`]
"description": "Congratulations! ",
"edited": "",
"guid": "activityguid0",
"isOwner": [Function],
"is_visible": true,
"mature": false,
"mature_visibility": false,
......@@ -86,6 +89,7 @@ exports[`Activity screen component renders correctly with an entity as param 2`]
"ownerObj": UserModel {
"__list": null,
"guid": "824853017709780997",
"isOwner": [Function],
"subtype": false,
"time_created": "1522036284",
"type": "user",
......
......@@ -100,6 +100,22 @@ describe('auth service logout', () => {
expect(session.logout.mock.calls.length).toEqual(1);
});
it('should clear cookies on logout', async () => {
api.post.mockResolvedValue(true);
const res = await authService.logout();
// assert on the response
expect(res).toEqual(true);
// call session logout one time
expect(session.logout.mock.calls.length).toBe(1);
// should clear cookies
expect(api.clearCookies).toBeCalled();
});
it('logout returns errors', async () => {
const response = {status: 'error', error: 'some error'};
......
......@@ -37,6 +37,7 @@ exports[`blog view screen component should renders correctly 1`] = `
"header_bg": "1",
"header_top": "0",
"impressions": 100,
"isOwner": [Function],
"last_save": "1524843907",
"last_updated": "1524838665",
"license": "attribution-noncommercial-cc",
......@@ -79,6 +80,7 @@ exports[`blog view screen component should renders correctly 1`] = `
"guid": 100,
"icontime": "1523515420",
"impressions": 100,
"isOwner": [Function],
"language": "en",
"legacy_guid": false,
"mature": "1",
......@@ -228,6 +230,7 @@ exports[`blog view screen component should renders correctly 1`] = `
"header_bg": "1",
"header_top": "0",
"impressions": 100,
"isOwner": [Function],
"last_save": "1524843907",
"last_updated": "1524838665",
"license": "attribution-noncommercial-cc",
......@@ -270,6 +273,7 @@ exports[`blog view screen component should renders correctly 1`] = `
"guid": 100,
"icontime": "1523515420",
"impressions": 100,
"isOwner": [Function],
"language": "en",
"legacy_guid": false,
"mature": "1",
......@@ -402,6 +406,7 @@ exports[`blog view screen component should renders correctly 1`] = `
"header_bg": "1",
"header_top": "0",
"impressions": 100,
"isOwner": [Function],
"last_save": "1524843907",
"last_updated": "1524838665",
"license": "attribution-noncommercial-cc",
......@@ -444,6 +449,7 @@ exports[`blog view screen component should renders correctly 1`] = `
"guid": 100,
"icontime": "1523515420",
"impressions": 100,
"isOwner": [Function],
"language": "en",
"legacy_guid": false,
"mature": "1",
......@@ -555,6 +561,7 @@ exports[`blog view screen component should renders correctly 1`] = `
"header_bg": "1",
"header_top": "0",
"impressions": 100,
"isOwner": [Function],
"last_save": "1524843907",
"last_updated": "1524838665",
"license": "attribution-noncommercial-cc",
......@@ -597,6 +604,7 @@ exports[`blog view screen component should renders correctly 1`] = `
"guid": 100,
"icontime": "1523515420",
"impressions": 100,
"isOwner": [Function],
"language": "en",
"legacy_guid": false,
"mature": "1",
......@@ -699,6 +707,7 @@ exports[`blog view screen component should renders correctly 1`] = `
"header_bg": "1",
"header_top": "0",
"impressions": 100,
"isOwner": [Function],
"last_save": "1524843907",
"last_updated": "1524838665",
"license": "attribution-noncommercial-cc",
......@@ -741,6 +750,7 @@ exports[`blog view screen component should renders correctly 1`] = `
"guid": 100,
"icontime": "1523515420",
"impressions": 100,
"isOwner": [Function],
"language": "en",
"legacy_guid": false,
"mature": "1",
......
......@@ -94,6 +94,7 @@ exports[`Channel screen component should renders correctly 1`] = `
"guid": 1,
"icontime": "1523515420",
"impressions": 100,
"isOwner": [Function],
"language": "en",
"legacy_guid": false,
"mature": "1",
......@@ -358,6 +359,7 @@ exports[`Channel screen component should renders correctly 1`] = `
"guid": 1,
"icontime": "1523515420",
"impressions": 100,
"isOwner": [Function],
"language": "en",
"legacy_guid": false,
"mature": "1",
......@@ -618,6 +620,7 @@ exports[`Channel screen component should show closed channel message 1`] = `
"guid": 1,
"icontime": "1523515420",
"impressions": 100,
"isOwner": [Function],
"language": "en",
"legacy_guid": false,
"mature": "1",
......@@ -903,6 +906,7 @@ exports[`Channel screen component should show closed channel message 1`] = `
"guid": 1,
"icontime": "1523515420",
"impressions": 100,
"isOwner": [Function],
"language": "en",
"legacy_guid": false,
"mature": "1",
......
......@@ -208,7 +208,7 @@ exports[`channel header component owner should render correctly 1`] = `
}
>
<View
accessibilityLabel="Edit your channel settings"
accessibilityLabel="Subscribe to this channel"
accessible={true}
focusable={true}
isTVSelectable={true}
......@@ -246,7 +246,56 @@ exports[`channel header component owner should render correctly 1`] = `
}
>
Edit
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
</Text>
</View>
......
import videoPlayerService from '../../../src/common/services/video-player.service';
const mockPlayerRef1 = {
pause: jest.fn()
}
const mockPlayerRef2 = {
pause: jest.fn()
}
/**
* Tests
*/
describe('Video player service', () => {
beforeEach(() => {
mockPlayerRef1.pause.mockClear();
mockPlayerRef2.pause.mockClear();
});
it('should set the current ref', () => {
expect(videoPlayerService.current).toBe(null);
videoPlayerService.setCurrent(mockPlayerRef1);
expect(videoPlayerService.current).toBe(mockPlayerRef1);
});
it('should pause the previous video', () => {
videoPlayerService.setCurrent(mockPlayerRef2);
expect(videoPlayerService.current).toBe(mockPlayerRef2);
expect(mockPlayerRef1.pause).toBeCalled();
});
it('should clear the ref', () => {
videoPlayerService.clear();
expect(videoPlayerService.current).toBe(null);
});
});
\ No newline at end of file
......@@ -80,6 +80,7 @@ project.ext.react = [
]
apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/@sentry/react-native/sentry.gradle"
/**
* Set this to true to create two separate APKs instead of one:
......
......@@ -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.0
versionName=3.12.1
# CUSTOM
versionCode=1050000016
versionCode=1050000017
# PLAY STORE
# versionCode=310034
# versionCode=310035
......@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.12.0</string>
<string>3.12.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.12.0</string>
<string>3.12.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
......
......@@ -5,7 +5,6 @@
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
00E356F31AD99517003FC87E /* MindsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* MindsTests.m */; };
0EA47AA13B1C4DFC8DCD3912 /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BFA6449C294D4B2FA6E84139 /* Roboto-Regular.ttf */; };
......@@ -32,6 +31,7 @@
FD04EC43AFE79D3DE060DD43 /* libPods-Minds-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 728C16C16413E9B0E10971C9 /* libPods-Minds-tvOS.a */; };
FE6E8EAA158246D09E0FEA9D /* Roboto-MediumItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2CD2622D204E4B0286943196 /* Roboto-MediumItalic.ttf */; };
FFEB1A966BF04513973FCA99 /* Roboto-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 73818081AF2E4726B1E7FBBC /* Roboto-LightItalic.ttf */; };
B080D11FCFB44B31B8AA0E13 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 37DF9BB306F046229D6C90DA /* libz.tbd */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
......@@ -92,6 +92,7 @@
E0F0DBA3E9FE7A03AF5DCCC7 /* Pods-Minds.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds.debug.xcconfig"; path = "Target Support Files/Pods-Minds/Pods-Minds.debug.xcconfig"; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
37DF9BB306F046229D6C90DA /* libz.tbd */ = {isa = PBXFileReference; name = "libz.tbd"; path = "usr/lib/libz.tbd"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = sourcecode.text-based-dylib-definition; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
......@@ -108,6 +109,7 @@
buildActionMask = 2147483647;
files = (
4DBD76CA42C18451868A4BD6 /* libPods-Minds.a in Frameworks */,
B080D11FCFB44B31B8AA0E13 /* libz.tbd in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -171,6 +173,7 @@
728C16C16413E9B0E10971C9 /* libPods-Minds-tvOS.a */,
CB3378F9EB6781ED5A558E01 /* libPods-Minds-tvOSTests.a */,
22BFDDB4D04FFCBEAEDF3516 /* libPods-MindsTests.a */,
37DF9BB306F046229D6C90DA /* libz.tbd */,
);
name = Frameworks;
sourceTree = "<group>";
......@@ -243,6 +246,14 @@
path = Pods;
sourceTree = "<group>";
};
B05B1CF8A35D4DBAB32376F4 /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
path = Application;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
......@@ -277,6 +288,7 @@
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
5D4283786B433FEB77EB2907 /* [CP] Embed Pods Frameworks */,
521352FB382A6F4552BD3ED7 /* [CP] Copy Pods Resources */,
DF23D9AE37694D50863E721D /* Upload Debug Symbols to Sentry */,
);
buildRules = (
);
......@@ -440,7 +452,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
shellScript = "export SENTRY_PROPERTIES=sentry.properties\nexport NODE_BINARY=node\n../node_modules/@sentry/cli/bin/sentry-cli react-native xcode ../node_modules/react-native/scripts/react-native-xcode.sh";
};
296731C110F3A1661160366F /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
......@@ -476,7 +488,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
shellScript = "export SENTRY_PROPERTIES=sentry.properties\nexport NODE_BINARY=node\n../node_modules/@sentry/cli/bin/sentry-cli react-native xcode ../node_modules/react-native/scripts/react-native-xcode.sh";
};
368FB13BAB274FAAEBBB6F32 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
......@@ -650,6 +662,20 @@
shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n";
showEnvVarsInLog = 0;
};
DF23D9AE37694D50863E721D /* Upload Debug Symbols to Sentry */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
name = "Upload Debug Symbols to Sentry";
inputPaths = (
);
outputPaths = (
);
shellPath = /bin/sh;
shellScript = "export SENTRY_PROPERTIES=sentry.properties\n../node_modules/@sentry/cli/bin/sentry-cli upload-dsym";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
......@@ -766,7 +792,7 @@
CODE_SIGN_ENTITLEMENTS = Minds/Minds.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 201907230162;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = 35U3998VRZ;
ENABLE_BITCODE = NO;
......@@ -793,7 +819,7 @@
CODE_SIGN_ENTITLEMENTS = Minds/Minds.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 201907230162;
DEVELOPMENT_TEAM = 35U3998VRZ;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Minds/Info.plist;
......
......@@ -9,7 +9,7 @@
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleGetInfoString</key>
<string/>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
......@@ -19,11 +19,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.12.0</string>
<string>3.12.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
......@@ -42,7 +40,7 @@
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
<string/>
<string></string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
......@@ -66,7 +64,7 @@
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to use your camera in order to upload images or videos</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string/>
<string></string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to your microphone in order to record videos </string>
<key>NSPhotoLibraryAddUsageDescription</key>
......@@ -125,6 +123,8 @@
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
</array>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
......
......@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>3.12.0</string>
<string>3.12.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
......
......@@ -35,6 +35,8 @@ target 'Minds' do
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
pod 'RNSentry', :path => '../node_modules/@sentry/react-native'
target 'MindsTests' do
inherit! :search_paths
# Pods for testing
......
......@@ -28,6 +28,10 @@ class AuthService {
try {
let resp = await api.delete('api/v2/oauth/token');
session.logout();
// Fixes autosubscribe issue on register
await api.clearCookies();
return true;
} catch (err) {
logService.exception('[AuthService] logout', err);
......
......@@ -8,9 +8,7 @@ import {
View,
Text,
// TextInput,
StyleSheet,
KeyboardAvoidingView,
Platform,
} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
......@@ -32,7 +30,9 @@ import TextInput from '../common/components/TextInput';
* Login Form
*/
export default class LoginForm extends Component {
/**
* State
*/
state = {
username: '',
password: '',
......@@ -44,13 +44,14 @@ export default class LoginForm extends Component {
showLanguages: false,
};
componentWillMount() {
this.setState({
language: i18n.getCurrentLocale()
});
/**
* Constructor
*/
constructor(props) {
super(props);
this.state.language = i18n.getCurrentLocale();
}
/**
* Render
*/
......@@ -90,9 +91,12 @@ export default class LoginForm extends Component {
);
}
/**
* Show languages
*/
showLanguages = () => {
this.setState({showLanguages: true});
}
};
/**
* Language selected
......@@ -100,12 +104,18 @@ export default class LoginForm extends Component {
languageSelected = (language) => {
this.setState({language, showLanguages: false});
i18n.setLocale(language);
}
};
/**
* Cancel language selection
*/
cancel = () => {
this.setState({showLanguages: false});
}
};
/**
* Returns the buttons
*/
getButtons() {
const buttons = [
<Button
......@@ -122,7 +132,7 @@ export default class LoginForm extends Component {
disabledStyle={CommonStyle.backgroundTransparent}
testID="loginButton"
/>
]
];
if (!this.state.twoFactorToken) {
buttons.unshift(
......@@ -141,6 +151,9 @@ export default class LoginForm extends Component {
return buttons;
}
/**
* Return the inputs for the form
*/
getInputs() {
if (this.state.twoFactorToken) {
return (
......@@ -150,7 +163,7 @@ export default class LoginForm extends Component {
returnKeyType={'done'}
placeholderTextColor="#444"
underlineColorAndroid='transparent'
onChangeText={(value) => this.setState({ twoFactorCode: value })}
onChangeText={this.setTwoFactor}
autoCapitalize={'none'}
value={this.state.twoFactorCode}
/>
......@@ -163,9 +176,9 @@ export default class LoginForm extends Component {
returnKeyType={'done'}
placeholderTextColor="#444"
underlineColorAndroid='transparent'
onChangeText={(value) => this.setState({ username: value })}
onChangeText={this.setUsername}
autoCapitalize={'none'}
value={this.state.username.trim()}
value={this.state.username}
key={1}
testID="usernameInput"
/>,
......@@ -178,7 +191,7 @@ export default class LoginForm extends Component {
returnKeyType={'done'}
placeholderTextColor="#444"
underlineColorAndroid='transparent'
onChangeText={(value) => this.setState({ password: value })}
onChangeText={this.setPassword}
value={this.state.password}
testID="userPasswordInput"
/>
......@@ -193,13 +206,47 @@ export default class LoginForm extends Component {
}
}
/**
* Set two factor
* @param {string} value
*/
setTwoFactor = value => {
const twoFactorCode = String(value).trim();
this.setState({twoFactorCode});
};
/**
* Set two factor
* @param {string} value
*/
setUsername = value => {
const username = String(value).trim();
this.setState({username});
};
/**
* Set two factor
* @param {string} value
*/
setPassword = value => {
const password = String(value).trim();
this.setState({password});
};
/**
* Set two factor
* @param {string} value
*/
toggleHidePassword = () => {
this.setState({hidePassword: !this.state.hidePassword});
}
};
/**
* Handle forgot password
*/
onForgotPress = () => {
this.props.onForgot()
}
this.props.onForgot();
};
/**
* On login press
......
......@@ -153,9 +153,12 @@ export default class BlogViewHTML extends Component {
}
if (html.indexOf('<iframe') >= 0) {
html = html.replace('<iframe', '<div class="iframewrapper"><iframe');
html = html.replace('</iframe>', '</iframe></div>');
html = html.replace('src="//', 'src="https://');
const iframeOpen = new RegExp(/\<iframe/g);
const iframeClose = new RegExp(/\<\/iframe\>/g);
const badSrc = new RegExp(/src=\"\/\//g);
html = html.replace(iframeOpen, '<div class="iframewrapper"><iframe');
html = html.replace(iframeClose, '</iframe></div>');
html = html.replace(badSrc, 'src="https://');
}
return `<!DOCTYPE html><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
......
......@@ -135,6 +135,7 @@ class Comment extends Component {
</View>
<CommentActionSheet
entity={this.props.entity}
comment={this.props.comment}
onSelection={this.onSelection}
ref={this.actionSheetRef}
......
......@@ -90,6 +90,8 @@ export default class CommentActionSheet extends Component {
} else {
actions.push( i18n.t('removeExplicit') )
}
} else if (this.props.entity.isOwner()) {
actions.push( i18n.t('delete') );
}
actions.push( i18n.t('report') );
......
......@@ -81,6 +81,11 @@ export default class BaseModel {
constructor(data) {
Object.assign(this, data);
// Some users have a number as username and engine return them as a number
if (this.username) {
this.username = this.username.toString();
}
// create childs instances
const childs = this.childModels()
for (var prop in childs) {
......@@ -90,6 +95,13 @@ export default class BaseModel {
}
}
/**
* Return if the current user is the owner of the activity
*/
isOwner = () => {
return this.ownerObj && sessionService.guid === this.ownerObj.guid;
}
/**
* Update model data
* @param {Object} data
......
......@@ -8,6 +8,8 @@ import abortableFetch from '../helpers/abortableFetch';
import { Version } from '../../config/Version';
import logService from './log.service';
import * as Sentry from '@sentry/react-native';
/**
* Api Error
*/
......@@ -29,6 +31,16 @@ export const isApiForbidden = function(err) {
* Api service
*/
class ApiService {
async parseJSON(response) {
try {
return await response.json();
} catch (error) {
Sentry.captureMessage(`ISSUE #1572 URL: ${response.url}, STATUS: ${response.status} STATUSTEXT: ${response.statusText}`);
throw error;
}
}
/**
* Clear cookies
*/
......@@ -113,7 +125,7 @@ class ApiService {
}
// Convert from JSON
const data = await response.json();
const data = await this.parseJSON(response);
// Failed on API side
if (data.status != 'success') {
......@@ -144,7 +156,7 @@ class ApiService {
}
// Convert from JSON
const data = await response.json();
const data = await this.parseJSON(response);
// Failed on API side
if (data.status != 'success') {
......@@ -175,7 +187,7 @@ class ApiService {
}
// Convert from JSON
const data = await response.json();
const data = await this.parseJSON(response);
// Failed on API side
if (data.status === 'error') {
......@@ -251,7 +263,7 @@ class ApiService {
}
// Convert from JSON
const data = await response.json();
const data = await this.parseJSON(response);
// Failed on API side
if (data.status === 'error') {
......
......@@ -4,6 +4,11 @@ import api from './../../common/services/api.service';
* Gathering service
*/
class GatheringService {
keepAliveInterval = null;
get isActive() {
return this.keepAliveInterval !== null;
}
/**
* Start keep alive pooling
*/
......@@ -16,6 +21,7 @@ class GatheringService {
*/
stopKeepAlive() {
clearInterval(this.keepAliveInterval);
this.keepAliveInterval = null;
}
/**
......
/**
* Video Player Service
*/
class VideoPlayerService {
/**
* current playing video player reference
*/
current = null;
/**
* Set current player reference
* @param {MindsVideo} videoPlayerRef
*/
setCurrent(videoPlayerRef) {
if (this.current && this.current !== videoPlayerRef) {
this.current.pause();
}
this.current = videoPlayerRef;
}
/**
* Clear the current player ref
*/
clear() {
this.current = null;
}
}
export default new VideoPlayerService();
......@@ -433,6 +433,14 @@ export default class FeedStore {
return this;
}
/**
* Reset store and service data
*/
reset() {
this.clear();
this.feedsService.clear();
}
/**
* Get channel scheduled activities count
*/
......
......@@ -121,6 +121,10 @@ class DiscoveryStore {
*/
@action
reload() {
// ignore reload for latest channels
if (this.filters.type === 'lastchannels') {
return;
}
this.listStore.clear();
this.fetch();
}
......
import React from 'react';
import { View } from 'react-native';
import { View, BackHandler } from 'react-native';
import JitsiMeet, { JitsiMeetView } from 'react-native-jitsi-meet';
import { CommonStyle } from '../styles/Common';
import sessionService from '../common/services/session.service';
......@@ -13,34 +13,63 @@ class Gathering extends React.Component {
* Remove navigation header
*/
static navigationOptions = {
header: null
header: null,
};
/**
* Constructor
*/
constructor(props) {
super(props);
// we disable the back button until the video call is started
// to prevent an inconsistent behavior
this.backHandler = BackHandler.addEventListener(
'hardwareBackPress',
() => true,
);
}
/**
* Component did mount
*/
componentDidMount() {
const entity = this.props.navigation.getParam('entity');
setTimeout(async () => {
const url = await gatheringService.getRoomName(entity);
const user = sessionService.getUser();
const avatar = user.getAvatarSource().uri;
this.init();
}
JitsiMeet.callWithUserInfo(url, avatar, user.name, entity.name);
}, 1000);
/**
* Init gathering
*/
async init() {
if (!gatheringService.isActive) {
const entity = this.props.navigation.getParam('entity');
this.timer = setTimeout(async () => {
const url = await gatheringService.getRoomName(entity);
const user = sessionService.getUser();
const avatar = user.getAvatarSource().uri;
JitsiMeet.callWithUserInfo(url, avatar, user.name, entity.name);
}, 300);
}
}
/**
* Component will unmount
*/
componentWillUnmount() {
if (this.backHandler) {
this.backHandler.remove();
this.backHandler = null;
}
if (this.timer) {
clearTimeout(this.timer);
}
JitsiMeet.endCall();
}
/**
* On conference terminated
*/
onConferenceTerminated = nativeEvent => {
onConferenceTerminated = event => {
gatheringService.stopKeepAlive();
this.props.navigation.goBack();
};
......@@ -48,16 +77,26 @@ class Gathering extends React.Component {
/**
* On conference joined
*/
onConferenceJoined = nativeEvent => {
onConferenceJoined = event => {
gatheringService.startKeepAlive();
if (this.backHandler) {
this.backHandler.remove();
this.backHandler = null;
// the back button should end the call instead of return to previous screen
this.backHandler = BackHandler.addEventListener(
'hardwareBackPress',
() => {
JitsiMeet.endCall();
return true;
},
);
}
};
/**
* On conference will join
*/
onConferenceWillJoin = nativeEvent => {
/* Conference will join event */
};
onConferenceWillJoin = event => {};
/**
* Render
......
......@@ -29,6 +29,7 @@ import ExplicitImage from '../common/components/explicit/ExplicitImage';
import logService from '../common/services/log.service';
import i18n from '../common/services/i18n.service';
import attachmentService from '../common/services/attachment.service';
import videoPlayerService from '../common/services/video-player.service';
const isIOS = Platform.OS === 'ios';
......@@ -89,6 +90,9 @@ class MindsVideo extends Component {
*/
componentWillUnmount() {
this.onScreenBlur.remove();
if (videoPlayerService.current === this) {
videoPlayerService.clear();
}
}
onVideoEnd = () => {
......@@ -106,13 +110,13 @@ class MindsVideo extends Component {
}
this.setState({loaded: false, currentTime: current, duration: e.duration});
this.player.seek(current)
this.player.seek(current);
this.onLoadEnd();
}
onLoadStart = () => {
this.setState({ error: false, inProgress: true, });
this.setState({error: false, inProgress: true});
};
onError = async err => {
......@@ -123,31 +127,31 @@ class MindsVideo extends Component {
this.setState({transcoding: true});
} else {
logService.exception('[MindsVideo]', new Error(err));
this.setState({ error: true, inProgress: false, });
this.setState({error: true, inProgress: false});
}
} catch (error) {
logService.exception('[MindsVideo]', new Error(error));
this.setState({ error: true, inProgress: false, });
this.setState({error: true, inProgress: false});
}
};
onLoadEnd = () => {
this.setState({ error: false, inProgress: false, });
this.setState({error: false, inProgress: false});
};
toggleVolume = () => {
const v = this.state.volume ? 0 : 1;
this.setState({volume: v});
}
};
onProgress = (e) => {
onProgress = e => {
this.setState({currentTime: e.currentTime});
}
};
onBackward(currentTime) {
let newTime = Math.max(currentTime - FORWARD_DURATION, 0);
this.player.seek(newTime);
this.setState({currentTime: newTime})
this.setState({currentTime: newTime});
}
onForward(currentTime, duration) {
......@@ -180,15 +184,13 @@ class MindsVideo extends Component {
}
play = () => {
this.setState({
showOverlay: false,
});
videoPlayerService.setCurrent(this);
this.setState({
active: true,
showOverlay: false,
paused: false,
});
}
};
pause = () => {
this.setState({
......@@ -247,6 +249,13 @@ class MindsVideo extends Component {
}
}
/**
* Set the reference to the video player
*/
setRef = (ref) => {
this.player = ref;
};
/**
* Get video component or thumb
*/
......@@ -257,9 +266,7 @@ class MindsVideo extends Component {
if (this.state.active || !thumb_uri) {
return (
<Video
ref={(ref) => {
this.player = ref
}}
ref={this.setRef}
volume={parseFloat(this.state.volume)}
onEnd={this.onVideoEnd}
onLoadStart={this.onLoadStart}
......
......@@ -38,11 +38,6 @@ export default class ConversationView extends Component {
let unread = item.unread ? <Icon style={styles.icons} name='md-notifications' color='#4caf50' size={19} /> : null;
let online = item.online ? <Icon style={styles.icons} name='md-radio-button-on' color='#2196f3' size={19} /> : null;
// Added to capture information about /issues/1203549247/?project=1538735
if (item.username && !item.username.toUpperCase) {
Sentry.captureMessage('ISSUE 1203549247 No username on ' + item.guid + ' name: ' + item.name);
}
return (
<TouchableOpacity style={styles.row} onPress={this._navToConversation}>
<Image source={avatarImg} style={styles.avatar} />
......
......@@ -104,13 +104,6 @@ export default class ActivityModel extends BaseModel {
FastImage.preload([this.getThumbSource(size)]);
}
/**
* Return if the current user is the owner of the activity
*/
isOwner() {
return sessionService.guid == this.ownerObj.guid;
}
shouldBeBlured() {
const user = sessionService.getUser();
......
......@@ -39,10 +39,6 @@ class NewsfeedStore {
*/
constructor() {
this.buildStores();
this.feedStore
.setEndpoint(`api/v2/feeds/subscribed/activities`)
.setInjectBoost(true)
.setLimit(12);
}
/**
......@@ -68,6 +64,11 @@ class NewsfeedStore {
this.list.getMetadataService()
.setSource('feed/boosts')
.setMedium('featured-content');
this.feedStore
.setEndpoint('api/v2/feeds/subscribed/activities')
.setInjectBoost(true)
.setLimit(12);
}
/**
......@@ -167,12 +168,12 @@ class NewsfeedStore {
@action
reset() {
this.feedStore.reset();
this.buildStores();
this.filter = 'subscribed';
this.boosts = [];
this.loading = false;
this.loadingBoost = false;
this.feedStore.clear();
}
}
......