diff --git a/.github/actions/markPullRequestsAsDeployed/action.yml b/.github/actions/markPullRequestsAsDeployed/action.yml index 42c9a6604ab..cb23977e230 100644 --- a/.github/actions/markPullRequestsAsDeployed/action.yml +++ b/.github/actions/markPullRequestsAsDeployed/action.yml @@ -8,6 +8,9 @@ inputs: description: "Check if deploying to production" required: false default: "false" + VERSION: + description: "The app version in which the pull requests were deployed" + required: true GITHUB_TOKEN: description: "Github token for authentication" required: true diff --git a/.github/actions/markPullRequestsAsDeployed/index.js b/.github/actions/markPullRequestsAsDeployed/index.js index eb24d24e90c..1179df1fd25 100644 --- a/.github/actions/markPullRequestsAsDeployed/index.js +++ b/.github/actions/markPullRequestsAsDeployed/index.js @@ -16,6 +16,7 @@ const prList = JSON.parse(core.getInput('PR_LIST', {required: true})); const isProd = JSON.parse( core.getInput('IS_PRODUCTION_DEPLOY', {required: true}), ); +const version = JSON.parse(core.getInput('VERSION', {required: true})); const token = core.getInput('GITHUB_TOKEN', {required: true}); const octokit = github.getOctokit(token); const githubUtils = new GithubUtils(octokit); @@ -48,7 +49,7 @@ const webResult = getDeployTableMessage(core.getInput('WEB', {required: true})); const workflowURL = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}` + `/actions/runs/${process.env.GITHUB_RUN_ID}`; -let message = `πŸš€ [Deployed](${workflowURL}) to ${isProd ? 'production' : 'staging'} πŸš€`; +let message = `πŸš€ [Deployed](${workflowURL}) to ${isProd ? 'production' : 'staging'} in version: ${version}πŸš€`; message += `\n\n platform | result \n ---|--- \nπŸ€– android πŸ€–|${androidResult} \nπŸ–₯ desktop πŸ–₯|${desktopResult}`; message += `\n🍎 iOS 🍎|${iOSResult} \nπŸ•Έ web πŸ•Έ|${webResult}`; diff --git a/.github/actions/markPullRequestsAsDeployed/markPullRequestsAsDeployed.js b/.github/actions/markPullRequestsAsDeployed/markPullRequestsAsDeployed.js index 3449a434108..0a58fce4606 100644 --- a/.github/actions/markPullRequestsAsDeployed/markPullRequestsAsDeployed.js +++ b/.github/actions/markPullRequestsAsDeployed/markPullRequestsAsDeployed.js @@ -6,6 +6,7 @@ const prList = JSON.parse(core.getInput('PR_LIST', {required: true})); const isProd = JSON.parse( core.getInput('IS_PRODUCTION_DEPLOY', {required: true}), ); +const version = JSON.parse(core.getInput('VERSION', {required: true})); const token = core.getInput('GITHUB_TOKEN', {required: true}); const octokit = github.getOctokit(token); const githubUtils = new GithubUtils(octokit); @@ -38,7 +39,7 @@ const webResult = getDeployTableMessage(core.getInput('WEB', {required: true})); const workflowURL = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}` + `/actions/runs/${process.env.GITHUB_RUN_ID}`; -let message = `πŸš€ [Deployed](${workflowURL}) to ${isProd ? 'production' : 'staging'} πŸš€`; +let message = `πŸš€ [Deployed](${workflowURL}) to ${isProd ? 'production' : 'staging'} in version: ${version}πŸš€`; message += `\n\n platform | result \n ---|--- \nπŸ€– android πŸ€–|${androidResult} \nπŸ–₯ desktop πŸ–₯|${desktopResult}`; message += `\n🍎 iOS 🍎|${iOSResult} \nπŸ•Έ web πŸ•Έ|${webResult}`; diff --git a/.github/workflows/deployBlocker.yml b/.github/workflows/deployBlocker.yml index 458768f8a34..9c0cd20c89b 100644 --- a/.github/workflows/deployBlocker.yml +++ b/.github/workflows/deployBlocker.yml @@ -21,7 +21,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} NEW_DEPLOY_BLOCKERS: ${{ github.event.issue.html_url }} - - name: Update StagingDeployCash with issue + - name: Update StagingDeployCash with pull request uses: Expensify/Expensify.cash/.github/actions/createOrUpdateStagingDeploy@main if: ${{ github.event_name == 'pull_request' }} with: diff --git a/.github/workflows/platformDeploy.yml b/.github/workflows/platformDeploy.yml index 0255fc0302a..b67ddc074db 100644 --- a/.github/workflows/platformDeploy.yml +++ b/.github/workflows/platformDeploy.yml @@ -292,6 +292,7 @@ jobs: with: PR_LIST: ${{ steps.getReleasePRList.outputs.PR_LIST }} IS_PRODUCTION_DEPLOY: ${{ env.SHOULD_DEPLOY_PRODUCTION == 'true' }} + VERSION: ${{ env.VERSION }} GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} ANDROID: ${{ needs.android.result }} DESKTOP: ${{ needs.desktop.result }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3cce5c9b974..d394f9d48cc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,40 +19,46 @@ If you are hired for an Upwork job and have any job-specific questions, please a If you've found a vulnerability, please email security@expensify.com with the subject `Vulnerability Report` instead of creating an issue. ## Payment for Contributions -We hire and pay external contributors via Upwork.com. If you'd like to be paid for contributing, please create an Upwork account and apply for a job in the [Upwork issue list](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2). Payment for your contributions will be made no less than 7 days after the pull request is merged to allow for regression testing. We hire one contributor for each Upwork job. New Expensify.cash contributors are limited to working on one job at a time, however experienced contributors may work on numerous jobs simultaneously. If you have not received payment after 8 days of the PR being deployed to production, please email contributors@expensify.com referencing the GH issue and your GH handle. +We hire and pay external contributors via Upwork.com. If you'd like to be paid for contributing, please create an Upwork account and apply for a job in the [Upwork issue list](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2). Payment for your contributions will be made no less than 7 days after the pull request is merged to allow for regression testing. We hire one contributor for each Upwork job. New Expensify.cash contributors are limited to working on one job at a time, however experienced contributors may work on numerous jobs simultaneously. If you have not received payment after 8 days of the PR being deployed to production, please email contributors@expensify.com referencing the GH issue and your GH handle. ## Finding Expensify.cash Jobs -There are two ways you can find an Expensify.cash job that you can contribute to: +There are two ways you can find an Expensify.cash job that you can contribute to: #### Finding a job that Expensify posted -This is the most common scenario for contributors. The Expensify team posts Expensify.cash jobs to the Upwork job list [here](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2). Each job in Upwork has a corresponding GitHub issue, which will include instructions to follow. +This is the most common scenario for contributors. The Expensify team posts Expensify.cash jobs to the Upwork job list [here](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2). Each job in Upwork has a corresponding GitHub issue, which will include instructions to follow. #### Proposing a job that Expensify hasn’t posted In this scenario, it’s possible that you found a bug or enhancement that we haven’t posted to the [Upwork job list](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2) or [Github repository](https://github.com/Expensify/Expensify.cash/issues?q=is%3Aissue). This is an opportunity to propose a job, and (optionally) a solution. If it's a valid job proposal, we will compensate you for the solution and give an additional bonus of $150 for proactively proposing the job. In this case, please take the following steps: - 1. Check to ensure an issue does not already exist in the Expensify.cash Issue list or Upwork job list. Please use your best judgement to search for similar titles and issue descriptions. - 2. If your bug or enhancement matches an existing issue, please feel free to comment on that GitHub issue with your findings if you think it’ll help solve a problem. - 3. If there is no existing issue or Upwork job, create a new GitHub issue in the Expensify.cash repo. - 4. Make sure to fill out all the required information fields in the issue template. - 5. Optional: If you would like to solve the bug or enhancement that you are proposing, please add a comment on your issue with a solution proposal. + 1. Check to ensure an issue does not already exist in the Expensify.cash Issue list or Upwork job list. Please use your best judgement to search for similar titles and issue descriptions. + 2. If your bug or enhancement matches an existing issue, please feel free to comment on that GitHub issue with your findings if you think it’ll help solve a problem. + 3. If there is no existing issue or Upwork job, create a new GitHub issue in the Expensify.cash repo. + 4. Make sure to fill out all the required information fields in the issue template. + 5. Optional: If you would like to solve the bug or enhancement that you are proposing, please add a comment on your issue with a solution proposal. 6. Pause on this step until a member of the Expensify team responds on your issue with next steps. -## Working on an Expensify.cash Jobs +>**Note:** Our problem solving approach at Expensify is to focus on high value problems and avoid small optimizations with results that are difficult to measure. We also prefer to identify and solve problems at their root. Given that, please ensure all proposed jobs fix a specific problem in a measurable way with evidence so they are easy to evaluate. Here's an example of a good problem/solution: +> +>**Problem:** The app start up time has regressed because we introduced "New Feature" in PR #12345 and is now 1042ms slower because `SomeComponent` is re-rendering 42 times. +> +>**Solution:** Start up time will perceptibly decrease by 1042ms if we prevent the unnecessary re-renders of this component. + +## Working on Expensify.cash Jobs *Reminder: For technical guidance please refer to the [README](https://github.com/Expensify/Expensify.cash/blob/main/README.md)*. #### Express interest for the job on Upwork.com -1. If you are interested in working on a job posted in Upwork, click **Submit a Proposal** in Upwork to express your interest to the Expensify team. +1. If you are interested in working on a job posted in Upwork, click **Submit a Proposal** in Upwork to express your interest to the Expensify team. #### Make sure you can reproduce the problem -2. Use your test account(s) to reproduce the problem by following the steps in the GitHub issue. +2. Use your test account(s) to reproduce the problem by following the steps in the GitHub issue. 3. If you cannot reproduce the problem, pause on this step and add a comment to the issue explaining where you are stuck. -#### Propose a solution for the job +#### Propose a solution for the job 4. After you reproduce the issue, make a proposal for your solution and post it as a comment in the corresponding GitHub issue (linked in the Upwork job). Your solution proposal should include a brief technical explanation of the changes you will make. -5. Pause at this step until Expensify provides feedback on your proposal (do not begin coding or creating a pull request yet). -6. If your solution proposal is accepted, Expensify will hire you on Upwork and assign the GitHub issue to you. +5. Pause at this step until Expensify provides feedback on your proposal (do not begin coding or creating a pull request yet). +6. If your solution proposal is accepted, Expensify will hire you on Upwork and assign the GitHub issue to you. #### Begin coding your solution in a pull request 7. When you are ready to start, fork the repository and create a new branch. @@ -79,8 +85,8 @@ In this scenario, it’s possible that you found a bug or enhancement that we ha 13. Please never force push when a PR review has already started (because this messes with the PR review history) 14. Upon submission of a PR, please include a numbered list of explicit testing steps for each platform (Web, Desktop, iOS, and Android) to confirm the fix works as expected and there are no regressions. -#### Timeline expectations and asking for help along the way -- If you have made a change to your pull request and are ready for another review, leave a comment that says "Updated" on the pull request itself. +#### Timeline expectations and asking for help along the way +- If you have made a change to your pull request and are ready for another review, leave a comment that says "Updated" on the pull request itself. - Please keep the conversation in GitHub, and do not ping individual reviewers in Slack or Upwork to get their attention. - Pull Request reviews can sometimes take a few days. If your pull request has not been addressed after four days please let us know via the #expensify-open-source Slack channel. diff --git a/android/app/build.gradle b/android/app/build.gradle index 6bbab5c893b..11a8a698a0d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -148,8 +148,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001002702 - versionName "1.0.27-2" + versionCode 1001002902 + versionName "1.0.29-2" } splits { abi { diff --git a/ios/ExpensifyCash/Info.plist b/ios/ExpensifyCash/Info.plist index 4c6ecd54d20..1ea80d85889 100644 --- a/ios/ExpensifyCash/Info.plist +++ b/ios/ExpensifyCash/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0.27 + 1.0.29 CFBundleSignature ???? CFBundleURLTypes @@ -30,7 +30,7 @@ CFBundleVersion - 1.0.27.2 + 1.0.29.2 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/ExpensifyCashTests/Info.plist b/ios/ExpensifyCashTests/Info.plist index 76dabd87c49..1ce40fc0695 100644 --- a/ios/ExpensifyCashTests/Info.plist +++ b/ios/ExpensifyCashTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.0.27 + 1.0.29 CFBundleSignature ???? CFBundleVersion - 1.0.27.2 + 1.0.29.2 diff --git a/package-lock.json b/package-lock.json index 3940880006a..d8217472ae5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "expensify.cash", - "version": "1.0.27-2", + "version": "1.0.29-2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 1ddb2728c10..285cd985b97 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "expensify.cash", - "version": "1.0.27-2", + "version": "1.0.29-2", "author": "Expensify, Inc.", "homepage": "https://expensify.cash", "description": "Expensify.cash is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", @@ -10,6 +10,7 @@ "android": "react-native run-android", "ios": "react-native run-ios", "ipad": "react-native run-ios --simulator=\"iPad Pro (12.9-inch) (4th generation)\"", + "ipad-sm": "react-native run-ios --simulator=\"iPad Pro (9.7-inch)\"", "desktop": "node desktop/start.js", "start": "react-native start", "web": "node web/proxy.js & webpack-dev-server --open --config config/webpack/webpack.dev.js", diff --git a/src/ROUTES.js b/src/ROUTES.js index 36d5f2117a5..a6c60a4809d 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -1,5 +1,5 @@ import lodashGet from 'lodash/get'; -import {addTrailingForwardSlash} from './libs/Url'; +import {wrapWithForwardSlash} from './libs/Url'; /** * This is a file containing constants for all of the routes we want to be able to go to @@ -21,8 +21,10 @@ export default { REPORT, REPORT_WITH_ID: 'r/:reportID', getReportRoute: reportID => `r/${reportID}`, - IOU_REQUEST: 'iou/request', - IOU_BILL: 'iou/split', + IOU_REQUEST: 'iou/request/:reportID', + getIouRequestRoute: reportID => `iou/request/${reportID}`, + IOU_BILL: 'iou/split/:reportID', + getIouSplitRoute: reportID => `iou/split/${reportID}`, SEARCH: 'search', SIGNIN: 'signin', SET_PASSWORD_WITH_VALIDATE_CODE: 'setpassword/:accountID/:validateCode', @@ -41,7 +43,7 @@ export default { * @returns {Object} */ parseReportRouteParams: (route) => { - if (!route.startsWith(addTrailingForwardSlash(REPORT))) { + if (!route.startsWith(wrapWithForwardSlash(REPORT))) { return {}; } diff --git a/src/components/AnchorForCommentsOnly/index.js b/src/components/AnchorForCommentsOnly/index.js index 3edda2184b8..6e70e859cf9 100644 --- a/src/components/AnchorForCommentsOnly/index.js +++ b/src/components/AnchorForCommentsOnly/index.js @@ -1,5 +1,5 @@ import React from 'react'; -import {StyleSheet, Text} from 'react-native'; +import {StyleSheet} from 'react-native'; import anchorForCommentsOnlyPropTypes from './anchorForCommentsOnlyPropTypes'; const defaultProps = { @@ -18,9 +18,8 @@ const AnchorForCommentsOnly = ({ style, ...props }) => ( - {children} - + ); AnchorForCommentsOnly.propTypes = anchorForCommentsOnlyPropTypes; diff --git a/src/components/CreateMenu.js b/src/components/CreateMenu.js deleted file mode 100644 index 2e0931bb8c6..00000000000 --- a/src/components/CreateMenu.js +++ /dev/null @@ -1,117 +0,0 @@ -import React, {PureComponent} from 'react'; -import {View} from 'react-native'; -import PropTypes from 'prop-types'; -import Popover from './Popover'; -import styles from '../styles/styles'; -import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; -import MenuItem from './MenuItem'; - -const propTypes = { - // Callback to fire on request to modal close - onClose: PropTypes.func.isRequired, - - // State that determines whether to display the create menu or not - isVisible: PropTypes.bool.isRequired, - - // Callback to fire when a CreateMenu item is selected - onItemSelected: PropTypes.func.isRequired, - - // Menu items to be rendered on the list - menuItems: PropTypes.arrayOf( - PropTypes.shape({ - icon: PropTypes.func.isRequired, - text: PropTypes.string.isRequired, - onSelected: PropTypes.func.isRequired, - }), - ).isRequired, - - // The anchor position of the CreateMenu popover - anchorPosition: PropTypes.shape({ - top: PropTypes.number, - right: PropTypes.number, - bottom: PropTypes.number, - left: PropTypes.number, - }).isRequired, - - // A react-native-animatable animation definition for the modal display animation. - animationIn: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.object, - ]), - - // A react-native-animatable animation definition for the modal hide animation. - animationOut: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.object, - ]), - - ...windowDimensionsPropTypes, -}; - -const defaultProps = { - animationIn: 'fadeIn', - animationOut: 'fadeOut', -}; - -class CreateMenu extends PureComponent { - constructor(props) { - super(props); - this.onModalHide = () => {}; - this.setOnModalHide = this.setOnModalHide.bind(this); - this.resetOnModalHide = this.resetOnModalHide.bind(this); - } - - /** - * Sets a new function to execute when the modal hides - * @param {Function} callback The function to be called on modal hide - */ - setOnModalHide(callback) { - this.onModalHide = callback; - } - - /** - * After the modal hides, reset the onModalHide to an empty function - */ - resetOnModalHide() { - this.onModalHide = () => {}; - } - - render() { - return ( - { - this.onModalHide(); - this.resetOnModalHide(); - }} - animationIn={this.props.animationIn} - animationOut={this.props.animationOut} - > - - {this.props.menuItems.map(({ - icon, - text, - onSelected = () => {}, - }) => ( - { - this.props.onItemSelected(); - this.setOnModalHide(onSelected); - }} - /> - ))} - - - ); - } -} - -CreateMenu.propTypes = propTypes; -CreateMenu.defaultProps = defaultProps; - -export default withWindowDimensions(CreateMenu); diff --git a/src/components/CreateMenu/BaseCreateMenu.js b/src/components/CreateMenu/BaseCreateMenu.js index 667158645ae..73d1e5234b8 100644 --- a/src/components/CreateMenu/BaseCreateMenu.js +++ b/src/components/CreateMenu/BaseCreateMenu.js @@ -5,7 +5,10 @@ import Popover from '../Popover'; import styles from '../../styles/styles'; import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; import MenuItem from '../MenuItem'; -import createMenuPropTypes from './CreateMenuPropTypes'; +import { + propTypes as createMenuPropTypes, + defaultProps as defaultCreateMenuPropTypes, +} from './CreateMenuPropTypes'; const propTypes = { // Callback fired when the menu is completely closed @@ -16,6 +19,7 @@ const propTypes = { }; const defaultProps = { + ...defaultCreateMenuPropTypes, onMenuHide: () => {}, }; @@ -23,10 +27,12 @@ class BaseCreateMenu extends PureComponent { render() { return ( {this.props.menuItems.map(item => ( diff --git a/src/components/CreateMenu/CreateMenuPropTypes.js b/src/components/CreateMenu/CreateMenuPropTypes.js index b8fd59c2493..89d1b9f7da3 100644 --- a/src/components/CreateMenu/CreateMenuPropTypes.js +++ b/src/components/CreateMenu/CreateMenuPropTypes.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; -const createMenuPropTypes = { +const propTypes = { // Callback method fired when the user requests to close the modal onClose: PropTypes.func.isRequired, @@ -23,6 +23,31 @@ const createMenuPropTypes = { onSelected: PropTypes.func.isRequired, }), ).isRequired, + + // The anchor position of the CreateMenu popover + anchorPosition: PropTypes.shape({ + top: PropTypes.number, + right: PropTypes.number, + bottom: PropTypes.number, + left: PropTypes.number, + }).isRequired, + + // A react-native-animatable animation definition for the modal display animation. + animationIn: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.object, + ]), + + // A react-native-animatable animation definition for the modal hide animation. + animationOut: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.object, + ]), +}; + +const defaultProps = { + animationIn: 'fadeIn', + animationOut: 'fadeOut', }; -export default createMenuPropTypes; +export {propTypes, defaultProps}; diff --git a/src/components/CreateMenu/index.js b/src/components/CreateMenu/index.js index 2bc0c03226c..39338cb4955 100644 --- a/src/components/CreateMenu/index.js +++ b/src/components/CreateMenu/index.js @@ -1,6 +1,6 @@ import React from 'react'; import BaseCreateMenu from './BaseCreateMenu'; -import createMenuPropTypes from './CreateMenuPropTypes'; +import {propTypes, defaultProps} from './CreateMenuPropTypes'; /** * The web implementation of the menu needs to trigger actions before the popup closes @@ -23,6 +23,8 @@ const CreateMenu = (props) => { return ; }; -CreateMenu.propTypes = createMenuPropTypes; +CreateMenu.propTypes = propTypes; +CreateMenu.defaultProps = defaultProps; +CreateMenu.displayName = 'CreateMenu'; export default CreateMenu; diff --git a/src/components/CreateMenu/index.native.js b/src/components/CreateMenu/index.native.js index 043da381985..6d20a86850f 100644 --- a/src/components/CreateMenu/index.native.js +++ b/src/components/CreateMenu/index.native.js @@ -1,6 +1,6 @@ import React, {Component} from 'react'; import BaseCreateMenu from './BaseCreateMenu'; -import createMenuPropTypes from './CreateMenuPropTypes'; +import {propTypes, defaultProps} from './CreateMenuPropTypes'; /** * The mobile native implementation of the CreateMenu needs to trigger actions after the popup closes @@ -35,6 +35,7 @@ class CreateMenu extends Component { } } -CreateMenu.propTypes = createMenuPropTypes; +CreateMenu.propTypes = propTypes; +CreateMenu.defaultProps = defaultProps; export default CreateMenu; diff --git a/src/components/InlineCodeBlock/index.android.js b/src/components/InlineCodeBlock/index.android.js new file mode 100644 index 00000000000..d68ca0031b8 --- /dev/null +++ b/src/components/InlineCodeBlock/index.android.js @@ -0,0 +1,19 @@ +/* eslint-disable react/jsx-props-no-spreading */ +import React from 'react'; +import {View} from 'react-native'; +import inlineCodeBlockPropTypes from './inlineCodeBlockPropTypes'; + +const InlineCodeBlock = ({ + TDefaultRenderer, + defaultRendererProps, + boxModelStyle, + textStyle, +}) => ( + + + +); + +InlineCodeBlock.propTypes = inlineCodeBlockPropTypes; +InlineCodeBlock.displayName = 'InlineCodeBlock'; +export default InlineCodeBlock; diff --git a/src/components/InlineCodeBlock/index.native.js b/src/components/InlineCodeBlock/index.ios.js similarity index 50% rename from src/components/InlineCodeBlock/index.native.js rename to src/components/InlineCodeBlock/index.ios.js index 0178ecf1828..163bf4a201d 100644 --- a/src/components/InlineCodeBlock/index.native.js +++ b/src/components/InlineCodeBlock/index.ios.js @@ -1,26 +1,23 @@ +/* eslint-disable react/jsx-props-no-spreading */ import React from 'react'; +import {View} from 'react-native'; import styles from '../../styles/styles'; -import WrappedText from './wrappedText'; import inlineCodeBlockPropTypes from './inlineCodeBlockPropTypes'; const InlineCodeBlock = ({ + TDefaultRenderer, defaultRendererProps, boxModelStyle, textStyle, }) => ( - - {defaultRendererProps.tnode.data} - + + ); InlineCodeBlock.propTypes = inlineCodeBlockPropTypes; diff --git a/src/components/InlineCodeBlock/wrappedText.js b/src/components/InlineCodeBlock/wrappedText.js deleted file mode 100644 index 930ebb4c99b..00000000000 --- a/src/components/InlineCodeBlock/wrappedText.js +++ /dev/null @@ -1,87 +0,0 @@ -import React, {Fragment} from 'react'; -import {Text, View} from 'react-native'; -import PropTypes from 'prop-types'; -import styles from '../../styles/styles'; - -/** - * Breaks the text into matrix - * for eg: My Name is Rajat - * [ - * [My,'',Name,'','',is,'',Rajat], - * ] - * - * @param {String} text - * @returns {Array} - */ -function getTextMatrix(text) { - return text.split('\n').map(row => row.split(/(\s)/)); -} - -const propTypes = { - // Required text - children: PropTypes.string.isRequired, - - // Style to be applied to Text - // eslint-disable-next-line react/forbid-prop-types - textStyle: PropTypes.object, - - // Style for each word(Token) in the text, - // remember that token also includes the following spaces before next word break - // eslint-disable-next-line react/forbid-prop-types - wordStyle: PropTypes.object, - - // Style for first word - // eslint-disable-next-line react/forbid-prop-types - firstWordStyle: PropTypes.object, - - // Style for last word - // eslint-disable-next-line react/forbid-prop-types - lastWordStyle: PropTypes.object, -}; - -const defaultProps = { - textStyle: {}, - wordStyle: {}, - firstWordStyle: {}, - lastWordStyle: {}, -}; - -const WrappedText = (props) => { - const textMatrix = getTextMatrix(props.children); - return ( - <> - {textMatrix.map((rowText, rowIndex) => ( - - {rowText.map((colText, colIndex) => ( - - // Outer View is important to vertically center the Text - - - {colText} - - - ))} - - ))} - - ); -}; - -WrappedText.propTypes = propTypes; -WrappedText.defaultProps = defaultProps; -WrappedText.displayName = 'WrappedText'; - -export default WrappedText; diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js index 2471288ed92..b4266eeaf8b 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.js @@ -79,6 +79,7 @@ class BaseModal extends PureComponent { onModalShow={() => { this.subscribeToKeyEvents(); setModalVisibility(true); + this.props.onModalShow(); }} onModalHide={this.hideModalAndRemoveEventListeners} onSwipeComplete={this.props.onClose} diff --git a/src/components/Modal/ModalPropTypes.js b/src/components/Modal/ModalPropTypes.js index 33881e0cd31..8d9533bb9a9 100644 --- a/src/components/Modal/ModalPropTypes.js +++ b/src/components/Modal/ModalPropTypes.js @@ -19,6 +19,9 @@ const propTypes = { // Callback method fired when the modal is hidden onModalHide: PropTypes.func, + // Callback method fired when the modal is shown + onModalShow: PropTypes.func, + // Style of modal to display type: PropTypes.oneOf(_.values(CONST.MODAL.MODAL_TYPE)), @@ -49,6 +52,7 @@ const defaultProps = { onSubmit: null, type: '', onModalHide: () => {}, + onModalShow: () => {}, animationIn: null, animationOut: null, popoverAnchorPosition: {}, diff --git a/src/components/Popover/index.native.js b/src/components/Popover/index.native.js new file mode 100644 index 00000000000..0b9398749fc --- /dev/null +++ b/src/components/Popover/index.native.js @@ -0,0 +1,23 @@ +import React from 'react'; +import {propTypes, defaultProps} from './PopoverPropTypes'; +import CONST from '../../CONST'; +import Modal from '../Modal'; +import withWindowDimensions from '../withWindowDimensions'; + +/* + * This is a convenience wrapper around the Modal component for a responsive Popover. + * On small screen widths, it uses BottomDocked modal type, and a Popover type on wide screen widths. + */ +const Popover = props => ( + +); + +Popover.propTypes = propTypes; +Popover.defaultProps = defaultProps; +Popover.displayName = 'Popover'; + +export default withWindowDimensions(Popover); diff --git a/src/components/TextInputFocusable/index.js b/src/components/TextInputFocusable/index.js index 01c038bdb8b..89b5928742f 100644 --- a/src/components/TextInputFocusable/index.js +++ b/src/components/TextInputFocusable/index.js @@ -92,7 +92,6 @@ class TextInputFocusable extends React.Component { } componentDidMount() { - this.focusInput(); this.updateNumberOfLines(); // This callback prop is used by the parent component using the constructor to @@ -212,10 +211,6 @@ class TextInputFocusable extends React.Component { }); } - focusInput() { - this.textInput.focus(); - } - render() { const propStyles = StyleSheet.flatten(this.props.style); propStyles.outline = 'none'; diff --git a/src/components/withWindowDimensions.js b/src/components/withWindowDimensions.js index 3dc8c3c0c9f..25e669d507d 100644 --- a/src/components/withWindowDimensions.js +++ b/src/components/withWindowDimensions.js @@ -16,7 +16,15 @@ const windowDimensionsPropTypes = { }; export default function (WrappedComponent) { - class withWindowDimensions extends Component { + const propTypes = { + forwardedRef: PropTypes.func, + }; + + const defaultProps = { + forwardedRef: () => {}, + }; + + class WithWindowDimensions extends Component { constructor(props) { super(props); @@ -56,10 +64,12 @@ export default function (WrappedComponent) { } render() { + const {forwardedRef, ...rest} = this.props; return ( ( + // eslint-disable-next-line react/jsx-props-no-spreading + + )); } export { diff --git a/src/libs/KeyboardAvoidingView/index.ios.js b/src/libs/KeyboardAvoidingView/index.ios.js new file mode 100644 index 00000000000..fc08d4f52fa --- /dev/null +++ b/src/libs/KeyboardAvoidingView/index.ios.js @@ -0,0 +1,30 @@ +/** + * This is a KeyboardAvoidingView only enabled for ios && disabled for all other platforms + * @param {Node} + */ +import React from 'react'; +import {KeyboardAvoidingView as KeyboardAvoidingViewComponent} from 'react-native'; +import PropTypes from 'prop-types'; +import styles from '../../styles/styles'; + +const propTypes = { + children: PropTypes.node, +}; +const defaultProps = { + children: null, +}; + +function KeyboardAvoidingView({children}) { + return ( + + {children} + + ); +} + +KeyboardAvoidingView.propTypes = propTypes; +KeyboardAvoidingView.defaultProps = defaultProps; +export default KeyboardAvoidingView; diff --git a/src/libs/KeyboardAvoidingView/index.js b/src/libs/KeyboardAvoidingView/index.js new file mode 100644 index 00000000000..894cbd36e6b --- /dev/null +++ b/src/libs/KeyboardAvoidingView/index.js @@ -0,0 +1,31 @@ +/** + * This is a KeyboardAvoidingView only enabled for ios && disabled for all other platforms + * @param {Node} + */ +import React from 'react'; +import {KeyboardAvoidingView as KeyboardAvoidingViewComponent} from 'react-native'; +import PropTypes from 'prop-types'; +import styles from '../../styles/styles'; + +const propTypes = { + children: PropTypes.node, +}; +const defaultProps = { + children: null, +}; + +function KeyboardAvoidingView({children}) { + return ( + + {children} + + ); +} + +KeyboardAvoidingView.propTypes = propTypes; +KeyboardAvoidingView.defaultProps = defaultProps; +export default KeyboardAvoidingView; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 2269e198f9e..d0c8b1988b9 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -179,6 +179,7 @@ class AuthScreens extends React.Component { cardStyleInterpolator: modalCardStyleInterpolator, animationEnabled: true, gestureDirection: 'horizontal', + cardOverlayEnabled: true, // This is a custom prop we are passing to custom navigator so that we will know to add a Pressable overlay // when displaying a modal. This allows us to dismiss by clicking outside on web / large screens. diff --git a/src/libs/Navigation/AppNavigator/ClickAwayHandler/ClickAwayHandler.js b/src/libs/Navigation/AppNavigator/ClickAwayHandler.js similarity index 55% rename from src/libs/Navigation/AppNavigator/ClickAwayHandler/ClickAwayHandler.js rename to src/libs/Navigation/AppNavigator/ClickAwayHandler.js index 9e1daab19fa..b7ffc552570 100644 --- a/src/libs/Navigation/AppNavigator/ClickAwayHandler/ClickAwayHandler.js +++ b/src/libs/Navigation/AppNavigator/ClickAwayHandler.js @@ -1,15 +1,19 @@ import React from 'react'; import PropTypes from 'prop-types'; import {Pressable} from 'react-native'; -import Navigation from '../../Navigation'; -import styles from '../../../../styles/styles'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; +import Navigation from '../Navigation'; +import styles from '../../../styles/styles'; const propTypes = { + // Whether a modal is currently being displayed isDisplayingModal: PropTypes.bool.isRequired, + + ...windowDimensionsPropTypes, }; const ClickAwayHandler = (props) => { - if (!props.isDisplayingModal) { + if (!props.isDisplayingModal || props.isSmallScreenWidth) { return null; } @@ -23,4 +27,4 @@ const ClickAwayHandler = (props) => { ClickAwayHandler.propTypes = propTypes; ClickAwayHandler.displayName = 'ClickAwayHandler'; -export default ClickAwayHandler; +export default withWindowDimensions(ClickAwayHandler); diff --git a/src/libs/Navigation/AppNavigator/ClickAwayHandler/index.js b/src/libs/Navigation/AppNavigator/ClickAwayHandler/index.js deleted file mode 100644 index 837018cba06..00000000000 --- a/src/libs/Navigation/AppNavigator/ClickAwayHandler/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import ClickAwayHandler from './ClickAwayHandler'; - -export default ClickAwayHandler; diff --git a/src/libs/Navigation/AppNavigator/ClickAwayHandler/index.native.js b/src/libs/Navigation/AppNavigator/ClickAwayHandler/index.native.js deleted file mode 100644 index 565061dadb5..00000000000 --- a/src/libs/Navigation/AppNavigator/ClickAwayHandler/index.native.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; -import ClickAwayHandler from './ClickAwayHandler'; - -const propTypes = { - ...windowDimensionsPropTypes, -}; - -const ClickAwayHandlerWithWindowDimensions = (props) => { - if (props.isSmallScreenWidth) { - return null; - } - - // eslint-disable-next-line react/jsx-props-no-spreading - return ; -}; - -ClickAwayHandlerWithWindowDimensions.propTypes = propTypes; -ClickAwayHandlerWithWindowDimensions.displayName = 'ClickAwayHandlerWithWindowDimensions'; -export default withWindowDimensions(ClickAwayHandlerWithWindowDimensions); diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index e5a39c19549..532b95e7a68 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -1,5 +1,5 @@ import React from 'react'; -import {createStackNavigator} from '@react-navigation/stack'; +import {createStackNavigator, CardStyleInterpolators} from '@react-navigation/stack'; import styles from '../../../styles/styles'; import ROUTES from '../../../ROUTES'; import NewChatPage from '../../../pages/NewChatPage'; @@ -29,6 +29,7 @@ const IOUBillModalStack = createStackNavigator(); const defaultSubRouteOptions = { cardStyle: styles.navigationScreenCardStyle, headerShown: false, + cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS, }; const IOUBillStackNavigator = () => ( @@ -163,7 +164,7 @@ const SettingsModalStackNavigator = () => ( component={SettingsProfilePage} /> { + const screenRoute = {type: 'route', name: screenName}; + const history = [...state.history].map(() => screenRoute); + history.push(screenRoute); + return CommonActions.reset({ + ...state, + routes: [{ + name: screenName, + params, + }], + history, + }); + }; +} + +export default { + pushDrawerRoute, +}; diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index d3d2e8811c8..30cce16a51a 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -6,6 +6,7 @@ import {getIsDrawerOpenFromState} from '@react-navigation/drawer'; import linkTo from './linkTo'; import ROUTES from '../../ROUTES'; import SCREENS from '../../SCREENS'; +import CustomActions from './CustomActions'; export const navigationRef = React.createRef(); @@ -49,7 +50,7 @@ function navigate(route = ROUTES.HOME) { const {reportID} = ROUTES.parseReportRouteParams(route); if (reportID) { - navigationRef.current.navigate(SCREENS.REPORT, {reportID}); + navigationRef.current.dispatch(CustomActions.pushDrawerRoute(SCREENS.REPORT, {reportID})); return; } diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 0b20414c5c8..e6df75231b6 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -48,7 +48,7 @@ export default { path: ROUTES.SETTINGS_PROFILE, exact: true, }, - Settings_Add_Seconday_Login: { + Settings_Add_Secondary_Login: { path: ROUTES.SETTINGS_ADD_LOGIN, }, }, diff --git a/src/libs/Url.js b/src/libs/Url.js index e166c127a83..004e9c5fe17 100644 --- a/src/libs/Url.js +++ b/src/libs/Url.js @@ -10,7 +10,23 @@ function addTrailingForwardSlash(url) { return url; } +/** + * Add / to the beginning and end of any URL if not present + * @param {String} url + * @returns {String} + */ +function wrapWithForwardSlash(url) { + const newUrl = addTrailingForwardSlash(url); + if (newUrl.startsWith('/')) { + return newUrl; + } + + return `/${newUrl}`; +} + + export { // eslint-disable-next-line import/prefer-default-export addTrailingForwardSlash, + wrapWithForwardSlash, }; diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index ddbb72a9e65..75a88cd4684 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -31,6 +31,7 @@ class ReportScreen extends React.Component { componentDidMount() { this.prepareTransition(); + this.storeCurrentlyViewedReport(); } componentDidUpdate(prevProps) { diff --git a/src/pages/home/report/EmojiPickerMenu/index.js b/src/pages/home/report/EmojiPickerMenu/index.js index e5ddfc56a44..99b69a7b858 100644 --- a/src/pages/home/report/EmojiPickerMenu/index.js +++ b/src/pages/home/report/EmojiPickerMenu/index.js @@ -8,10 +8,20 @@ import themeColors from '../../../../styles/themes/default'; import emojis from '../../../../../assets/emojis'; import EmojiPickerMenuItem from '../EmojiPickerMenuItem'; import TextInputFocusable from '../../../../components/TextInputFocusable'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; const propTypes = { // Function to add the selected emoji to the main compose text input onEmojiSelected: PropTypes.func.isRequired, + + // The ref to the search input (may be null on small screen widths) + forwardedRef: PropTypes.func, + + ...windowDimensionsPropTypes, +}; + +const defaultProps = { + forwardedRef: () => {}, }; class EmojiPickerMenu extends Component { @@ -43,6 +53,16 @@ class EmojiPickerMenu extends Component { }; } + componentDidMount() { + // This callback prop is used by the parent component using the constructor to + // get a ref to the inner textInput element e.g. if we do + // this.textInput = el} /> this will not + // return a ref to the component, but rather the HTML element by default + if (this.props.forwardedRef && _.isFunction(this.props.forwardedRef)) { + this.props.forwardedRef(this.searchInput); + } + } + /** * Filter the entire list of emojis to only emojis that have the search term in their keywords * @@ -98,17 +118,19 @@ class EmojiPickerMenu extends Component { render() { return ( - - this.searchInput = el} - /> - + {!this.props.isSmallScreenWidth && ( + + this.searchInput = el} + /> + + )} ( + // eslint-disable-next-line react/jsx-props-no-spreading + +))); diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 4551398667e..ecf3d7db91f 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -97,6 +97,9 @@ class ReportActionCompose extends React.Component { this.focus = this.focus.bind(this); this.comment = props.comment; this.shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus(); + this.focusEmojiSearchInput = this.focusEmojiSearchInput.bind(this); + + this.emojiSearchInput = null; this.state = { isFocused: this.shouldFocusInputOnScreenFocus, @@ -242,6 +245,15 @@ class ReportActionCompose extends React.Component { this.updateComment(this.textInput.value); } + /** + * Focus the search input in the emoji picker. + */ + focusEmojiSearchInput() { + if (this.emojiSearchInput) { + this.emojiSearchInput.focus(); + } + } + /** * Add a new comment to this chat * @@ -383,6 +395,7 @@ class ReportActionCompose extends React.Component { this.emojiSearchInput = el} /> ({ + login: personalDetails.login, + text: personalDetails.displayName, + alternateText: personalDetails.login, + icons: [personalDetails.avatar], + keyForList: personalDetails.login, + })); this.state = { currentStepIndex: 0, - participants: [], + participants: participantsWithDetails, // amount is currency in decimal format amount: '', selectedCurrency: 'USD', comment: '', }; + + // Skip IOUParticipants step if participants are passed in + if (participants.length) { + // The steps to be shown within the create IOU flow. + this.steps = [Steps.IOUAmount, Steps.IOUConfirm]; + } else { + this.steps = [Steps.IOUAmount, Steps.IOUParticipants, Steps.IOUConfirm]; + } } componentDidMount() { @@ -84,7 +119,6 @@ class IOUModal extends Component { * * @returns {String} */ - getTitleForStep() { const currentStepIndex = this.state.currentStepIndex; if (currentStepIndex === 1 || currentStepIndex === 2) { @@ -93,7 +127,7 @@ class IOUModal extends Component { if (currentStepIndex === 0) { return this.props.hasMultipleParticipants ? 'Split Bill' : 'Request Money'; } - return steps[currentStepIndex] || ''; + return this.steps[currentStepIndex] || ''; } addParticipants(participants) { @@ -118,7 +152,7 @@ class IOUModal extends Component { * Navigate to the previous IOU step if possible */ navigateToNextStep() { - if (this.state.currentStepIndex >= steps.length - 1) { + if (this.state.currentStepIndex >= this.steps.length - 1) { return; } this.setState(prevState => ({ @@ -178,7 +212,7 @@ class IOUModal extends Component { } render() { - const currentStep = steps[this.state.currentStepIndex]; + const currentStep = this.steps[this.state.currentStepIndex]; return ( <> @@ -250,8 +284,16 @@ IOUModal.defaultProps = defaultProps; IOUModal.displayName = 'IOUModal'; export default withOnyx({ + report: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, + }, iousReport: { key: ONYXKEYS.COLLECTION.REPORT_IOUS, }, - iou: {key: ONYXKEYS.IOU}, + iou: { + key: ONYXKEYS.IOU, + }, + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS, + }, })(IOUModal); diff --git a/src/pages/settings/AddSecondaryLoginPage.js b/src/pages/settings/AddSecondaryLoginPage.js index 7ac3f1e6deb..4527654d97f 100644 --- a/src/pages/settings/AddSecondaryLoginPage.js +++ b/src/pages/settings/AddSecondaryLoginPage.js @@ -14,6 +14,7 @@ import ONYXKEYS from '../../ONYXKEYS'; import ButtonWithLoader from '../../components/ButtonWithLoader'; import ROUTES from '../../ROUTES'; import CONST from '../../CONST'; +import KeyboardAvoidingView from '../../libs/KeyboardAvoidingView'; const propTypes = { /* Onyx Props */ @@ -96,60 +97,62 @@ class AddSecondaryLoginPage extends Component { render() { return ( - Navigation.navigate(ROUTES.SETTINGS_PROFILE)} - onCloseButtonPress={() => Navigation.dismissModal()} - /> - - - - {this.formType === CONST.LOGIN_TYPE.PHONE - ? 'Enter your preferred phone number and password to send a validation link.' - : 'Enter your preferred email address and password to send a validation link.'} - - - - {this.formType === CONST.LOGIN_TYPE.PHONE ? 'Phone Number' : 'Email Address'} + + Navigation.navigate(ROUTES.SETTINGS_PROFILE)} + onCloseButtonPress={() => Navigation.dismissModal()} + /> + + + + {this.formType === CONST.LOGIN_TYPE.PHONE + ? 'Enter your preferred phone number and password to send a validation link.' + : 'Enter your preferred email address and password to send a validation link.'} - this.setState({login})} - autoFocus - keyboardType={this.formType === CONST.LOGIN_TYPE.PHONE - ? CONST.KEYBOARD_TYPE.PHONE_PAD : undefined} - returnKeyType="done" - /> - - - Password - this.setState({password})} - secureTextEntry - autoCompleteType="password" - textContentType="password" - onSubmitEditing={this.submitForm} - /> - - {!_.isEmpty(this.props.user.error) && ( + + + {this.formType === CONST.LOGIN_TYPE.PHONE ? 'Phone Number' : 'Email Address'} + + this.setState({login})} + autoFocus + keyboardType={this.formType === CONST.LOGIN_TYPE.PHONE + ? CONST.KEYBOARD_TYPE.PHONE_PAD : undefined} + returnKeyType="done" + /> + + + Password + this.setState({password})} + secureTextEntry + autoCompleteType="password" + textContentType="password" + onSubmitEditing={this.submitForm} + /> + + {!_.isEmpty(this.props.user.error) && ( {this.props.user.error} - )} - - - + )} + + + + - + ); } diff --git a/src/styles/styles.js b/src/styles/styles.js index 516f6cd5069..3f3fa97e678 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -838,6 +838,7 @@ const styles = { emojiText: { fontFamily: fontFamily.GTA_BOLD, fontSize: variables.iconSizeLarge, + textAlign: 'center', ...spacing.pv1, ...spacing.ph2, }, @@ -1311,39 +1312,6 @@ const styles = { scrollbarWidth: 'none', }, - codeWordWrapper: { - height: 10, - }, - - codeWordStyle: { - borderLeftWidth: 0, - borderRightWidth: 0, - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - flexBasis: 'auto', - paddingLeft: 0, - paddingRight: 0, - justifyContent: 'center', - marginVertical: -2, - top: -1, - }, - - codeFirstWordStyle: { - borderLeftWidth: 1, - borderTopLeftRadius: 4, - borderBottomLeftRadius: 4, - paddingLeft: 5, - }, - - codeLastWordStyle: { - borderRightWidth: 1, - borderTopRightRadius: 4, - borderBottomRightRadius: 4, - paddingRight: 5, - }, - fullScreenLoading: { backgroundColor: themeColors.componentBG, opacity: 0.8, @@ -1364,6 +1332,8 @@ const styles = { const baseCodeTagStyles = { borderWidth: 1, borderRadius: 5, + marginTop: 4, + marginBottom: 4, borderColor: themeColors.border, backgroundColor: themeColors.textBackground, }; @@ -1420,9 +1390,9 @@ const webViewStyles = { ...baseCodeTagStyles, paddingLeft: 5, paddingRight: 5, + paddingBottom: 2, + alignSelf: 'flex-start', fontFamily: fontFamily.MONOSPACE, - lineHeight: 18, - fontSize: 13, }, img: { @@ -1435,7 +1405,6 @@ const webViewStyles = { baseFontStyle: { color: themeColors.text, fontSize: variables.fontSizeNormal, - lineHeight: variables.fontSizeNormalHeight, fontFamily: fontFamily.GTA, }, }; diff --git a/src/styles/variables.js b/src/styles/variables.js index 4ca7fef9136..9b70c5d5b07 100644 --- a/src/styles/variables.js +++ b/src/styles/variables.js @@ -12,7 +12,6 @@ export default { fontSizeExtraSmall: 9, fontSizeLabel: 13, fontSizeNormal: 15, - fontSizeNormalHeight: 20, fontSizeLarge: 17, fontSizeh1: 19, iconSizeExtraSmall: 12,