Repository: kiwicom/mobile
Pull request #810 (2018-07-03 12:52:54)

MMB: Add passenger icon to passenger screen


Patch:
diff --git a/app/MMB/src/scenes/passenger/PassengerMenuItem.js b/app/MMB/src/scenes/passenger/PassengerMenuItem.js
index 877d3bee..90cb9ef0 100644
--- a/app/MMB/src/scenes/passenger/PassengerMenuItem.js
+++ b/app/MMB/src/scenes/passenger/PassengerMenuItem.js
@@ -2,7 +2,7 @@
 
 import * as React from 'react';
 import { View } from 'react-native';
-import { Color, StyleSheet, Text } from '@kiwicom/mobile-shared';
+import { Color, StyleSheet, Text, TextIcon } from '@kiwicom/mobile-shared';
 import { Translation } from '@kiwicom/mobile-localization';
 
 type Props = {|
@@ -14,7 +14,12 @@ type Props = {|
 export default function PassengerMenuItem(props: Props) {
   return (
     <View style={styleSheet.wrapper}>
-      {props.name && <Text style={styleSheet.name}>{props.name}</Text>}
+      {props.name && (
+        <View style={styleSheet.row}>
+          <TextIcon code="w" style={styleSheet.icon} />
+          <Text style={styleSheet.name}>{props.name}</Text>
+        </View>
+      )}
       <Text style={styleSheet.title}>{props.title}</Text>
       <Text>{props.value}</Text>
     </View>
@@ -36,4 +41,14 @@ const styleSheet = StyleSheet.create({
     color: Color.textLight,
     fontSize: 12,
   },
+  icon: {
+    color: Color.black,
+    fontSize: 16,
+    paddingTop: 2,
+    marginEnd: 5,
+    alignSelf: 'flex-start',
+  },
+  row: {
+    flexDirection: 'row',
+  },
 });
diff --git a/app/MMB/src/scenes/tickets/TicketScene.js b/app/MMB/src/scenes/tickets/TicketScene.js
index 9972ff6a..e2c81a52 100644
--- a/app/MMB/src/scenes/tickets/TicketScene.js
+++ b/app/MMB/src/scenes/tickets/TicketScene.js
@@ -7,6 +7,7 @@ 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';
 
 type Props = {|
   +data: TicketSceneQueryResponse,
@@ -42,4 +43,7 @@ export const TicketSceneSubMenus = {
   'mmb.tickets.e_ticket': {
     screen: ETicketPdfScreen,
   },
+  'mmb.tickets.boarding_pass': {
+    screen: BoardingPassPdfScreen,
+  },
 };
diff --git a/app/MMB/src/scenes/tickets/boardingPassesPdf/BoardingPassPdf.js b/app/MMB/src/scenes/tickets/boardingPassesPdf/BoardingPassPdf.js
new file mode 100644
index 00000000..7f819877
--- /dev/null
+++ b/app/MMB/src/scenes/tickets/boardingPassesPdf/BoardingPassPdf.js
@@ -0,0 +1,41 @@
+// @flow
+
+import * as React from 'react';
+import { GeneralError } from '@kiwicom/mobile-shared';
+import { Translation } from '@kiwicom/mobile-localization';
+import { PdfViewAndStore } from '@kiwicom/mobile-pdf';
+
+export const getPdfFilenameFromUrl = (url: string) => {
+  const filename = url.slice(url.lastIndexOf('/') + 1);
+  const isPdf = filename.includes('.pdf');
+  if (!isPdf) {
+    return null;
+  }
+  return filename.slice(0, filename.indexOf('.pdf'));
+};
+
+type Props = {|
+  +boardingPassUrl: ?string,
+|};
+
+const BoardingPassPdf = ({ boardingPassUrl }: Props) => {
+  if (boardingPassUrl) {
+    const filename = getPdfFilenameFromUrl(boardingPassUrl);
+
+    if (filename) {
+      return (
+        <PdfViewAndStore
+          fileName={`/boardingPasses/${filename}.pdf`}
+          url={boardingPassUrl}
+        />
+      );
+    }
+  }
+  return (
+    <GeneralError
+      errorMessage={<Translation id="mmb.boarding_passes.not_available" />}
+    />
+  );
+};
+
+export default BoardingPassPdf;
diff --git a/app/MMB/src/scenes/tickets/boardingPassesPdf/BoardingPassPdfScreen.js b/app/MMB/src/scenes/tickets/boardingPassesPdf/BoardingPassPdfScreen.js
new file mode 100644
index 00000000..d65561d6
--- /dev/null
+++ b/app/MMB/src/scenes/tickets/boardingPassesPdf/BoardingPassPdfScreen.js
@@ -0,0 +1,15 @@
+// @flow strict
+
+import * as React from 'react';
+
+import BoardingPassesPdf from './BoardingPassPdf';
+
+type Props = {|
+  +boardingPassUrl: ?string,
+|};
+
+const BoardingPassesPdfScreen = ({ boardingPassUrl }: Props) => (
+  <BoardingPassesPdf boardingPassUrl={boardingPassUrl} />
+);
+
+export default BoardingPassesPdfScreen;
diff --git a/app/MMB/src/scenes/timeline/events/DownloadBoardingPassTimelineEvent.js b/app/MMB/src/scenes/timeline/events/DownloadBoardingPassTimelineEvent.js
index c1cef0e1..56a86180 100644
--- a/app/MMB/src/scenes/timeline/events/DownloadBoardingPassTimelineEvent.js
+++ b/app/MMB/src/scenes/timeline/events/DownloadBoardingPassTimelineEvent.js
@@ -32,8 +32,13 @@ class DownloadBoardingPassTimelineEvent extends React.Component<Props> {
     });
   };
 
-  handleOpenETicket = () => {
-    // TODO
+  handleOpenBoardingPass = () => {
+    this.navigate('mmb.tickets.boarding_pass', {
+      boardingPassUrl: idx(
+        this.props,
+        _ => _.data.leg.boardingPass.boardingPassUrl,
+      ),
+    });
   };
 
   render() {
@@ -82,7 +87,7 @@ class DownloadBoardingPassTimelineEvent extends React.Component<Props> {
               <TextButton
                 disabled={disabled}
                 title={buttonTitle}
-                onPress={this.handleOpenETicket}
+                onPress={this.handleOpenBoardingPass}
               />
             </View>
           </View>
diff --git a/packages/navigation/types/Navigation.js b/packages/navigation/types/Navigation.js
index 2e057a68..a78a9ff8 100644
--- a/packages/navigation/types/Navigation.js
+++ b/packages/navigation/types/Navigation.js
@@ -37,6 +37,7 @@ type MMBStackNavigatorNames =
   | 'mmb.other.open'
   | 'mmb.other.refund'
   | 'mmb.tickets.e_ticket'
+  | 'mmb.tickets.boarding_pass'
   | 'mmb.timeline.invoice'
   | 'DetailScreen';
 


Test patch:
diff --git a/app/MMB/src/scenes/tickets/boardingPassesPdf/__tests__/BoardingPassPdf.test.js b/app/MMB/src/scenes/tickets/boardingPassesPdf/__tests__/BoardingPassPdf.test.js
new file mode 100644
index 00000000..9719e4ae
--- /dev/null
+++ b/app/MMB/src/scenes/tickets/boardingPassesPdf/__tests__/BoardingPassPdf.test.js
@@ -0,0 +1,57 @@
+// @flow
+
+import * as React from 'react';
+import ShallowRenderer from 'react-test-renderer/shallow';
+
+const renderer = new ShallowRenderer();
+
+import BoardingPassPdf, { getPdfFilenameFromUrl } from '../BoardingPassPdf';
+
+jest.mock('react-native-fetch-blob', () => {
+  return {
+    config: () => ({
+      fetch: async () => ({
+        path: () => {},
+      }),
+    }),
+    polyfill: () => {},
+    fs: {
+      dirs: {
+        DocumentDir: 'ait',
+      },
+      exists: () => {},
+    },
+  };
+});
+
+describe('BoardingPassPdf', () => {
+  it('renders', () => {
+    const wrapper = renderer.render(
+      <BoardingPassPdf boardingPassUrl="https://somedomain.com/folder/subfolder/file.pdf" />,
+    );
+
+    expect(wrapper.props.fileName).toBe('/boardingPasses/file.pdf');
+  });
+});
+
+describe('getPdfFilenameFromUrl', () => {
+  it('should return correct filename given the URL of a pdf', () => {
+    const url =
+      'https://somedomainname.com/numbers1234/Vienna-Palma%2CMajorca_1234567890_5bafa3bd88fbf8e8c90a0a.pdf?v=12345676';
+
+    expect(getPdfFilenameFromUrl(url)).toBe(
+      'Vienna-Palma%2CMajorca_1234567890_5bafa3bd88fbf8e8c90a0a',
+    );
+    const pdf = 'somePdf.pdf';
+    expect(getPdfFilenameFromUrl(pdf)).toBe('somePdf');
+  });
+
+  it('should return null if given a URL not containing .pdf', () => {
+    const url =
+      'https://somedomainname.com/numbers1234/Vienna-Palma%2CMajorca_1234567890_5bafa3bd88fbf8e8c90a0a.doc?v=12345676';
+
+    expect(getPdfFilenameFromUrl(url)).toBe(null);
+    const pdf = 'someNonPdf.xls';
+    expect(getPdfFilenameFromUrl(pdf)).toBe(null);
+  });
+});