Repository: kiwicom/mobile
Pull request #1046 (2018-08-30 09:12:37)

MMB: Fix header share button for e-ticket and boardingPasses


Patch:
diff --git a/app/MMB/src/navigation/AddressPickerScreen.js b/app/MMB/src/navigation/AddressPickerScreen.js
index 33f2682b..83de4695 100644
--- a/app/MMB/src/navigation/AddressPickerScreen.js
+++ b/app/MMB/src/navigation/AddressPickerScreen.js
@@ -5,6 +5,8 @@ import { View } from 'react-native';
 import { Translation } from '@kiwicom/mobile-localization';
 import { HeaderButton, type NavigationType } from '@kiwicom/mobile-navigation';
 
+import AddressLocationInput from '../scenes/tripServices/transportation/AddressLocationInput';
+
 type Props = {|
   +navigation: NavigationType,
   +currentAddress: ?string,
@@ -16,6 +18,10 @@ export default class AddressPickerScreen extends React.Component<Props> {
       props.navigation.goBack();
     }
 
+    function todo() {
+      console.warn('todo');
+    }
+
     return {
       headerLeft: (
         <HeaderButton.CloseModal
@@ -25,13 +31,11 @@ export default class AddressPickerScreen extends React.Component<Props> {
           }
         />
       ),
-      headerTitle: (
-        <View>
-          <Translation passThrough="TODO" />
-        </View>
-      ),
+      headerTitle: <AddressLocationInput onChangeText={todo} />,
+      headerStyle: { paddingBottom: 5 },
     };
   };
