Skip to content

Commit

Permalink
Merge pull request #38 from shoutem/release/2.3.0
Browse files Browse the repository at this point in the history
Release/2.3.0
  • Loading branch information
Definitely-Not-Vlad authored Apr 30, 2020
2 parents bdc175e + bf7b1e9 commit 1559db2
Show file tree
Hide file tree
Showing 289 changed files with 3,390 additions and 2,292 deletions.
2 changes: 1 addition & 1 deletion shoutem.about/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "shoutem.about",
"version": "2.2.2",
"version": "2.3.0",
"description": "Shoutem Extension with basic information about business",
"dependencies": {
"@shoutem/redux-io": "3.0.1-beta.3",
Expand Down
4 changes: 2 additions & 2 deletions shoutem.about/extension.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "about",
"version": "2.2.2",
"platform": "2.2.*",
"version": "2.3.0",
"platform": "2.3.*",
"title": "About",
"description": "Show info about your app or your business",
"icon": "server/assets/add-about-image.png",
Expand Down
2 changes: 1 addition & 1 deletion shoutem.about/server/package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"name": "shoutem.about",
"version": "2.2.2"
"version": "2.3.0"
}
2 changes: 1 addition & 1 deletion shoutem.analytics/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "shoutem.analytics",
"version": "2.2.0",
"version": "2.3.0",
"private": true,
"main": "index.js"
}
4 changes: 2 additions & 2 deletions shoutem.analytics/extension.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "analytics",
"version": "2.2.0",
"platform": "2.2.*",
"version": "2.3.0",
"platform": "2.3.*",
"type": "system",
"title": "Analytics",
"description": "Shoutem analytics extension defines interface in a form of dispatched redux actions which can be used to track Shoutem events. Use middleware to intercept analytics actions and track events.",
Expand Down
2 changes: 1 addition & 1 deletion shoutem.analytics/server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "shoutem.analytics",
"version": "2.2.0",
"version": "2.3.0",
"private": true,
"main": "index.js"
}
4 changes: 0 additions & 4 deletions shoutem.application/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,6 @@ export function appDidMount(app) {
*/
export const isDevelopment = () => process.env.NODE_ENV === 'development';

export function appDidFinishLaunching(app) {
SplashScreen.hide();
}

export function appWillUnmount() {
AppState.removeEventListener('change', appStateChangeHandler);
}
2 changes: 1 addition & 1 deletion shoutem.application/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "shoutem.application",
"version": "2.2.4",
"version": "2.3.0",
"private": true,
"main": "index.js",
"dependencies": {
Expand Down
7 changes: 4 additions & 3 deletions shoutem.application/app/screens/SubscriptionMissingScreen.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React from 'react';

import { I18n } from 'shoutem.i18n';

import { EmptyStateView } from '@shoutem/ui';

const NO_SUBSCRIPTION_MESSAGE = 'This app is currently inactive. Please contact its developer for support!';
import { ext } from '../const';

export default function SubscriptionMissingScreen() {
return (
<EmptyStateView icon="error" message={NO_SUBSCRIPTION_MESSAGE} styleName="wide-subtitle" />
<EmptyStateView icon="error" message={I18n.t(ext("noSubscriptionMessage"))} styleName="wide-subtitle" />
);
}

8 changes: 7 additions & 1 deletion shoutem.application/app/shared/openInitialScreen.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { navigateTo } from 'shoutem.navigation';
import SplashScreen from 'react-native-splash-screen';

import { navigateTo, setNavigationInitialized } from 'shoutem.navigation';

import { executeShortcut } from '../redux';
import { ext } from '../const';
import { getFirstShortcut } from './getFirstShortcut';
Expand All @@ -9,6 +12,7 @@ export const openInitialScreen = (app, subscriptionValid) => {

if (subscriptionValid === false) {
store.dispatch(navigateTo({ screen: ext('SubscriptionMissingScreen') }));
SplashScreen.hide();
return;
}

Expand All @@ -17,5 +21,7 @@ export const openInitialScreen = (app, subscriptionValid) => {
// Initial navigation action has some constraints on the navigation actions,
// @see initialNavigationReducer in shoutem-core/navigation
store.dispatch(executeShortcut(firstShortcut.id));
store.dispatch(setNavigationInitialized());
SplashScreen.hide();
}
};
3 changes: 2 additions & 1 deletion shoutem.application/app/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"tryAgainButton": "TRY AGAIN",
"configurationLoadErrorMessage": "App configuration failed to load.",
"networkErrorTitle": "Network error",
"networkErrorMessage": "No internet connection, please check your network."
"networkErrorMessage": "No internet connection, please check your network.",
"noSubscriptionMessage": "This app is currently inactive. Please contact its developer for support!"
}
}
}
4 changes: 2 additions & 2 deletions shoutem.application/extension.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "application",
"version": "2.2.4",
"platform": "2.2.*",
"version": "2.3.0",
"platform": "2.3.*",
"type": "system",
"title": "Application",
"description": "System Shoutem extension that initializes and configures the application",
Expand Down
2 changes: 1 addition & 1 deletion shoutem.application/server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "shoutem.application",
"version": "2.2.4",
"version": "2.3.0",
"private": true,
"main": "index.js"
}
2 changes: 1 addition & 1 deletion shoutem.audio/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "shoutem.audio",
"version": "2.2.6",
"version": "2.3.0",
"description": "Track player components for other extensions to utilize.",
"dependencies": {
"@raydeck/xcode": "^2.2.0",
Expand Down
4 changes: 2 additions & 2 deletions shoutem.audio/extension.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "audio",
"title": "Audio",
"version": "2.2.6",
"version": "2.3.0",
"description": "Track player components for other extensions to utilize. NOTE: Lock screen controls will not be visible in the preview.",
"platform": "~2.2.2",
"platform": "2.3.*",
"icon": "server/assets/add-audio-image.png",
"type": "system"
}
2 changes: 1 addition & 1 deletion shoutem.audio/server/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "shoutem.audio",
"version": "2.2.6",
"version": "2.3.0",
"description": "Track player components for other extensions to utilize."
}
191 changes: 191 additions & 0 deletions shoutem.auth/app/components/AppleSignInButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import { Alert } from 'react-native';
import PropTypes from 'prop-types';
import _ from 'lodash';
import autoBind from 'auto-bind';
import appleAuth, {
AppleButton,
AppleAuthRequestOperation,
AppleAuthRequestScope,
AppleAuthCredentialState,
} from '@invertase/react-native-apple-authentication';
import { connectStyle } from '@shoutem/theme';
import { View, Spinner } from '@shoutem/ui';
import { getExtensionSettings, getAppId } from 'shoutem.application';
import { I18n } from 'shoutem.i18n';
import { getUser, loginWithApple, registerWithApple } from '../redux';
import * as Storage from '../services/storage';
import { resolveAppleErrorMessage } from '../services/apple';
import { getErrorCode, resolveErrorMessage } from '../errorMessages';
import { ext } from '../const';

