From 41cba17a6742c8bdc00fb51e1a834ce3103c7767 Mon Sep 17 00:00:00 2001 From: xem Date: Wed, 27 Jul 2022 16:35:35 +0200 Subject: [PATCH 01/22] QMAPS-2129 add startAt / arriveBy options in public transports (draft/poc) --- src/adapters/direction_api.js | 12 +++++--- src/panel/direction/DirectionForm/index.jsx | 24 +++++++++++++++ .../RouteStartEndTimes/index.tsx | 3 ++ src/panel/direction/index.jsx | 30 +++++++++++++++++-- src/scss/includes/direction-form.scss | 9 ++++++ 5 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/adapters/direction_api.js b/src/adapters/direction_api.js index fe32a9f79..c4447b114 100644 --- a/src/adapters/direction_api.js +++ b/src/adapters/direction_api.js @@ -48,18 +48,20 @@ const modeToProfile = { }; export default class DirectionApi { - static async search(start, end, mode) { + static async search(start, end, mode, startAt, arriveBy) { if (mode === modes.CYCLING) { // Fetch routes without ferry in priority - const firstSearch = await DirectionApi._search(start, end, mode, { exclude: 'ferry' }); + const firstSearch = await DirectionApi._search(start, end, mode, null, null, { + exclude: 'ferry', + }); if (firstSearch.data && firstSearch.data.routes && firstSearch.data.routes.length > 0) { return firstSearch; } } - return DirectionApi._search(start, end, mode); + return DirectionApi._search(start, end, mode, startAt, arriveBy); } - static async _search(start, end, mode, { exclude = '' } = {}) { + static async _search(start, end, mode, startAt, arriveBy, { exclude = '' } = {}) { const apiProfile = modeToProfile[mode]; let directionsUrl = directionConfig.apiBaseUrl; const userLang = window.getLang(); @@ -92,6 +94,8 @@ export default class DirectionApi { } const s_start = poiToMapBoxCoordinates(start); const s_end = poiToMapBoxCoordinates(end); + if (startAt) directionsParams.depart_at = startAt; + if (arriveBy) directionsParams.arrive_by = arriveBy; directionsUrl = `${directionsUrl}${s_start};${s_end}`; let response = null; try { diff --git a/src/panel/direction/DirectionForm/index.jsx b/src/panel/direction/DirectionForm/index.jsx index 81c5872a4..6fae230fe 100644 --- a/src/panel/direction/DirectionForm/index.jsx +++ b/src/panel/direction/DirectionForm/index.jsx @@ -19,6 +19,9 @@ const DirectionForm = ({ isInitializing, originInputText, destinationInputText, + onStartNow, + onStartAt, + onArriveBy, }) => { const { _ } = useI18n(); const { isMobile } = useDevice(); @@ -93,6 +96,27 @@ const DirectionForm = ({ + {activeVehicle === 'publicTransport' && ( +
+

+ Start now +

+

+ or: Start at {' '} + +

+

+ or: Arrive by {' '} + +

+
+ )} ); }; diff --git a/src/panel/direction/RouteSummaryInfo/RouteStartEndTimes/index.tsx b/src/panel/direction/RouteSummaryInfo/RouteStartEndTimes/index.tsx index 00d52a4a5..bfc27d955 100644 --- a/src/panel/direction/RouteSummaryInfo/RouteStartEndTimes/index.tsx +++ b/src/panel/direction/RouteSummaryInfo/RouteStartEndTimes/index.tsx @@ -17,10 +17,13 @@ const RouteStartEndTimes: React.FunctionComponent = ({ return null; } + const dateFormatter = getTimeFormatter({ month: '2-digit', day: '2-digit' }); const timeFormatter = getTimeFormatter({ hour: '2-digit', minute: '2-digit' }); return (
+ {dateFormatter.format(new Date(stripTimeZone(start)))} + {' - '} {timeFormatter.format(new Date(stripTimeZone(start)))} {' - '} {timeFormatter.format(new Date(stripTimeZone(end)))} diff --git a/src/panel/direction/index.jsx b/src/panel/direction/index.jsx index fed513efe..654044f9d 100644 --- a/src/panel/direction/index.jsx +++ b/src/panel/direction/index.jsx @@ -65,6 +65,8 @@ class DirectionPanel extends React.Component { isInitializing: true, originInputText: '', destinationInputText: '', + startAt: null, + arriveBy: null, }; this.restorePoints(props); @@ -180,7 +182,7 @@ class DirectionPanel extends React.Component { } computeRoutes = async () => { - const { origin, destination, vehicle } = this.state; + const { origin, destination, vehicle, startAt, arriveBy } = this.state; if (origin && destination) { this.setState({ isDirty: false, @@ -191,7 +193,13 @@ class DirectionPanel extends React.Component { const currentQueryId = ++this.lastQueryId; fire('set_origin', origin); fire('set_destination', destination); - const directionResponse = await DirectionApi.search(origin, destination, vehicle); + const directionResponse = await DirectionApi.search( + origin, + destination, + vehicle, + startAt, + arriveBy + ); // A more recent query was done in the meantime, ignore this result silently if (currentQueryId !== this.lastQueryId) { return; @@ -330,6 +338,21 @@ class DirectionPanel extends React.Component { } }; + startNow = () => { + this.setState({ startAt: null, arriveBy: null }, this.update); + this.computeRoutes(); + }; + + startAt = datetime => { + this.setState({ startAt: datetime, arriveBy: null }, this.update); + this.computeRoutes(); + }; + + arriveBy = datetime => { + this.setState({ startAt: null, arriveBy: datetime }, this.update); + this.computeRoutes(); + }; + render() { const { origin, @@ -361,6 +384,9 @@ class DirectionPanel extends React.Component { onSelectVehicle={this.onSelectVehicle} activeVehicle={vehicle} isInitializing={isInitializing} + onStartNow={this.startNow} + onStartAt={this.startAt} + onArriveBy={this.arriveBy} /> ); diff --git a/src/scss/includes/direction-form.scss b/src/scss/includes/direction-form.scss index 74531297b..3a40cb159 100644 --- a/src/scss/includes/direction-form.scss +++ b/src/scss/includes/direction-form.scss @@ -1,6 +1,15 @@ @import './vehicleSelector.scss'; .direction-form { + + .poc p { + margin: 5px 0; + } + .poc input, .poc button { + border: 1px solid #000; + } + + display: flex; flex-direction: column; padding: var(--spacing-m); From 09227287b1a5f097819afc6d1f2449e08162eaa3 Mon Sep 17 00:00:00 2001 From: pascaloliv Date: Fri, 29 Jul 2022 11:26:41 +0200 Subject: [PATCH 02/22] Rework Actions init --- .eslintrc.json | 2 +- .github/workflows/ci.yml | 59 ++++++++++++++++++++++++++--------- .github/workflows/docker.yml | 6 ++-- .gitignore | 1 + grep | 0 package.json | 2 +- src/adapters/poi/idunn_poi.ts | 13 ++------ tests/unit.js | 5 ++- 8 files changed, 59 insertions(+), 29 deletions(-) create mode 100644 grep diff --git a/.eslintrc.json b/.eslintrc.json index 8707662d1..90bbd3a1f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -63,7 +63,7 @@ } }, { - "files": ["bin/**/*.js", "build/**/*.js"], + "files": ["bin/**/*.js", "build/**/*.js", "tests/**/*.js"], "rules": { "@typescript-eslint/no-var-requires": "off" } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e992a707b..a8aac635b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,16 +1,19 @@ -name: Erdapfel CI +name: Qwant Maps - CI -on: [push, pull_request] +on: [push] jobs: - build: + Build: runs-on: ubuntu-18.04 steps: + - name: Init summary markown + run: echo '# 🗺 Qwant Maps - Build summary' >> $GITHUB_STEP_SUMMARY + - name: Install system dependencies run: | sudo apt-get update - sudo apt-get install -yq libstdc++6 gettext + sudo apt-get install -yq libstdc++6 gettext jq - uses: actions/checkout@v2 @@ -24,25 +27,53 @@ jobs: npm install -g npm@8.5.0 npm ci - - name: Build - run: TEST=true npm run build -- --mode=production + - name: Run build + run: | + TEST=true npm run build -- --mode=production + echo "- ## 🤖 Project build" >> $GITHUB_STEP_SUMMARY + echo "✅ Success" >> $GITHUB_STEP_SUMMARY - - name: Run coverage - run: npm run ts:coverage + - name: Run ESLint + run: | + npm run lint + echo "- ## 🤖 ESLint" >> $GITHUB_STEP_SUMMARY + echo "✅ Success" >> $GITHUB_STEP_SUMMARY - - name: Run ts/js files count - run: npm run ts:count + - name: Run TypeScript migration files count + run: | + echo "- ## 🤖 TypeScript migration" >> $GITHUB_STEP_SUMMARY + npm run ts:count >> $GITHUB_STEP_SUMMARY - - name: Run linter - run: npm run lint + - name: Run TypeScript coverage + id: tscov + run: | + echo "- ## 🤖 TypeScript coverage" >> $GITHUB_STEP_SUMMARY + npm run ts:coverage + echo "📊 Total coverage: $(cat coverage-ts/typescript-coverage.json | jq '.percentage')% - Details: $(cat coverage-ts/typescript-coverage.json | jq '.covered')/$(cat coverage-ts/typescript-coverage.json | jq '.total') lines covered" >> $GITHUB_STEP_SUMMARY - - name: Run tests - run: npm run test + - name: Run unit tests + run: | + npm run unit-test + echo "- ## 🤖 Unit tests" >> $GITHUB_STEP_SUMMARY + echo "✅ Success" >> $GITHUB_STEP_SUMMARY + echo "📊 Lines coverage: $(cat coverage/coverage-summary.json | jq '.total.lines.pct')% - Details: $(cat coverage/coverage-summary.json | jq '.total.lines.covered')/$(cat coverage/coverage-summary.json | jq '.total.lines.total') lines covered" >> $GITHUB_STEP_SUMMARY + echo "📊 Statements coverage: $(cat coverage/coverage-summary.json | jq '.total.statements.pct')% - Details: $(cat coverage/coverage-summary.json | jq '.total.statements.covered')/$(cat coverage/coverage-summary.json | jq '.total.statements.total') statements covered" >> $GITHUB_STEP_SUMMARY + echo "📊 Functions coverage: $(cat coverage/coverage-summary.json | jq '.total.functions.pct')% - Details: $(cat coverage/coverage-summary.json | jq '.total.functions.covered')/$(cat coverage/coverage-summary.json | jq '.total.functions.total') functions covered" >> $GITHUB_STEP_SUMMARY + echo "📊 Branches coverage: $(cat coverage/coverage-summary.json | jq '.total.branches.pct')% - Details: $(cat coverage/coverage-summary.json | jq '.total.branches.covered')/$(cat coverage/coverage-summary.json | jq '.total.branches.total') branches covered" >> $GITHUB_STEP_SUMMARY + + - name: Run integration tests + run: | + npm run integration-test + echo "- ## 🤖 Integration tests" >> $GITHUB_STEP_SUMMARY + echo "✅ Success" >> $GITHUB_STEP_SUMMARY - name: Deploy Storybook if: "contains(github.ref_name, 'master')" run: | git remote set-url origin https://git:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git npm run deploy-storybook -- -u "github-actions-bot " + echo "- ## 🎨 Storybook + echo "- ✅ [Successfully deployed](https://qwant.github.io/erdapfel/) 🚀" >> $GITHUB_STEP_SUMMARY + env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index cbf366cff..d36a57e93 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,3 +1,5 @@ +name: Qwant Maps - Docker image + on: push: branches: @@ -9,7 +11,7 @@ env: DOCKER_IMAGE_BASENAME: qwantresearch/erdapfel jobs: - build_docker: + BuildDockerImage: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -32,5 +34,5 @@ jobs: - run: docker build --label "org.label-schema.vcs-ref=$GITHUB_SHA" -t $DOCKER_IMAGE . - - if: github.event_name == 'push' + - if: github.event_name == 'push' run: docker push $DOCKER_IMAGE diff --git a/.gitignore b/.gitignore index 695389f0b..aae178561 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ public/message # Code coverage coverage-ts/ +coverage/ # Static Storybook Build storybook-static/ diff --git a/grep b/grep new file mode 100644 index 000000000..e69de29bb diff --git a/package.json b/package.json index 910456c28..e7b9bb4e9 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "prettier:write": "prettier --write '{src,bin,build,tests}/**/*.{js,jsx,ts,tsx}'", "analyze": "webpack --config build/webpack.config.js --mode=production --profile --json > stats.json && webpack-bundle-analyzer stats.json public/build/javascript", "ts:coverage": "typescript-coverage-report", - "ts:count": "cloc src --fullpath --include-lang TypeScript,JavaScript --force-lang='Javascript',jsx --not-match-d \"(node_modules|coverage-ts|tmp|build)\" --by-percent cm", + "ts:count": "cloc src --fullpath --include-lang TypeScript,JavaScript --force-lang='Javascript',jsx --not-match-d \"(node_modules|coverage-ts|tmp|build)\" --by-percent cm --md", "ts:codegen-idunn": "node build/generateIdunnTypes.mjs && eslint --fix @types/idunn.ts", "storybook": "start-storybook -p 6006", "build-storybook": "build-storybook", diff --git a/src/adapters/poi/idunn_poi.ts b/src/adapters/poi/idunn_poi.ts index 3eae933b8..ae299c32b 100644 --- a/src/adapters/poi/idunn_poi.ts +++ b/src/adapters/poi/idunn_poi.ts @@ -1,4 +1,4 @@ -import Poi from './poi'; +import Poi, { TPoi } from './poi'; import Ajax from '../../libs/ajax'; import nconf from '@qwant/nconf-getter'; import Error from '../../adapters/error'; @@ -30,16 +30,9 @@ export default class IdunnPoi extends Poi { const latLng = { lat: (rawPoi?.geometry?.coordinates as number[])[1], lng: (rawPoi?.geometry?.coordinates as number[])[0], - }; + } as TPoi['latLon']; - super( - rawPoi.id, - rawPoi.name, - rawPoi.type, - latLng as any, // TODO: Check why lat/lng and not lat/lon - rawPoi.class_name, - rawPoi.subclass_name - ); + super(rawPoi.id, rawPoi.name, rawPoi.type, latLng, rawPoi.class_name, rawPoi.subclass_name); this.blocks = rawPoi.blocks; this.localName = rawPoi.local_name; this.bbox = rawPoi?.geometry?.bbox as [number, number, number, number]; // TODO: Check if there is always a bbox on Idunn Place diff --git a/tests/unit.js b/tests/unit.js index 038a7feaa..0e36b936f 100644 --- a/tests/unit.js +++ b/tests/unit.js @@ -3,7 +3,10 @@ module.exports = { testPathIgnorePatterns: ['/node_modules/'], rootDir: __dirname + '/../', verbose: true, - collectCoverage: false, + collectCoverage: true, + collectCoverageFrom: ['src/**/*.{js,ts}'], + coveragePathIgnorePatterns: ['mock.js', 'mock.ts'], + coverageReporters: ['json-summary'], globals: { __config: require('@qwant/nconf-builder').get_without_check(), }, From 9ecc6cdaf115eb6b0c297b3f9be8d127ff090a99 Mon Sep 17 00:00:00 2001 From: pascaloliv Date: Wed, 3 Aug 2022 10:46:26 +0200 Subject: [PATCH 03/22] Replace mapbox-gl typescript patch as @types/mapbox-gl PR was merged --- package-lock.json | 30 +++++++++++++----------------- package.json | 2 +- src/adapters/pois_styles.ts | 18 ++---------------- 3 files changed, 16 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index afd5662db..4454a47f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@turf/line-slice-along": "^5.1.5", "@turf/meta": "^6.0.2", "@types/color": "^3.0.3", + "@types/mapbox-gl": "^1.13.4", "alt-route-labeller": "^0.3.1", "axios": "^0.21.2", "bunyan": "^1.8.12", @@ -73,7 +74,6 @@ "@storybook/react": "^6.4.21", "@storybook/testing-library": "^0.0.9", "@svgr/webpack": "^5.5.0", - "@types/mapbox-gl": "^1.13.1", "@types/react": "^16.14.23", "@types/react-dom": "^16.9.14", "@typescript-eslint/eslint-plugin": "^5.14.0", @@ -7830,10 +7830,9 @@ "dev": true }, "node_modules/@types/geojson": { - "version": "7946.0.8", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", - "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==", - "dev": true + "version": "7946.0.10", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", + "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" }, "node_modules/@types/glob": { "version": "7.2.0", @@ -7912,10 +7911,9 @@ "dev": true }, "node_modules/@types/mapbox-gl": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-1.13.3.tgz", - "integrity": "sha512-qKcbA5ZKhGwqU5/ti8zC0nbqkxqBYi9EUo4bIjB7MK8ve+mBhbJBcYRjTYWYD7IhHCQfvPGVSnVlesH6yZ2Fiw==", - "dev": true, + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-1.13.4.tgz", + "integrity": "sha512-yoHSG4J9uNTlpQk5GXzbaC6b+XBHGljT10yM5fX2ZAGcH2CVb96TF2uFdoYFehsmeZqlo3txNsbMWpiGh7oy2g==", "dependencies": { "@types/geojson": "*" } @@ -38656,10 +38654,9 @@ "dev": true }, "@types/geojson": { - "version": "7946.0.8", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", - "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==", - "dev": true + "version": "7946.0.10", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", + "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" }, "@types/glob": { "version": "7.2.0", @@ -38738,10 +38735,9 @@ "dev": true }, "@types/mapbox-gl": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-1.13.3.tgz", - "integrity": "sha512-qKcbA5ZKhGwqU5/ti8zC0nbqkxqBYi9EUo4bIjB7MK8ve+mBhbJBcYRjTYWYD7IhHCQfvPGVSnVlesH6yZ2Fiw==", - "dev": true, + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-1.13.4.tgz", + "integrity": "sha512-yoHSG4J9uNTlpQk5GXzbaC6b+XBHGljT10yM5fX2ZAGcH2CVb96TF2uFdoYFehsmeZqlo3txNsbMWpiGh7oy2g==", "requires": { "@types/geojson": "*" } diff --git a/package.json b/package.json index 910456c28..8cad35a53 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,6 @@ "@storybook/react": "^6.4.21", "@storybook/testing-library": "^0.0.9", "@svgr/webpack": "^5.5.0", - "@types/mapbox-gl": "^1.13.1", "@types/react": "^16.14.23", "@types/react-dom": "^16.9.14", "@typescript-eslint/eslint-plugin": "^5.14.0", @@ -123,6 +122,7 @@ "@turf/line-slice-along": "^5.1.5", "@turf/meta": "^6.0.2", "@types/color": "^3.0.3", + "@types/mapbox-gl": "^1.13.4", "alt-route-labeller": "^0.3.1", "axios": "^0.21.2", "bunyan": "^1.8.12", diff --git a/src/adapters/pois_styles.ts b/src/adapters/pois_styles.ts index 3dc7c9c84..0975c8465 100644 --- a/src/adapters/pois_styles.ts +++ b/src/adapters/pois_styles.ts @@ -1,5 +1,5 @@ import { ACTION_BLUE_BASE, RED_DARKER, GREY_BLACK } from 'src/libs/colors'; -import { FilterOptions, Map } from 'mapbox-gl'; +import { Map } from 'mapbox-gl'; export const FILTERED_POIS_PIN_STYLES = { type: 'symbol', @@ -45,21 +45,7 @@ export const FILTERED_POIS_LABEL_STYLES = { type SetPaintPropertyValue = T | Array>; -export const setPoiHoverStyle = ( - map: Map & { - /* TODO: Remove this patch - PR on DefinetelyTyped - URL: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/61382 - */ - setPaintProperty( - layer: string, - name: string, - value: SetPaintPropertyValue, - options?: FilterOptions - ): Map; - }, - layer: string -) => { +export const setPoiHoverStyle = (map: Map, layer: string) => { if (!map.getPaintProperty) { // @MAPBOX: This method isn't implemented by the Mapbox-GL mock return; From 679e1c1e144918863b51f07cb093c5e6a5f0e775 Mon Sep 17 00:00:00 2001 From: sdirollo Date: Wed, 3 Aug 2022 17:13:51 +0200 Subject: [PATCH 04/22] add category airport --- config/categories.js | 1 + config/categories.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/config/categories.js b/config/categories.js index 789b8f638..556df391c 100644 --- a/config/categories.js +++ b/config/categories.js @@ -145,5 +145,6 @@ window.categories = [ _('stadium'), _('viewpoint'), _('charging station'), + _('airport'), _('osteopathy'), ]; diff --git a/config/categories.yml b/config/categories.yml index f05096e5c..072a2830a 100644 --- a/config/categories.yml +++ b/config/categories.yml @@ -336,5 +336,7 @@ name: viewpoint - label: _('charging station') name: charging_station +- label: _('airport') + name: airport - label: _('osteopathy') name: health_osteopathy From 5248585c61094e601f847c727af6c434b9b0387b Mon Sep 17 00:00:00 2001 From: sdirollo Date: Mon, 8 Aug 2022 18:26:49 +0200 Subject: [PATCH 05/22] Add distance on walking public transport instructions --- language/message/de.po | 3 +++ language/message/es.po | 3 +++ language/message/fr.po | 3 +++ language/message/it.po | 3 +++ src/libs/route_utils.js | 5 ++++- 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/language/message/de.po b/language/message/de.po index 1816a3939..3d7888bac 100644 --- a/language/message/de.po +++ b/language/message/de.po @@ -1346,6 +1346,9 @@ msgstr "Zurück gehen" msgid "{modifier} on {name}" msgstr "{modifier} auf {name}" +msgid "{modifier} on {length} m" +msgstr "{modifier} auf {length} m" + msgid "Walk on {walkDistance}" msgstr "Lauf über {walkDistance}" diff --git a/language/message/es.po b/language/message/es.po index 9f9c7e41f..5b05ab7f7 100644 --- a/language/message/es.po +++ b/language/message/es.po @@ -1340,6 +1340,9 @@ msgstr "Volver" msgid "{modifier} on {name}" msgstr "{modifier} en la {name}" +msgid "{modifier} on {length} m" +msgstr "{modifier} en la {length} m" + msgid "Walk on {walkDistance}" msgstr "Camina {walkDistance}" diff --git a/language/message/fr.po b/language/message/fr.po index 700707be3..6e8d14489 100644 --- a/language/message/fr.po +++ b/language/message/fr.po @@ -1336,6 +1336,9 @@ msgstr "Faites demi tour" msgid "{modifier} on {name}" msgstr "{modifier} sur {name}" +msgid "{modifier} on {length} m" +msgstr "{modifier} sur {length} m" + msgid "Walk on {walkDistance}" msgstr "Marchez sur {walkDistance}" diff --git a/language/message/it.po b/language/message/it.po index bd8c89442..eeca6842f 100644 --- a/language/message/it.po +++ b/language/message/it.po @@ -1343,6 +1343,9 @@ msgstr "Torna indietro" msgid "{modifier} on {name}" msgstr "{modifier} su {name}" +msgid "{modifier} on {length} m" +msgstr "{modifier} su {length} m" + msgid "Walk on {walkDistance}" msgstr "Cammina per {walkDistance}" diff --git a/src/libs/route_utils.js b/src/libs/route_utils.js index 8cf1f5f35..cf5fac01b 100644 --- a/src/libs/route_utils.js +++ b/src/libs/route_utils.js @@ -101,7 +101,10 @@ export const walkingManeuver = maneuver => { const context = { modifier: stringifyModifier[maneuver.modifier], name: maneuver.detail.name, + length: maneuver.detail.length, }; - return maneuver.detail.name ? _('{modifier} on {name}', 'direction', context) : context.modifier; + return maneuver.detail.name + ? _('{modifier} on {name}', 'direction', context) + : _('{modifier} on {length} m', 'direction', context); }; From 10b37e18afaf0694512129f53cb766dbef038f03 Mon Sep 17 00:00:00 2001 From: sdirollo Date: Tue, 9 Aug 2022 11:23:37 +0200 Subject: [PATCH 06/22] fix translation for walking instructions --- language/message/de.po | 7 +++++-- language/message/es.po | 7 +++++-- language/message/fr.po | 7 +++++-- language/message/it.po | 7 +++++-- src/libs/route_utils.js | 6 +++++- 5 files changed, 25 insertions(+), 9 deletions(-) diff --git a/language/message/de.po b/language/message/de.po index 3d7888bac..282b12bb3 100644 --- a/language/message/de.po +++ b/language/message/de.po @@ -1346,8 +1346,11 @@ msgstr "Zurück gehen" msgid "{modifier} on {name}" msgstr "{modifier} auf {name}" -msgid "{modifier} on {length} m" -msgstr "{modifier} auf {length} m" +msgid "{modifier} in {length} m" +msgstr "{modifier} in {length} m" + +msgid "{modifier} during {length} m" +msgstr "{modifier} während {length} m" msgid "Walk on {walkDistance}" msgstr "Lauf über {walkDistance}" diff --git a/language/message/es.po b/language/message/es.po index 5b05ab7f7..22e2ba9c2 100644 --- a/language/message/es.po +++ b/language/message/es.po @@ -1340,8 +1340,11 @@ msgstr "Volver" msgid "{modifier} on {name}" msgstr "{modifier} en la {name}" -msgid "{modifier} on {length} m" -msgstr "{modifier} en la {length} m" +msgid "{modifier} in {length} m" +msgstr "{modifier} en {length} m" + +msgid "{modifier} during {length} m" +msgstr "{modifier} durante {length} m" msgid "Walk on {walkDistance}" msgstr "Camina {walkDistance}" diff --git a/language/message/fr.po b/language/message/fr.po index 6e8d14489..fccf55eea 100644 --- a/language/message/fr.po +++ b/language/message/fr.po @@ -1336,8 +1336,11 @@ msgstr "Faites demi tour" msgid "{modifier} on {name}" msgstr "{modifier} sur {name}" -msgid "{modifier} on {length} m" -msgstr "{modifier} sur {length} m" +msgid "{modifier} in {length} m" +msgstr "{modifier} dans {length} m" + +msgid "{modifier} during {length} m" +msgstr "{modifier} pendant {length} m" msgid "Walk on {walkDistance}" msgstr "Marchez sur {walkDistance}" diff --git a/language/message/it.po b/language/message/it.po index eeca6842f..ec6ba7e70 100644 --- a/language/message/it.po +++ b/language/message/it.po @@ -1343,8 +1343,11 @@ msgstr "Torna indietro" msgid "{modifier} on {name}" msgstr "{modifier} su {name}" -msgid "{modifier} on {length} m" -msgstr "{modifier} su {length} m" +msgid "{modifier} in {length} m" +msgstr "{modifier} in {length} m" + +msgid "{modifier} during {length} m" +msgstr "{modifier} durante {length} m" msgid "Walk on {walkDistance}" msgstr "Cammina per {walkDistance}" diff --git a/src/libs/route_utils.js b/src/libs/route_utils.js index cf5fac01b..1b638e2e7 100644 --- a/src/libs/route_utils.js +++ b/src/libs/route_utils.js @@ -106,5 +106,9 @@ export const walkingManeuver = maneuver => { return maneuver.detail.name ? _('{modifier} on {name}', 'direction', context) - : _('{modifier} on {length} m', 'direction', context); + : maneuver.modifier === 'straight' || + maneuver.modifier === 'slight right' || + maneuver.modifier === 'slight left' + ? _('{modifier} during {length} m', 'direction', context) + : _('{modifier} in {length} m', 'direction', context); }; From 2c8ae9f00948a025b2b31b676db835e43683b515 Mon Sep 17 00:00:00 2001 From: xem Date: Tue, 23 Aug 2022 10:32:07 +0200 Subject: [PATCH 07/22] QMAPS-2843 design (#1388) * QMAPS-2843 branding * QMAPS-2843 directions * QMAPS-2843 directions * QMAPS-2843 directions * QMAPS-2843 try to fix github --- .../direction_icons/components-icons-pin-2.svg | 6 ++++++ public/images/direction_icons/origin-focus.svg | 2 +- public/images/direction_icons/origin.svg | 12 +++--------- public/images/direction_icons/pin-focus.svg | 2 +- src/components/ui/Chevron.jsx | 2 +- src/libs/route_utils.js | 2 +- src/panel/direction/RouteSummaryInfo/index.jsx | 6 +++--- .../RoadMap/Default/DefaultRoadMapItem/index.jsx | 2 +- .../PublicTransportRoadMapItem/index.jsx | 6 +++++- src/panel/service/ServicePanelDesktop.jsx | 2 +- src/scss/includes/direction-field.scss | 3 ++- src/scss/includes/direction-form.scss | 6 ++++-- src/scss/includes/direction.scss | 15 +++++++++------ src/scss/includes/main.scss | 2 +- src/scss/includes/panels/favorite_panel.scss | 2 +- src/scss/includes/panels/history_panel.scss | 5 +++-- src/scss/includes/panels/poi_panel.scss | 4 ++++ src/scss/includes/panels/timetable.scss | 2 +- src/scss/includes/routes.scss | 5 +++-- src/scss/includes/search_form.scss | 7 ++++++- src/scss/includes/suggest.scss | 4 ++++ src/scss/includes/vehicleSelector.scss | 2 +- tests/units/route_utils.js | 4 ++-- 23 files changed, 64 insertions(+), 39 deletions(-) create mode 100644 public/images/direction_icons/components-icons-pin-2.svg diff --git a/public/images/direction_icons/components-icons-pin-2.svg b/public/images/direction_icons/components-icons-pin-2.svg new file mode 100644 index 000000000..5ec4f9e34 --- /dev/null +++ b/public/images/direction_icons/components-icons-pin-2.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/direction_icons/origin-focus.svg b/public/images/direction_icons/origin-focus.svg index 89d856f38..a72b3ba64 100644 --- a/public/images/direction_icons/origin-focus.svg +++ b/public/images/direction_icons/origin-focus.svg @@ -3,4 +3,4 @@ - \ No newline at end of file + diff --git a/public/images/direction_icons/origin.svg b/public/images/direction_icons/origin.svg index c074c999d..0fe357f65 100644 --- a/public/images/direction_icons/origin.svg +++ b/public/images/direction_icons/origin.svg @@ -1,12 +1,6 @@ - + - - - - - - - - + + diff --git a/public/images/direction_icons/pin-focus.svg b/public/images/direction_icons/pin-focus.svg index ce82b63dd..810f5b117 100644 --- a/public/images/direction_icons/pin-focus.svg +++ b/public/images/direction_icons/pin-focus.svg @@ -1,6 +1,6 @@ - + diff --git a/src/components/ui/Chevron.jsx b/src/components/ui/Chevron.jsx index 79467b4aa..2a33913e2 100644 --- a/src/components/ui/Chevron.jsx +++ b/src/components/ui/Chevron.jsx @@ -8,7 +8,7 @@ const Chevron = ({ up, fill = GREY_SEMI_DARKNESS, size = 24 }) => ( fill={fill} style={{ transition: 'transform .2s', - transform: `rotate(${up ? 0 : 180}deg)`, + transform: `rotate(${up ? 180 : 0}deg)`, }} /> ); diff --git a/src/libs/route_utils.js b/src/libs/route_utils.js index 1b638e2e7..4c49677cd 100644 --- a/src/libs/route_utils.js +++ b/src/libs/route_utils.js @@ -14,7 +14,7 @@ export function formatDuration(sec) { min = min - 60 * hour; let ret = `${hour} h`; if (hour < 10) { - ret += ' ' + min.toString().padStart(2, '0'); + ret += ' ' + min.toString().padStart(2, '0') + ' min'; } return ret; } diff --git a/src/panel/direction/RouteSummaryInfo/index.jsx b/src/panel/direction/RouteSummaryInfo/index.jsx index 6d2bd2a75..0db34cefe 100644 --- a/src/panel/direction/RouteSummaryInfo/index.jsx +++ b/src/panel/direction/RouteSummaryInfo/index.jsx @@ -22,7 +22,7 @@ const RouteWalkingTime = ({ route }) => { const RouteSummaryInfo = ({ isFastest, route, vehicle }) => (
-
+
{formatDuration(route.duration)}
@@ -30,10 +30,10 @@ const RouteSummaryInfo = ({ isFastest, route, vehicle }) => ( )} - + {vehicle !== 'publicTransport' && ( - {formatDistance(route.distance)} + {formatDistance(route.distance)} )} {vehicle === 'publicTransport' && } diff --git a/src/panel/direction/RoutesList/Route/RoadMap/Default/DefaultRoadMapItem/index.jsx b/src/panel/direction/RoutesList/Route/RoadMap/Default/DefaultRoadMapItem/index.jsx index 7d7c59980..7e0aabd02 100644 --- a/src/panel/direction/RoutesList/Route/RoadMap/Default/DefaultRoadMapItem/index.jsx +++ b/src/panel/direction/RoutesList/Route/RoadMap/Default/DefaultRoadMapItem/index.jsx @@ -11,7 +11,7 @@ const DefaultRoadMapItem = ({ children, icon, distance, className, line, alignTo
{icon}
{children} -
{distance}
+
{distance}
diff --git a/src/panel/direction/RoutesList/Route/RoadMap/PublicTransport/PublicTransportRoadMapItem/index.jsx b/src/panel/direction/RoutesList/Route/RoadMap/PublicTransport/PublicTransportRoadMapItem/index.jsx index a68528713..81020a64c 100644 --- a/src/panel/direction/RoutesList/Route/RoadMap/PublicTransport/PublicTransportRoadMapItem/index.jsx +++ b/src/panel/direction/RoutesList/Route/RoadMap/PublicTransport/PublicTransportRoadMapItem/index.jsx @@ -19,7 +19,11 @@ const PublicTransportRoadMapItem = ({
{icon}
{children} - {type === 'WALK' && {distance}} + {type === 'WALK' && ( + + {distance} + + )}
diff --git a/src/panel/service/ServicePanelDesktop.jsx b/src/panel/service/ServicePanelDesktop.jsx index 1d4a6155e..30cef5253 100644 --- a/src/panel/service/ServicePanelDesktop.jsx +++ b/src/panel/service/ServicePanelDesktop.jsx @@ -10,7 +10,7 @@ const ServicePanelDesktop = () => { return ( - +

{_('Search around this place', 'service panel')}

diff --git a/src/scss/includes/direction-field.scss b/src/scss/includes/direction-field.scss index 33aa619bf..9351d388b 100644 --- a/src/scss/includes/direction-field.scss +++ b/src/scss/includes/direction-field.scss @@ -8,6 +8,7 @@ .direction-input { position: relative; display: flex; + // reverse to handle icon active style with css flex-direction: row-reverse; transition: box-shadow 0.3s ease-in-out; @@ -41,7 +42,7 @@ top: var(--spacing-m); display: flex; align-items: center; - height: 50px; + height: 47px; width: 24px; } diff --git a/src/scss/includes/direction-form.scss b/src/scss/includes/direction-form.scss index 74531297b..10b342c6c 100644 --- a/src/scss/includes/direction-form.scss +++ b/src/scss/includes/direction-form.scss @@ -3,17 +3,19 @@ .direction-form { display: flex; flex-direction: column; - padding: var(--spacing-m); + padding: 14px; + .direction-fields { background: var(--grey-000); - margin: 0 0 4px; + margin: 2px 0 4px; position: relative; border-radius: 8px; display: flex; justify-content: space-between; align-items: center; box-shadow: 0 2px 16px 0 rgba(12, 12, 14, 0.2), 0 4px 8px 0 rgba(12, 12, 14, 0.12); + height: 96px; @media (min-width: 641px) { margin-bottom: 0; diff --git a/src/scss/includes/direction.scss b/src/scss/includes/direction.scss index fb0048012..6add03f57 100644 --- a/src/scss/includes/direction.scss +++ b/src/scss/includes/direction.scss @@ -136,7 +136,7 @@ display: flex; justify-content: center; flex-shrink: 0; - width: 60px; + width: 78px; z-index: 1; } @@ -198,12 +198,11 @@ &:before { @include active-border; } - } + } .itinerary_roadmap_item_icon { align-self: center; justify-content: center; - opacity: 0.5; } &--walk { @@ -228,6 +227,10 @@ padding: var(--spacing-s) var(--spacing-s) var(--spacing-s) 0; } + .itinerary_roadmap_step_deistance { + color: #4b5058; + } + .itinerary_roadmap_item_icon { align-self: flex-start !important; justify-content: flex-end !important; @@ -238,7 +241,7 @@ } .itinerary_roadmap_step_description { - padding: 10px 0; + padding: 10px 14px 10px 0; flex-grow: 1; &--reverse { @@ -303,7 +306,7 @@ .itinerary_roadmap_line { position: absolute; - width: 7px; + width: 6px; top: 0; border-radius: 3px; z-index: 1; @@ -312,7 +315,7 @@ &--walk { top: -5px; background: url(../../images/direction_icons/walking_bullet_roadmap.png) repeat space; - background-size: 7px 10px; + background-size: 6px 10px; height: calc(100% + 15px); } diff --git a/src/scss/includes/main.scss b/src/scss/includes/main.scss index 73af8accc..791d8549f 100644 --- a/src/scss/includes/main.scss +++ b/src/scss/includes/main.scss @@ -14,7 +14,7 @@ noscript { .panel_container { position: relative; left: $ui_margin; - top: 24px; + top: 28px; transition: transform 0.3s; width: $panel_width; } diff --git a/src/scss/includes/panels/favorite_panel.scss b/src/scss/includes/panels/favorite_panel.scss index a17facc45..b9cbe38cc 100644 --- a/src/scss/includes/panels/favorite_panel.scss +++ b/src/scss/includes/panels/favorite_panel.scss @@ -1,6 +1,6 @@ .favorite_panel { .panel-header { - padding: 20px 14px 4px; + padding: 20px 14px 0; } } diff --git a/src/scss/includes/panels/history_panel.scss b/src/scss/includes/panels/history_panel.scss index 83c96d618..b06fd9051 100644 --- a/src/scss/includes/panels/history_panel.scss +++ b/src/scss/includes/panels/history_panel.scss @@ -12,7 +12,7 @@ } .panel-header { - padding: 20px 14px 4px; + padding: 20px 14px 0; } } @@ -82,7 +82,8 @@ .history-list-title { margin-bottom: 22px; - padding: 0 14px; + padding: 0 14px 8px; + border-bottom: 1px solid #e9eaec; } .history_panel_switch_line { diff --git a/src/scss/includes/panels/poi_panel.scss b/src/scss/includes/panels/poi_panel.scss index 869990de0..b819713cc 100644 --- a/src/scss/includes/panels/poi_panel.scss +++ b/src/scss/includes/panels/poi_panel.scss @@ -21,6 +21,10 @@ $BLOCK_ICON_FONT_SIZE: 16px; margin-left: -$spacing-m; margin-right: -$spacing-m; } + + .block-icon { + margin-right: 26px; + } } @keyframes appear { diff --git a/src/scss/includes/panels/timetable.scss b/src/scss/includes/panels/timetable.scss index 365d76115..b0cfef11d 100644 --- a/src/scss/includes/panels/timetable.scss +++ b/src/scss/includes/panels/timetable.scss @@ -18,7 +18,7 @@ .hours { text-align: right; - padding: 4px 10px; + padding: 4px 36px 4px 10px; } .currentDay { diff --git a/src/scss/includes/routes.scss b/src/scss/includes/routes.scss index 49c65257b..23f4c4515 100644 --- a/src/scss/includes/routes.scss +++ b/src/scss/includes/routes.scss @@ -24,8 +24,9 @@ } .routeVia { - font-size: 16px; - line-height: 24px; + font-size: 14px; + line-height: 18px; + color: #4b5058; &-step { display: inline-block; diff --git a/src/scss/includes/search_form.scss b/src/scss/includes/search_form.scss index 4fa95198c..c334859f0 100644 --- a/src/scss/includes/search_form.scss +++ b/src/scss/includes/search_form.scss @@ -66,7 +66,7 @@ input[type='search'] { flex-shrink: 0; background: url(../../images/qwant-logo.svg) no-repeat; background-size: cover; - margin: 0 var(--spacing-s) 0 var(--spacing-xs); + margin: 0 var(--spacing-m) 0 var(--spacing-xs); } // Return arrows @@ -103,6 +103,11 @@ input[type='search'] { flex-shrink: 0; cursor: pointer; + svg { + width: 24px; + height: 24px; + } + .directions_arrow_svg__background { fill: var(--grey-000); transition: fill 0.2s ease-in-out; diff --git a/src/scss/includes/suggest.scss b/src/scss/includes/suggest.scss index 6785427aa..fc22ec200 100644 --- a/src/scss/includes/suggest.scss +++ b/src/scss/includes/suggest.scss @@ -3,6 +3,10 @@ /* core styles should not be changed */ cursor: default; padding-bottom: 12px; + + li:first-child { + margin-top: 8px; + } &--empty { diff --git a/src/scss/includes/vehicleSelector.scss b/src/scss/includes/vehicleSelector.scss index 622380b70..baebd72c8 100644 --- a/src/scss/includes/vehicleSelector.scss +++ b/src/scss/includes/vehicleSelector.scss @@ -1,7 +1,7 @@ .vehicleSelector { display: flex; justify-content: center; - margin-bottom: var(--spacing-m); + margin-bottom: var(--spacing-s); @media (min-width: 641px) { justify-content: flex-start; diff --git a/tests/units/route_utils.js b/tests/units/route_utils.js index 82f89ca58..28dad9460 100644 --- a/tests/units/route_utils.js +++ b/tests/units/route_utils.js @@ -7,8 +7,8 @@ describe('route_utils', () => { { seconds: 0, result: '1 min' }, { seconds: 37, result: '1 min' }, { seconds: 125, result: '2 min' }, - { seconds: 3600, result: '1 h 00' }, - { seconds: 5100, result: '1 h 25' }, + { seconds: 3600, result: '1 h 00 min' }, + { seconds: 5100, result: '1 h 25 min' }, { seconds: 36000, result: '10 h' }, ]; From 4f559fd2878acbacc1c9988a7f480b2cdabfb7e4 Mon Sep 17 00:00:00 2001 From: xem Date: Tue, 23 Aug 2022 14:59:15 +0200 Subject: [PATCH 08/22] QMAPS fix bold rating (#1390) --- src/scss/includes/reviewScore.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/scss/includes/reviewScore.scss b/src/scss/includes/reviewScore.scss index 259eccedd..e2c2761e7 100644 --- a/src/scss/includes/reviewScore.scss +++ b/src/scss/includes/reviewScore.scss @@ -31,9 +31,8 @@ } .reviewScore-starRating { - color: var(--green-500); + color: #050506; div { font-size: 14px; - font-weight: bold; } } From b39050eab4ca7c65ecce70aace83800bc605151b Mon Sep 17 00:00:00 2001 From: sdirollo Date: Tue, 23 Aug 2022 17:53:55 +0200 Subject: [PATCH 09/22] fix intention renaming revert --- src/adapters/geocoder.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/adapters/geocoder.js b/src/adapters/geocoder.js index c808df766..96935c05b 100644 --- a/src/adapters/geocoder.js +++ b/src/adapters/geocoder.js @@ -71,7 +71,7 @@ export function getGeocoderSuggestions(term, { focus = {}, useNlu = false } = {} } suggestsPromise = ajax.get(geocoderUrl, query); suggestsPromise - .then(({ features, intention }) => { + .then(({ features, intentions }) => { const pois = features.map((feature, index) => { const queryContext = new QueryContext( term, @@ -82,11 +82,10 @@ export function getGeocoderSuggestions(term, { focus = {}, useNlu = false } = {} return new BragiPoi(feature, queryContext); }); const bragiResponse = { pois }; - if (intention) { - const parsed = new Intention(intention); - if (parsed.isValid()) { - bragiResponse.intentions = [parsed]; - } + if (intentions) { + bragiResponse.intentions = intentions + .map(intention => new Intention(intention)) + .filter(intention => intention.isValid()); } bragiCache[cacheKey] = bragiResponse; resolve(bragiResponse); From f8eb905f03a491301d05f7cd1e3702dab37bcdc3 Mon Sep 17 00:00:00 2001 From: sdirollo Date: Wed, 24 Aug 2022 11:13:11 +0200 Subject: [PATCH 10/22] make front compatible with intention api field --- src/adapters/geocoder.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/adapters/geocoder.js b/src/adapters/geocoder.js index 96935c05b..c808df766 100644 --- a/src/adapters/geocoder.js +++ b/src/adapters/geocoder.js @@ -71,7 +71,7 @@ export function getGeocoderSuggestions(term, { focus = {}, useNlu = false } = {} } suggestsPromise = ajax.get(geocoderUrl, query); suggestsPromise - .then(({ features, intentions }) => { + .then(({ features, intention }) => { const pois = features.map((feature, index) => { const queryContext = new QueryContext( term, @@ -82,10 +82,11 @@ export function getGeocoderSuggestions(term, { focus = {}, useNlu = false } = {} return new BragiPoi(feature, queryContext); }); const bragiResponse = { pois }; - if (intentions) { - bragiResponse.intentions = intentions - .map(intention => new Intention(intention)) - .filter(intention => intention.isValid()); + if (intention) { + const parsed = new Intention(intention); + if (parsed.isValid()) { + bragiResponse.intentions = [parsed]; + } } bragiCache[cacheKey] = bragiResponse; resolve(bragiResponse); From eeedc51c8771ec57f24ca337ac3938805dc38c40 Mon Sep 17 00:00:00 2001 From: pascaloliv Date: Wed, 24 Aug 2022 11:26:28 +0200 Subject: [PATCH 11/22] Deploy Storybook command execption --- .github/workflows/ci.yml | 2 +- grep | 0 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 grep diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8aac635b..de094f4f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,7 +73,7 @@ jobs: git remote set-url origin https://git:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git npm run deploy-storybook -- -u "github-actions-bot " echo "- ## 🎨 Storybook - echo "- ✅ [Successfully deployed](https://qwant.github.io/erdapfel/) 🚀" >> $GITHUB_STEP_SUMMARY + echo "- ✅ Successfully deployed to https://qwant.github.io/erdapfel/ 🚀" >> $GITHUB_STEP_SUMMARY env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/grep b/grep deleted file mode 100644 index e69de29bb..000000000 From 661cc73b77b3ed619ce88be2f47c3b45435881f1 Mon Sep 17 00:00:00 2001 From: pascaloliv Date: Wed, 24 Aug 2022 11:48:46 +0200 Subject: [PATCH 12/22] Missing closing double quote in Github action --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de094f4f7..7f33098ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,8 +72,8 @@ jobs: run: | git remote set-url origin https://git:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git npm run deploy-storybook -- -u "github-actions-bot " - echo "- ## 🎨 Storybook - echo "- ✅ Successfully deployed to https://qwant.github.io/erdapfel/ 🚀" >> $GITHUB_STEP_SUMMARY + echo "- ## 🎨 Storybook" + echo "- ✅ Successfully deployed to [https://qwant.github.io/erdapfel/](https://qwant.github.io/erdapfel/) 🚀" >> $GITHUB_STEP_SUMMARY env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 1a89687a817b6f8b4e75c06167aa3c165fd2c204 Mon Sep 17 00:00:00 2001 From: xem Date: Wed, 31 Aug 2022 10:48:31 +0200 Subject: [PATCH 13/22] QMAPS-2909 fix PT dezoom (#1397) * QMAPS-2909 fix PT dezoom * QMAPS-2909 cleanup * QMAPS-2909 credits due --- local_modules/alt-route-labeller/index.js | 120 +++++++ local_modules/alt-route-labeller/package.json | 43 +++ package-lock.json | 333 +++++++++++++++++- package.json | 1 - src/adapters/scene_direction.js | 2 +- 5 files changed, 480 insertions(+), 19 deletions(-) create mode 100644 local_modules/alt-route-labeller/index.js create mode 100644 local_modules/alt-route-labeller/package.json diff --git a/local_modules/alt-route-labeller/index.js b/local_modules/alt-route-labeller/index.js new file mode 100644 index 000000000..c6ebffc27 --- /dev/null +++ b/local_modules/alt-route-labeller/index.js @@ -0,0 +1,120 @@ +// Alt-route-labeller +// Original author: Benjamin Becquet +// Forked from: https://www.npmjs.com/package/alt-route-labeller +// Altered distinctSegment() to ensure the filtered array passed to lineString() has at least 2 items + +import { coordAll } from '@turf/meta'; +import { lineString } from '@turf/helpers'; +import { getCoord } from '@turf/invariant'; +import along from '@turf/along'; +import lineLength from '@turf/length'; +import bearing from '@turf/bearing'; + +const TOLERANCE = 0.000001; +const floatEquals = (f1, f2) => Math.abs(f1 - f2) < TOLERANCE; +const coordEquals = (c1 = [], c2 = []) => floatEquals(c1[0], c2[0]) && floatEquals(c1[1], c2[1]); +const asKey = coord => `${coord[0].toFixed(6)},${coord[1].toFixed(6)}`; +const last = (array = []) => array[array.length - 1]; + +// find the point at the given distance ratio on the linestring +const project = ratio => ls => { + const length = lineLength(ls); + const lngLat = getCoord(along(ls, length * ratio)); + // keep the local bearing of the line to later choose an anchor minimizing the portion of line covered. + const localLineBearing = bearing( + along(ls, length * (ratio - 0.1)), + along(ls, length * (ratio + 0.1)) + ); + + return { lngLat, localLineBearing }; +}; + +function distinctSegment(coordinates, coordCounts) { + const adjacentCoordsUsedOnce = [[]]; + coordinates.forEach(coord => { + if (coordCounts.get(asKey(coord)) > 1) { + adjacentCoordsUsedOnce.push([]); + } else { + last(adjacentCoordsUsedOnce).push(coord); + } + }); + const longestDistinctSegment = adjacentCoordsUsedOnce + .filter(a => a.length > 0) + .reduce((longest, current) => (current.length > longest.length ? current : longest), []); + + const tmp = longestDistinctSegment.length === 0 ? coordinates : longestDistinctSegment; + + if (tmp.length === 1) tmp[1] = tmp[0]; + + return lineString(tmp); +} + +// extract the longest segment of each linestring +// whose coordinates don't overlap with another feature +export function findDistinctSegments(linestrings) { + if (linestrings.length < 2) { + return linestrings; + } + // extract raw coordinates + const featuresCoords = linestrings.map(coordAll); + // count occurences of each coordinate accross all features + const coordCounts = new Map(); + [].concat(...featuresCoords).forEach(coord => { + coordCounts.set(asKey(coord), (coordCounts.get(asKey(coord)) || 0) + 1); + }); + return featuresCoords.map(coordinates => distinctSegment(coordinates, coordCounts)); +} + +function toSimpleLinestring(feature) { + const allCoordsWithNoDups = coordAll(feature).reduce((noDups, coord) => { + const prevCoord = last(noDups); + if (!prevCoord || !coordEquals(prevCoord, coord)) { + noDups.push(coord); + } + return noDups; + }, []); + return lineString(allCoordsWithNoDups); +} + +// Reduce possibilities of collision by chosing anchors so that labels repulse each other +function optimizeAnchors(positions) { + return positions.map((position, index) => { + const others = positions.slice(); + others.splice(index, 1); + const othersBearing = getBearingFromOtherPoints(position, others); + return { + lngLat: position.lngLat, + anchor: getAnchor(position, othersBearing), + }; + }); +} + +function getBearingFromOtherPoints(position, others) { + return ( + others + .map(other => bearing(other.lngLat, position.lngLat)) + .reduce((avg, value, _index, { length }) => avg + value / length, 0) || // mean + 0 + ); +} + +function getAnchor(position, otherBearing) { + const axis = + Math.abs(position.localLineBearing) < 45 || Math.abs(position.localLineBearing) > 135 + ? 'vertical' + : 'horizontal'; + + if (axis === 'vertical') { + return otherBearing > 0 ? 'left' : 'right'; + } + return Math.abs(otherBearing) < 90 ? 'bottom' : 'top'; +} + +// routes can be a FeatureCollection or an array of Feature or Geometry +export function getLabelPositions(routes = []) { + const featuresOrGeoms = Array.isArray(routes) ? routes : routes.features; + const lineStrings = featuresOrGeoms.map(toSimpleLinestring); + const segments = findDistinctSegments(lineStrings); + const positions = segments.map(project(0.5)); + return optimizeAnchors(positions); +} diff --git a/local_modules/alt-route-labeller/package.json b/local_modules/alt-route-labeller/package.json new file mode 100644 index 000000000..f24ab47f1 --- /dev/null +++ b/local_modules/alt-route-labeller/package.json @@ -0,0 +1,43 @@ +{ + "name": "alt-route-labeller", + "version": "0.3.1", + "description": "A library to compute the best position to put labels on multiple routes between two points", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "rollup -c" + }, + "keywords": [ + "map", + "routes", + "directions", + "itineraries", + "ux", + "labels" + ], + "author": "Benjamin Becquet (benjamin@bbecquet.net)", + "license": "ISC", + "dependencies": { + "@turf/along": "^6.0.1", + "@turf/bearing": "^6.0.1", + "@turf/distance": "^6.0.1", + "@turf/helpers": "^6.1.4", + "@turf/invariant": "^6.1.2", + "@turf/length": "^6.0.2", + "@turf/meta": "^6.0.2" + }, + "devDependencies": { + "@babel/core": "^7.12.10", + "@babel/preset-env": "^7.12.11", + "@rollup/plugin-babel": "^5.2.2", + "@rollup/plugin-commonjs": "^17.0.0", + "@rollup/plugin-node-resolve": "^11.0.1", + "rollup": "^2.35.1", + "rollup-plugin-terser": "^7.0.2" + }, + "repository": { + "type": "git", + "url": "https://github.com/bbecquet/alt-route-labeller" + } +} diff --git a/package-lock.json b/package-lock.json index 4454a47f9..e857ad2b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,6 @@ "@turf/meta": "^6.0.2", "@types/color": "^3.0.3", "@types/mapbox-gl": "^1.13.4", - "alt-route-labeller": "^0.3.1", "axios": "^0.21.2", "bunyan": "^1.8.12", "color": "^3.1.2", @@ -128,6 +127,28 @@ "npm": "8.5.0" } }, + "local_modules/alt-route-labeller": { + "version": "0.3.1", + "license": "ISC", + "dependencies": { + "@turf/along": "^6.0.1", + "@turf/bearing": "^6.0.1", + "@turf/distance": "^6.0.1", + "@turf/helpers": "^6.1.4", + "@turf/invariant": "^6.1.2", + "@turf/length": "^6.0.2", + "@turf/meta": "^6.0.2" + }, + "devDependencies": { + "@babel/core": "^7.12.10", + "@babel/preset-env": "^7.12.11", + "@rollup/plugin-babel": "^5.2.2", + "@rollup/plugin-commonjs": "^17.0.0", + "@rollup/plugin-node-resolve": "^11.0.1", + "rollup": "^2.35.1", + "rollup-plugin-terser": "^7.0.2" + } + }, "local_modules/config-sanitizer-loader": { "name": "@qwant/config-sanitizer-loader", "version": "1.0.0" @@ -4445,6 +4466,29 @@ "resolved": "local_modules/uri", "link": true }, + "node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-commonjs": { "version": "17.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz", @@ -4509,6 +4553,26 @@ "rollup": "^1.20.0 || ^2.0.0" } }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, "node_modules/@rollup/pluginutils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", @@ -8049,6 +8113,15 @@ "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", "devOptional": true }, + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", @@ -8824,18 +8897,8 @@ } }, "node_modules/alt-route-labeller": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/alt-route-labeller/-/alt-route-labeller-0.3.1.tgz", - "integrity": "sha512-InxHzDZRuipV1dx31rdDGGtIeOi56i+T857eoMjHN4oFaj5I7tRxEhxGsBHxuh7GWPPtfS42ykrgH4WoepsWhA==", - "dependencies": { - "@turf/along": "^6.0.1", - "@turf/bearing": "^6.0.1", - "@turf/distance": "^6.0.1", - "@turf/helpers": "^6.1.4", - "@turf/invariant": "^6.1.2", - "@turf/length": "^6.0.2", - "@turf/meta": "^6.0.2" - } + "resolved": "local_modules/alt-route-labeller", + "link": true }, "node_modules/ansi-align": { "version": "3.0.1", @@ -10489,6 +10552,18 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", @@ -17626,6 +17701,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, "node_modules/is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", @@ -26815,6 +26896,101 @@ "fsevents": "~2.3.2" } }, + "node_modules/rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/rollup-plugin-terser/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup-plugin-terser/node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup-plugin-terser/node_modules/terser": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", + "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/rsvp": { "version": "4.8.5", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", @@ -36238,6 +36414,16 @@ "@qwant/uri": { "version": "file:local_modules/uri" }, + "@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + } + }, "@rollup/plugin-commonjs": { "version": "17.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz", @@ -36286,6 +36472,20 @@ "@rollup/pluginutils": "^3.0.8" } }, + "@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + } + }, "@rollup/pluginutils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", @@ -38875,6 +39075,15 @@ "@types/react": "*" } }, + "@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", @@ -39483,17 +39692,22 @@ "requires": {} }, "alt-route-labeller": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/alt-route-labeller/-/alt-route-labeller-0.3.1.tgz", - "integrity": "sha512-InxHzDZRuipV1dx31rdDGGtIeOi56i+T857eoMjHN4oFaj5I7tRxEhxGsBHxuh7GWPPtfS42ykrgH4WoepsWhA==", + "version": "file:local_modules/alt-route-labeller", "requires": { + "@babel/core": "^7.12.10", + "@babel/preset-env": "^7.12.11", + "@rollup/plugin-babel": "^5.2.2", + "@rollup/plugin-commonjs": "^17.0.0", + "@rollup/plugin-node-resolve": "^11.0.1", "@turf/along": "^6.0.1", "@turf/bearing": "^6.0.1", "@turf/distance": "^6.0.1", "@turf/helpers": "^6.1.4", "@turf/invariant": "^6.1.2", "@turf/length": "^6.0.2", - "@turf/meta": "^6.0.2" + "@turf/meta": "^6.0.2", + "rollup": "^2.35.1", + "rollup-plugin-terser": "^7.0.2" } }, "ansi-align": { @@ -40802,6 +41016,12 @@ } } }, + "builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true + }, "builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", @@ -46371,6 +46591,12 @@ "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", "dev": true }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, "is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", @@ -53558,6 +53784,79 @@ "fsevents": "~2.3.2" } }, + "rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "dependencies": { + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "terser": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", + "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + } + } + } + }, "rsvp": { "version": "4.8.5", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", diff --git a/package.json b/package.json index 3e2f2f78d..cfdaa259c 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,6 @@ "@turf/meta": "^6.0.2", "@types/color": "^3.0.3", "@types/mapbox-gl": "^1.13.4", - "alt-route-labeller": "^0.3.1", "axios": "^0.21.2", "bunyan": "^1.8.12", "color": "^3.1.2", diff --git a/src/adapters/scene_direction.js b/src/adapters/scene_direction.js index 6e0bce952..fbb9c3cd6 100644 --- a/src/adapters/scene_direction.js +++ b/src/adapters/scene_direction.js @@ -10,7 +10,7 @@ import { getAllSteps, getAllStops, originDestinationCoords } from 'src/libs/rout import Error from '../adapters/error'; import nconf from '@qwant/nconf-getter'; import { fire, listen } from 'src/libs/customEvents'; -import { getLabelPositions } from 'alt-route-labeller'; +import { getLabelPositions } from '/local_modules/alt-route-labeller'; import { isMobileDevice } from 'src/libs/device'; import renderStaticReact from 'src/libs/renderStaticReact'; import cx from 'classnames'; From 3d716ab8a6f5626300ce6e4b61c3f7278fd30c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Dupr=C3=A9?= Date: Wed, 31 Aug 2022 14:42:47 +0200 Subject: [PATCH 14/22] fix transparent topbar borders for mobile (#1398) The topbar was displayed with weird white corners on top and the bottom corners were also displayed in white instead of transparent. --- src/scss/includes/search_form.scss | 2 +- src/scss/includes/top_bar.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scss/includes/search_form.scss b/src/scss/includes/search_form.scss index c334859f0..b6279f49d 100644 --- a/src/scss/includes/search_form.scss +++ b/src/scss/includes/search_form.scss @@ -10,7 +10,7 @@ $input-height: 48px; align-items: center; background: var(--green-400) !important; border: 1px solid var(--grey-900-alpha16); - border-radius: var(--spacing-xs) + border-radius: 0 0 var(--spacing-xs) var(--spacing-xs); } // Wrapper around the field and some icons, gets a colored border when the field is focused diff --git a/src/scss/includes/top_bar.scss b/src/scss/includes/top_bar.scss index c088f986d..d05b524e7 100644 --- a/src/scss/includes/top_bar.scss +++ b/src/scss/includes/top_bar.scss @@ -21,7 +21,7 @@ top: 0; left: 0; width: 100%; - border-radius: 0; + border-radius: 0 0 var(--spacing-xs) var(--spacing-xs); box-shadow: 0 2px 16px 0 rgba(12, 12, 14, 0.2), 0 4px 8px 0 rgba(12, 12, 14, 0.12); From 2e7d96cf6cfce920f1dc72fa12dd2b938b249bf4 Mon Sep 17 00:00:00 2001 From: xem Date: Wed, 31 Aug 2022 14:59:45 +0200 Subject: [PATCH 15/22] QMAPS-2129 wip --- src/panel/direction/DirectionForm/index.jsx | 41 ++++++++++++++++++++- src/scss/includes/direction-form.scss | 7 +++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/panel/direction/DirectionForm/index.jsx b/src/panel/direction/DirectionForm/index.jsx index 6fae230fe..6e78113f8 100644 --- a/src/panel/direction/DirectionForm/index.jsx +++ b/src/panel/direction/DirectionForm/index.jsx @@ -97,7 +97,46 @@ const DirectionForm = ({ {activeVehicle === 'publicTransport' && ( -
+
+ + + + + +
+
+

Start now

diff --git a/src/scss/includes/direction-form.scss b/src/scss/includes/direction-form.scss index 3a40cb159..0298d2532 100644 --- a/src/scss/includes/direction-form.scss +++ b/src/scss/includes/direction-form.scss @@ -2,11 +2,14 @@ .direction-form { - .poc p { + .delayed { margin: 5px 0; } - .poc input, .poc button { + .delayed input, .delayed button, .delayed select { border: 1px solid #000; + border-radius: 5px; + margin-right: 5px; + height: 20px; } From 31ba1c9349f313f2f63f102fa6f90a90c00d0e2c Mon Sep 17 00:00:00 2001 From: xem Date: Wed, 31 Aug 2022 17:13:09 +0200 Subject: [PATCH 16/22] QMAPS-2850 scroll to selected route --- src/panel/direction/RoutesList/Route/index.jsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/panel/direction/RoutesList/Route/index.jsx b/src/panel/direction/RoutesList/Route/index.jsx index 0deed8b3f..64bb4980c 100644 --- a/src/panel/direction/RoutesList/Route/index.jsx +++ b/src/panel/direction/RoutesList/Route/index.jsx @@ -1,4 +1,4 @@ -import React, { Fragment } from 'react'; +import React, { Fragment, useEffect } from 'react'; import RouteSummary from './RouteSummary'; import RoadMap from './RoadMap'; @@ -14,9 +14,19 @@ const Route = ({ selectRoute, isMobile, }) => { + const itemRef = React.useRef(null); + + useEffect(() => { + if (isActive) { + itemRef.current.scrollIntoView({ + behavior: 'smooth', + }); + } + }, [isActive]); + return ( -
+
Date: Tue, 6 Sep 2022 18:27:10 +0200 Subject: [PATCH 17/22] disable nlu switch and use intention field --- src/adapters/geocoder.js | 2 +- src/adapters/suggest_sources.js | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/adapters/geocoder.js b/src/adapters/geocoder.js index c808df766..635001d9e 100644 --- a/src/adapters/geocoder.js +++ b/src/adapters/geocoder.js @@ -85,7 +85,7 @@ export function getGeocoderSuggestions(term, { focus = {}, useNlu = false } = {} if (intention) { const parsed = new Intention(intention); if (parsed.isValid()) { - bragiResponse.intentions = [parsed]; + bragiResponse.intention = [parsed]; } } bragiCache[cacheKey] = bragiResponse; diff --git a/src/adapters/suggest_sources.js b/src/adapters/suggest_sources.js index 2da247c14..7ffd56ba8 100644 --- a/src/adapters/suggest_sources.js +++ b/src/adapters/suggest_sources.js @@ -1,6 +1,5 @@ import PoiStore from './poi/poi_store'; import { getGeocoderSuggestions } from 'src/adapters/geocoder'; -import CategoryService from './category_service'; import { getHistoryItems } from 'src/adapters/search_history'; // @TODO: Improvement: don't access directly to window.map @@ -66,14 +65,11 @@ export function suggestResults( return resolve(null); } - const { pois, intentions } = geocoderSuggestions; + const { pois, intention } = geocoderSuggestions; let intentionsOrCategories = []; if (withCategories) { - if (!intentions) { - // no NLU activated - intentionsOrCategories = CategoryService.getMatchingCategories(term); - } else { - intentionsOrCategories = intentions; + if (intention) { + intentionsOrCategories = intention; } } From 27da256e1827d69de030b40853eddd50d52e454c Mon Sep 17 00:00:00 2001 From: sdirollo Date: Wed, 7 Sep 2022 09:35:30 +0200 Subject: [PATCH 18/22] remove unused test in production --- tests/integration/tests/autocomplete.js | 41 +++++++++++++------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/tests/integration/tests/autocomplete.js b/tests/integration/tests/autocomplete.js index 741a89d3c..97146454b 100644 --- a/tests/integration/tests/autocomplete.js +++ b/tests/integration/tests/autocomplete.js @@ -196,26 +196,27 @@ test('check template', async () => { expect(lines[3][1]).toEqualCaseInsensitive("Alpes-Maritimes, Provence-Alpes-Côte d'Azur, France"); }); -test('Retrieve restaurant category when we search "restau"', async () => { - const searchQuery = 'restau'; - - responseHandler.addPreparedResponse(mockAutocomplete, /autocomplete\?q=restau/); - - await page.goto(APP_URL); - await autocompleteHelper.typeAndWait(searchQuery); - - const [firstLine, suggestionId] = await page.evaluate(() => { - return [ - document.querySelector( - '.autocomplete_suggestion:first-child .autocomplete_suggestion__first_line' - ).innerText, - document.querySelector('.autocomplete_suggestion:first-child').getAttribute('data-id'), - ]; - }); - - expect(firstLine).toEqual('Restaurant'); - expect(suggestionId).toEqual('category:restaurant'); -}); +// Now it's calling category only in backend +// test('Retrieve restaurant category when we search "restau"', async () => { +// const searchQuery = 'restau'; +// +// responseHandler.addPreparedResponse(mockAutocomplete, /autocomplete\?q=restau/); +// +// await page.goto(APP_URL); +// await autocompleteHelper.typeAndWait(searchQuery); +// +// const [firstLine, suggestionId] = await page.evaluate(() => { +// return [ +// document.querySelector( +// '.autocomplete_suggestion:first-child .autocomplete_suggestion__first_line' +// ).innerText, +// document.querySelector('.autocomplete_suggestion:first-child').getAttribute('data-id'), +// ]; +// }); +// +// expect(firstLine).toEqual('test result 1'); +// expect(suggestionId).toEqual('category:restaurant'); +// }); test('Retrieve no category when we search "barcelona", not even "bar"', async () => { const searchQuery = 'barcelona'; From 1a959cc52c46d0a6f7fb5e68bd19d62f171a21f1 Mon Sep 17 00:00:00 2001 From: sdirollo Date: Wed, 7 Sep 2022 10:26:37 +0200 Subject: [PATCH 19/22] remove unused test in production --- tests/integration/tests/autocomplete.js | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/tests/integration/tests/autocomplete.js b/tests/integration/tests/autocomplete.js index 97146454b..4779a7cc6 100644 --- a/tests/integration/tests/autocomplete.js +++ b/tests/integration/tests/autocomplete.js @@ -196,28 +196,6 @@ test('check template', async () => { expect(lines[3][1]).toEqualCaseInsensitive("Alpes-Maritimes, Provence-Alpes-Côte d'Azur, France"); }); -// Now it's calling category only in backend -// test('Retrieve restaurant category when we search "restau"', async () => { -// const searchQuery = 'restau'; -// -// responseHandler.addPreparedResponse(mockAutocomplete, /autocomplete\?q=restau/); -// -// await page.goto(APP_URL); -// await autocompleteHelper.typeAndWait(searchQuery); -// -// const [firstLine, suggestionId] = await page.evaluate(() => { -// return [ -// document.querySelector( -// '.autocomplete_suggestion:first-child .autocomplete_suggestion__first_line' -// ).innerText, -// document.querySelector('.autocomplete_suggestion:first-child').getAttribute('data-id'), -// ]; -// }); -// -// expect(firstLine).toEqual('test result 1'); -// expect(suggestionId).toEqual('category:restaurant'); -// }); - test('Retrieve no category when we search "barcelona", not even "bar"', async () => { const searchQuery = 'barcelona'; From 81335a4e34d9a3263cd009dc10ad87fddba440b0 Mon Sep 17 00:00:00 2001 From: xem Date: Wed, 27 Jul 2022 16:35:35 +0200 Subject: [PATCH 20/22] QMAPS-2129 add startAt / arriveBy options in public transports (draft/poc) --- src/adapters/direction_api.js | 12 +++++--- src/panel/direction/DirectionForm/index.jsx | 24 +++++++++++++++ .../RouteStartEndTimes/index.tsx | 3 ++ src/panel/direction/index.jsx | 30 +++++++++++++++++-- src/scss/includes/direction-form.scss | 9 ++++++ 5 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/adapters/direction_api.js b/src/adapters/direction_api.js index fe32a9f79..c4447b114 100644 --- a/src/adapters/direction_api.js +++ b/src/adapters/direction_api.js @@ -48,18 +48,20 @@ const modeToProfile = { }; export default class DirectionApi { - static async search(start, end, mode) { + static async search(start, end, mode, startAt, arriveBy) { if (mode === modes.CYCLING) { // Fetch routes without ferry in priority - const firstSearch = await DirectionApi._search(start, end, mode, { exclude: 'ferry' }); + const firstSearch = await DirectionApi._search(start, end, mode, null, null, { + exclude: 'ferry', + }); if (firstSearch.data && firstSearch.data.routes && firstSearch.data.routes.length > 0) { return firstSearch; } } - return DirectionApi._search(start, end, mode); + return DirectionApi._search(start, end, mode, startAt, arriveBy); } - static async _search(start, end, mode, { exclude = '' } = {}) { + static async _search(start, end, mode, startAt, arriveBy, { exclude = '' } = {}) { const apiProfile = modeToProfile[mode]; let directionsUrl = directionConfig.apiBaseUrl; const userLang = window.getLang(); @@ -92,6 +94,8 @@ export default class DirectionApi { } const s_start = poiToMapBoxCoordinates(start); const s_end = poiToMapBoxCoordinates(end); + if (startAt) directionsParams.depart_at = startAt; + if (arriveBy) directionsParams.arrive_by = arriveBy; directionsUrl = `${directionsUrl}${s_start};${s_end}`; let response = null; try { diff --git a/src/panel/direction/DirectionForm/index.jsx b/src/panel/direction/DirectionForm/index.jsx index 81c5872a4..6fae230fe 100644 --- a/src/panel/direction/DirectionForm/index.jsx +++ b/src/panel/direction/DirectionForm/index.jsx @@ -19,6 +19,9 @@ const DirectionForm = ({ isInitializing, originInputText, destinationInputText, + onStartNow, + onStartAt, + onArriveBy, }) => { const { _ } = useI18n(); const { isMobile } = useDevice(); @@ -93,6 +96,27 @@ const DirectionForm = ({ + {activeVehicle === 'publicTransport' && ( +
+

+ Start now +

+

+ or: Start at {' '} + +

+

+ or: Arrive by {' '} + +

+
+ )}
); }; diff --git a/src/panel/direction/RouteSummaryInfo/RouteStartEndTimes/index.tsx b/src/panel/direction/RouteSummaryInfo/RouteStartEndTimes/index.tsx index 00d52a4a5..bfc27d955 100644 --- a/src/panel/direction/RouteSummaryInfo/RouteStartEndTimes/index.tsx +++ b/src/panel/direction/RouteSummaryInfo/RouteStartEndTimes/index.tsx @@ -17,10 +17,13 @@ const RouteStartEndTimes: React.FunctionComponent = ({ return null; } + const dateFormatter = getTimeFormatter({ month: '2-digit', day: '2-digit' }); const timeFormatter = getTimeFormatter({ hour: '2-digit', minute: '2-digit' }); return (
+ {dateFormatter.format(new Date(stripTimeZone(start)))} + {' - '} {timeFormatter.format(new Date(stripTimeZone(start)))} {' - '} {timeFormatter.format(new Date(stripTimeZone(end)))} diff --git a/src/panel/direction/index.jsx b/src/panel/direction/index.jsx index fed513efe..654044f9d 100644 --- a/src/panel/direction/index.jsx +++ b/src/panel/direction/index.jsx @@ -65,6 +65,8 @@ class DirectionPanel extends React.Component { isInitializing: true, originInputText: '', destinationInputText: '', + startAt: null, + arriveBy: null, }; this.restorePoints(props); @@ -180,7 +182,7 @@ class DirectionPanel extends React.Component { } computeRoutes = async () => { - const { origin, destination, vehicle } = this.state; + const { origin, destination, vehicle, startAt, arriveBy } = this.state; if (origin && destination) { this.setState({ isDirty: false, @@ -191,7 +193,13 @@ class DirectionPanel extends React.Component { const currentQueryId = ++this.lastQueryId; fire('set_origin', origin); fire('set_destination', destination); - const directionResponse = await DirectionApi.search(origin, destination, vehicle); + const directionResponse = await DirectionApi.search( + origin, + destination, + vehicle, + startAt, + arriveBy + ); // A more recent query was done in the meantime, ignore this result silently if (currentQueryId !== this.lastQueryId) { return; @@ -330,6 +338,21 @@ class DirectionPanel extends React.Component { } }; + startNow = () => { + this.setState({ startAt: null, arriveBy: null }, this.update); + this.computeRoutes(); + }; + + startAt = datetime => { + this.setState({ startAt: datetime, arriveBy: null }, this.update); + this.computeRoutes(); + }; + + arriveBy = datetime => { + this.setState({ startAt: null, arriveBy: datetime }, this.update); + this.computeRoutes(); + }; + render() { const { origin, @@ -361,6 +384,9 @@ class DirectionPanel extends React.Component { onSelectVehicle={this.onSelectVehicle} activeVehicle={vehicle} isInitializing={isInitializing} + onStartNow={this.startNow} + onStartAt={this.startAt} + onArriveBy={this.arriveBy} /> ); diff --git a/src/scss/includes/direction-form.scss b/src/scss/includes/direction-form.scss index 10b342c6c..4d614b2e9 100644 --- a/src/scss/includes/direction-form.scss +++ b/src/scss/includes/direction-form.scss @@ -1,6 +1,15 @@ @import './vehicleSelector.scss'; .direction-form { + + .poc p { + margin: 5px 0; + } + .poc input, .poc button { + border: 1px solid #000; + } + + display: flex; flex-direction: column; padding: 14px; From 259d5baefaa9e45ef449f675d7fc538b07cad370 Mon Sep 17 00:00:00 2001 From: xem Date: Wed, 31 Aug 2022 14:59:45 +0200 Subject: [PATCH 21/22] QMAPS-2129 wip --- src/panel/direction/DirectionForm/index.jsx | 41 ++++++++++++++++++++- src/scss/includes/direction-form.scss | 7 +++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/panel/direction/DirectionForm/index.jsx b/src/panel/direction/DirectionForm/index.jsx index 6fae230fe..6e78113f8 100644 --- a/src/panel/direction/DirectionForm/index.jsx +++ b/src/panel/direction/DirectionForm/index.jsx @@ -97,7 +97,46 @@ const DirectionForm = ({ {activeVehicle === 'publicTransport' && ( -
+
+ + + + + +
+
+

Start now

diff --git a/src/scss/includes/direction-form.scss b/src/scss/includes/direction-form.scss index 4d614b2e9..c20679154 100644 --- a/src/scss/includes/direction-form.scss +++ b/src/scss/includes/direction-form.scss @@ -2,11 +2,14 @@ .direction-form { - .poc p { + .delayed { margin: 5px 0; } - .poc input, .poc button { + .delayed input, .delayed button, .delayed select { border: 1px solid #000; + border-radius: 5px; + margin-right: 5px; + height: 20px; } From c7560fc274cc2cc27aacbbe5a2d98ecdaff42c64 Mon Sep 17 00:00:00 2001 From: xem Date: Mon, 12 Sep 2022 15:48:34 +0200 Subject: [PATCH 22/22] QMAPS-2129 clean PoC --- src/panel/direction/DirectionForm/index.jsx | 47 ++------------------- 1 file changed, 4 insertions(+), 43 deletions(-) diff --git a/src/panel/direction/DirectionForm/index.jsx b/src/panel/direction/DirectionForm/index.jsx index 6e78113f8..f9a9e411f 100644 --- a/src/panel/direction/DirectionForm/index.jsx +++ b/src/panel/direction/DirectionForm/index.jsx @@ -98,60 +98,21 @@ const DirectionForm = ({ {activeVehicle === 'publicTransport' && (
- - - - - -
-
-

Start now

- or: Start at {' '} + Start at {' '}

- or: Arrive by {' '} + Arrive by {' '}