+
   render() {
     return (
       <View>
diff --git a/app/MMB/src/navigation/NavigationStack.js b/app/MMB/src/navigation/NavigationStack.js
index 02cf8b3e..d2cab1d8 100644
--- a/app/MMB/src/navigation/NavigationStack.js
+++ b/app/MMB/src/navigation/NavigationStack.js
@@ -1,5 +1,6 @@
 // @flow
 
+import * as React from 'react';
 import {
   StackNavigator,
   StackNavigatorOptions,
@@ -24,14 +25,16 @@ Object.entries(MenuComponents).forEach(
   ([routeName, { screen, headerTitle, headerRight, headerStyle }]) => {
     Screens[routeName] = {
       screen: withMappedProps(screen),
-      navigationOptions: {
+      navigationOptions: (props: Object) => ({
         headerTitle,
-        headerRight,
+        headerRight:
+          headerRight == null ? null : React.cloneElement(headerRight, props),
         headerStyle: {
+          ...StackNavigatorOptions.navigationOptions.headerStyle,
           ...headerStyle,
           backgroundColor: defaultTokens.paletteWhite,
         },
-      },
+      }),
     };
   },
 );
diff --git a/app/MMB/src/scenes/tickets/TicketScene.js b/app/MMB/src/scenes/tickets/TicketScene.js
index 458ddaee..20a0a26a 100644
--- a/app/MMB/src/scenes/tickets/TicketScene.js
+++ b/app/MMB/src/scenes/tickets/TicketScene.js
@@ -6,8 +6,12 @@ import { PublicApiRenderer, graphql } from '@kiwicom/mobile-relay';
 import BookingDetailContext from '../../context/BookingDetailContext';
 import type { TicketSceneQueryResponse } from './__generated__/TicketSceneQuery.graphql';
 import TicketRefetch from './TicketRefetch';
-import ETicketPdfScreen from './eTicketPdf/ETicketPdfScreen';
-import BoardingPassPdfScreen from './boardingPassesPdf/BoardingPassPdfScreen';
+import ETicketPdfScreen, {
+  ShareButtonWithContext,
+} from './eTicketPdf/ETicketPdfScreen';
+import BoardingPassPdfScreen, {
+  BoardingPassShareButtonWithContext,
+} from './boardingPassesPdf/BoardingPassPdfScreen';
 
 type Props = {|
   +data: TicketSceneQueryResponse,
@@ -42,8 +46,10 @@ export default class TicketScene extends React.Component<Props> {
 export const TicketSceneSubMenus = {
   'mmb.tickets.e_ticket': {
     screen: ETicketPdfScreen,
+    headerRight: <ShareButtonWithContext />,
   },
   'mmb.tickets.boarding_pass': {
     screen: BoardingPassPdfScreen,
+    headerRight: <BoardingPassShareButtonWithContext />,
   },
 };
diff --git a/app/MMB/src/scenes/tickets/boardingPassesPdf/BoardingPassPdfScreen.js b/app/MMB/src/scenes/tickets/boardingPassesPdf/BoardingPassPdfScreen.js
index 57b07930..4d311a66 100644
--- a/app/MMB/src/scenes/tickets/boardingPassesPdf/BoardingPassPdfScreen.js
+++ b/app/MMB/src/scenes/tickets/boardingPassesPdf/BoardingPassPdfScreen.js
@@ -1,8 +1,71 @@
 // @flow strict
 
 import * as React from 'react';
+import Share from 'react-native-share';
+import { documentDir } from '@kiwicom/mobile-assets';
+import type { NavigationType } from '@kiwicom/mobile-navigation';
+import idx from 'idx';
 
 import BoardingPassesPdf from './BoardingPassPdf';
+import ShareButton from '../../../components/ShareButton';
+import BookingDetailContext from '../../../context/BookingDetailContext';
+
+type ShareButtonPropsWithContext = {|
+  +boardingPassUrl: string,
+  +bookingId: number,
+  +flightNumber: string,
+|};
+
+class BoardingPassShareButton extends React.Component<
+  ShareButtonPropsWithContext,
+> {
+  onPress = () => {
+    this.sharePromise();
+  };
+
+  sharePromise = async () => {
+    try {
+      const boardingPassUrl = `${documentDir}/boardingPasses/${
+        this.props.bookingId
+      }/${this.props.flightNumber}.pdf`;
+
+      await Share.open({ url: boardingPassUrl });
+    } catch (err) {
+      // No worries, user just canceled share
+      return;
+    }
+  };
+
+  render = () => <ShareButton onPress={this.onPress} />;
+}
+
+type ShareButtonProps = {|
+  +navigation?: NavigationType,
+|};
+
+export function BoardingPassShareButtonWithContext(props: ShareButtonProps) {
+  const boardingPassUrl = idx(
+    props,
+    _ => _.navigation.state.params.boardingPassUrl,
+  );
+  const flightNumber = idx(props, _ => _.navigation.state.params.flightNumber);
+
+  if (boardingPassUrl == null || flightNumber == null) {
+    return null;
+  }
+
+  return (
+    <BookingDetailContext.Consumer>
+      {({ bookingId }) => (
+        <BoardingPassShareButton
+          boardingPassUrl={boardingPassUrl}
+          bookingId={bookingId}
+          flightNumber={flightNumber}
+        />
+      )}
+    </BookingDetailContext.Consumer>
+  );
+}
 
 type Props = {|
   +boardingPassUrl: ?string,
diff --git a/app/MMB/src/scenes/tickets/eTicketPdf/ETicketPdfScreen.js b/app/MMB/src/scenes/tickets/eTicketPdf/ETicketPdfScreen.js
index 3deedf3c..f32e985e 100644
--- a/app/MMB/src/scenes/tickets/eTicketPdf/ETicketPdfScreen.js
+++ b/app/MMB/src/scenes/tickets/eTicketPdf/ETicketPdfScreen.js
@@ -4,7 +4,7 @@ import * as React from 'react';
 import Share from 'react-native-share';
 import { documentDir } from '@kiwicom/mobile-assets';
 
-import ETicketPdf from './ETicketPdf';
+import ETicketPdf, { getEticketPath } from './ETicketPdf';
 import ShareButton from '../../../components/ShareButton';
 import BookingDetailContext from '../../../context/BookingDetailContext';
 
@@ -14,14 +14,25 @@ type ShareButtonProps = {|
 
 class ETicketShareButton extends React.Component<ShareButtonProps> {
   onPress = () => {
-    const eTicketPath = `${documentDir}/eTickets/${this.props.bookingId}.pdf`;
-    Share.open({ url: eTicketPath });
+    this.sharePromise();
+  };
+
+  sharePromise = async () => {
+    try {
+      const eTicketPath = `${documentDir}/${getEticketPath(
+        this.props.bookingId,
+      )}`;
+      await Share.open({ url: eTicketPath });
+    } catch (err) {
+      // No worries, user just canceled share
+      return;
+    }
   };
 
   render = () => <ShareButton onPress={this.onPress} />;
 }
 
-function ShareButtonWithContext() {
+export function ShareButtonWithContext() {
   return (
     <BookingDetailContext.Consumer>
       {({ bookingId }) => <ETicketShareButton bookingId={bookingId} />}
@@ -37,8 +48,4 @@ const ETicketPdfScreen = ({ ticketUrl }: Props) => (
   <ETicketPdf ticketUrl={ticketUrl} />
 );
 
-ETicketPdfScreen.navigationOptions = () => ({
-  headerRight: <ShareButtonWithContext />,
-});
-
 export default ETicketPdfScreen;
diff --git a/app/MMB/src/scenes/tripServices/transportation/AddressLocationInput.js b/app/MMB/src/scenes/tripServices/transportation/AddressLocationInput.js
new file mode 100644
index 00000000..5f2a0766
--- /dev/null
+++ b/app/MMB/src/scenes/tripServices/transportation/AddressLocationInput.js
@@ -0,0 +1,71 @@
+// @flow
+
+import * as React from 'react';
+import { View } from 'react-native';
+import { StyleSheet, TextIcon, TextInput, Text } from '@kiwicom/mobile-shared';
+import { Translation } from '@kiwicom/mobile-localization';
+import { defaultTokens } from '@kiwicom/mobile-orbit';
+
+type Props = {|
+  +defaultValue?: string,
+  +autoFocus?: boolean,
+  +onChangeText?: (text: string) => void,
+  +keyboardType?: 'email-address',
+  +secureTextEntry?: boolean,
+|};
+
+export default function AddressLocationInput(props: Props) {
+  return (
+    <View style={styles.wrapper}>
+      <View style={[styles.iconWrapper, styles.inputWrapperStyle]}>
+        <TextIcon code="B" style={styles.icon} />
+        <Text style={styles.text}>
+          <Translation id="mmb.trip_services.transportation.address_picker.input.placeholder" />
+        </Text>
+      </View>
+      <View style={styles.inputWrapper}>
+        <TextInput {...props} inputWrapperStyle={styles.inputWrapperStyle} />
+      </View>
+    </View>
+  );
+}
+
+const styles = StyleSheet.create({
+  wrapper: {
+    position: 'absolute',
+    start: 0,
+    end: -65,
+    flexDirection: 'row',
+    alignItems: 'center',
+  },
+  inputWrapper: {
+    flex: 1,
+    paddingStart: 52,
+  },
+  inputWrapperStyle: {
+    android: {
+      elevation: 1,
+      height: 38,
+    },
+    ios: {
+      height: 37,
+    },
+  },
+  iconWrapper: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    position: 'absolute',
+    start: 0,
+    backgroundColor: defaultTokens.paletteCloudNormal,
+    borderTopStartRadius: 6,
+    borderBottomStartRadius: 6,
+  },
+  icon: {
+    fontSize: 14,
+    padding: 10,
+    color: defaultTokens.paletteInkLight,
+  },
+  text: {
+    color: defaultTokens.paletteInkLight,
+  },
+});
diff --git a/packages/shared/src/forms/TextInput.js b/packages/shared/src/forms/TextInput.js
index cdee8a13..847c7927 100644
--- a/packages/shared/src/forms/TextInput.js
+++ b/packages/shared/src/forms/TextInput.js
@@ -18,6 +18,7 @@ type Props = {|
   +secureTextEntry?: boolean,
   +label?: React.Element<typeof Translation>,
   +labelStyle?: StylePropType,
+  +inputWrapperStyle?: StylePropType,
 |};
 
 type State = {|
@@ -53,7 +54,7 @@ export default class TextInput extends React.Component<Props, State> {
           {this.props.label}
         </Text>
       )}
-      <View style={styleSheet.wrapper}>
+      <View style={[styleSheet.wrapper, this.props.inputWrapperStyle]}>
         <OriginalTextInput
           underlineColorAndroid="transparent"
           autoCorrect={false}


Test patch:
diff --git a/packages/shared/src/forms/__tests__/__snapshots__/TextInput.test.js.snap b/packages/shared/src/forms/__tests__/__snapshots__/TextInput.test.js.snap
index 73f03336..d6265908 100644
--- a/packages/shared/src/forms/__tests__/__snapshots__/TextInput.test.js.snap
+++ b/packages/shared/src/forms/__tests__/__snapshots__/TextInput.test.js.snap
@@ -4,10 +4,13 @@ exports[`renders 1`] = `
 <React.Fragment>
   <Component
     style={
-      Object {
-        "flexDirection": "row",
-        "height": 47,
-      }
+      Array [
+        Object {
+          "flexDirection": "row",
+          "height": 47,
+        },
+        undefined,
+      ]
     }
   >
     <TextInput
@@ -39,10 +42,13 @@ exports[`renders with default value 1`] = `
 <React.Fragment>
   <Component
     style={
-      Object {
-        "flexDirection": "row",
-        "height": 47,
-      }
+      Array [
+        Object {
+          "flexDirection": "row",
+          "height": 47,
+        },
+        undefined,
+      ]
     }
   >
     <TextInput
@@ -89,10 +95,13 @@ exports[`renders with label 1`] = `
   </Text>
   <Component
     style={
-      Object {
-        "flexDirection": "row",
-        "height": 47,
-      }
+      Array [
+        Object {
+          "flexDirection": "row",
+          "height": 47,
+        },
+        undefined,
+      ]
     }
   >
     <TextInput
@@ -130,10 +139,13 @@ exports[`renders with placeholder 1`] = `
 <React.Fragment>
   <Component
     style={
-      Object {
-        "flexDirection": "row",
-        "height": 47,
-      }
+      Array [
+        Object {
+          "flexDirection": "row",
+          "height": 47,
+        },
+        undefined,
+      ]
     }
   >
     <TextInput