const APPLE_AUTH_OPTIONS = {
requestedOperation: AppleAuthRequestOperation.LOGIN,
requestedScopes: [
AppleAuthRequestScope.EMAIL,
AppleAuthRequestScope.FULL_NAME,
],
};
const LOGIN_ERROR_TITLE = () => I18n.t(ext('loginFailedErrorTitle'));
const UNEXPECTED_ERROR = () =>
I18n.t('shoutem.application.unexpectedErrorMessage');

function firstNameStorageKey() {
return `app-${getAppId()}:${ext()}.firstName`;
}

function lastNameStorageKey() {
return `app-${getAppId()}:${ext()}.lastName`;
}

class AppleSignInButton extends PureComponent {
constructor(props) {
super(props);

autoBind(this);

this.state = {
idToken: null,
inProgress: false,
};
}

handleAppleButtonPress() {
this.setState({ inProgress: true });

return appleAuth
.performRequest(APPLE_AUTH_OPTIONS)
.then(this.handleAppleAuthRequest)
.catch(this.handleAppleAuthError);
}

handleAppleAuthRequest(results) {
const { user, identityToken, fullName } = results;

this.setState({ idToken: identityToken });

const firstName = _.get(fullName, 'givenName');
const lastName = _.get(fullName, 'familyName');

Storage.saveItem(firstNameStorageKey(), firstName);
Storage.saveItem(lastNameStorageKey(), lastName);

appleAuth
.getCredentialStateForUser(user)
.then(this.handleAppleLoginRequest)
.catch(this.handleAppleAuthError);
}

handleAppleLoginRequest(results) {
const { loginWithApple, onLoginSuccess } = this.props;
const { idToken } = this.state;

if (results === AppleAuthCredentialState.AUTHORIZED) {
return loginWithApple(idToken)
.then(onLoginSuccess)
.catch(this.handleAppleLoginFailed);
}

return Alert.alert(LOGIN_ERROR_TITLE(), UNEXPECTED_ERROR());
}

handleAppleAuthError(error) {
this.setState({ inProgress: false });

const errorCode = _.get(error, 'code');
const resolvedMessage = resolveAppleErrorMessage(errorCode);

Alert.alert(LOGIN_ERROR_TITLE(), resolvedMessage);
}

handleAppleLoginFailed({ payload }) {
const errorStatus = _.get(payload, 'status');

if (errorStatus === 401) {
return this.performAppleRegistration();
}
return this.resolveError({ payload });
}

performAppleRegistration() {
const { onLoginSuccess, registerWithApple } = this.props;
const { idToken } = this.state;

Promise.all([
Storage.getItem(firstNameStorageKey()),
Storage.getItem(lastNameStorageKey()),
])
.then(([storageFirstName, storageLastName]) =>
registerWithApple(idToken, storageFirstName, storageLastName),
)
.then(onLoginSuccess)
.catch(this.resolveError);
}

resolveError({ payload }) {
this.setState({ inProgress: false });

const { response } = payload;

const payloadErrorCode = _.get(response, 'errors[0].code');
const resolvedErrorCode = getErrorCode(payloadErrorCode);
const errorMessage = resolveErrorMessage(resolvedErrorCode);

Alert.alert(LOGIN_ERROR_TITLE(), errorMessage);
}

render() {
const { inProgress } = this.state;
const { settings, style } = this.props;

const darkModeEnabled = _.get(
settings,
'providers.apple.buttonDarkModeStyle',
true,
);
const buttonStyle = darkModeEnabled
? AppleButton.Style.BLACK
: AppleButton.Style.WHITE;

if (inProgress) {
return (
<View>
<Spinner styleName="xl-gutter-top" />
</View>
);
}

return (
<AppleButton
buttonStyle={buttonStyle}
buttonType={AppleButton.Type.SIGN_IN}
onPress={this.handleAppleButtonPress}
style={style.appleButton}
/>
);
}
}

AppleSignInButton.propTypes = {
onLoginSuccess: PropTypes.func,
settings: PropTypes.object,
style: PropTypes.object,
};

const mapDispatchToProps = {
registerWithApple,
loginWithApple,
};

function mapStateToProps(state) {
return {
user: getUser(state),
settings: getExtensionSettings(state, ext()),
};
}

export default connect(
mapStateToProps,
mapDispatchToProps,
)(connectStyle(ext('AppleSignInButton'))(AppleSignInButton));
Loading

0 comments on commit 1559db2

Please sign in to comment.