Skip to content

Commit

Permalink
Merge pull request #495 from GeotrekCE/develop
Browse files Browse the repository at this point in the history
Develop > Master / Preparation 3.4.0
  • Loading branch information
camillemonchicourt authored Oct 22, 2021
2 parents b4659c5 + abe79cf commit 67d3562
Show file tree
Hide file tree
Showing 44 changed files with 961 additions and 238 deletions.
12 changes: 12 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

3.4.0 (2021-10-22)
------------------

**✨ Improvements**

* Explicit and manual offline content download - Texts, images and map tiles (#287)
* Add a page listing offline contents on mobile version (#287)
* Add a redirection page when trying to access an unavailable offline page (#287)
* Move back PDF button and open it in a new tab
* Separate types values of services with commas (by @dtrucs)
* Set ``enableSensitiveAreas`` setting default value to ``false``

3.3.0 (2021-10-12)
------------------

Expand Down
7 changes: 6 additions & 1 deletion docs/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,22 @@ In json files, you can just override the primary keys you need. You have to over
- You can also update the map layers. There are two map layers available:

- `mapClassicLayerUrl` for the map version
- `mapClassicLayerUrlOffline` for the map version in offline mode
- `mapSatelliteLayerUrl` for the satellite version. It is optional, so if you want to have only one available map background, you can add `mapSatelliteLayerUrl: undefined`. This will remove the button which allows the user to switch between two map backgrounds.

- `zoomAvailableOffline` allows you to define the zoom modes allowed in offline mode. This allows you to control the amount of disk space required when caching. Default `[13,14,15]`

- `redirects.json` to define URL rewriting for your instance. For example, you can use this customization to redirect old URL style (Geotrek-rando V2) to the new URL style (Geotrek-rando V3) or to redirect old URL to a new URL after changing the name of a hike in the backend.

- In `rules`, you can define all the rules needed to redirect clients

- `source`: must match to the old URL. Use the wildcard `*` to redirect a subdirectory. Use `:varname` to forward a variable to the destination
- `destination`: must match to the new URL. Use `:varname` to inject a variable captured in the old URL
- `locale`: This can be `undefined` or `false`. This argument specifies if the server must detect automatically the locale or if the rule specifies itself the locale used. Default to `undefined`
- `permanent`: Set to `true` if the redirection is permanent. Set to `false` if the redirection is temporally. Default to `false`

Examples :

```json
{
"rules": [
Expand Down
12 changes: 8 additions & 4 deletions docs/knowledge/caching.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ There are 2 caching strategies at work on this project.

The first one is the responsibility of the service worker. It handles the caching from the web browser of routes and urls that are called from the end user's machine. This is the mechanism allowing the site to be reached offline.

The configuration of this cache is handled in the `frontend/cache.js` file.
The configuration of this cache is handled in the `frontend/cache.js` file and in the `frontend/src/worker/index.js` file.
We use three different strategies here:

### Network First

This is the main strategy involved with offline support. We use it for the regular pages, API calls and map tiles. These contents are always queried from the network unless it is unavailable.
This is the main strategy involved with offline support. We use it for the regular pages, API calls. These contents are always queried from the network unless it is unavailable.

They are stored in separated categories, each page has its own category (search, trek, touristic content) and can cache up to 32 occurence each. The API calls and map tiles are stored in the "other" categoy which can host up to 256 entries, when this category reaches 256 entries, the first ones will be poped out of the cache to keep storing the new content.
They are stored in separated categories, each page has its own category (search, content, images, ...) and can cache up to 32 occurence each. The API calls are stored in the "other" category which can host up to 256 entries, when this category reaches 256 entries, the first ones will be poped out of the cache to keep storing the new content.

When constantly unable to reach the network, these resources will appear as "stale" after a week.
The map tiles is cached by the plugin [leaflet.offline](https://github.com/allartk/leaflet.offline) in an indexed database. Each content store tiles with the id of the content to keep a link and remove the tiles later.

Content page is cached in the Cache Storage in the "trek-pages" category. In addition, there is a resume in the local storage to fill the /offline page.

When constantly unable to reach the network, these resources will appear as "stale" after 90 days.

![Network First Strategy](../assets/NetworkFirstStrategy.png)

Expand Down
55 changes: 33 additions & 22 deletions frontend/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,18 @@ module.exports = [
cacheName: 'start-url',
expiration: {
maxEntries: 1,
maxAgeSeconds: 24 * 60 * 60, // 24 hours
maxAgeSeconds: 90 * 60 * 60 * 24, // 90 days
},
},
},
{
urlPattern: '/offline',
handler: 'NetworkFirst',
options: {
cacheName: 'offline',
expiration: {
maxEntries: 1,
maxAgeSeconds: 90 * 60 * 60 * 24, // 90 days
},
},
},
Expand All @@ -24,7 +35,7 @@ module.exports = [
options: {
cacheName: 'google-fonts',
expiration: {
maxEntries: 4,
maxEntries: 8,
maxAgeSeconds: 365 * 24 * 60 * 60, // 365 days
},
},
Expand All @@ -35,7 +46,7 @@ module.exports = [
options: {
cacheName: 'static-font-assets',
expiration: {
maxEntries: 4,
maxEntries: 8,
maxAgeSeconds: 7 * 24 * 60 * 60, // 7 days
},
},
Expand All @@ -46,8 +57,8 @@ module.exports = [
options: {
cacheName: 'static-image-assets',
expiration: {
maxEntries: 64,
maxAgeSeconds: 24 * 60 * 60, // 24 hours
maxEntries: 128,
maxAgeSeconds: 90 * 60 * 60 * 24, // 90 days
},
},
},
Expand All @@ -57,8 +68,8 @@ module.exports = [
options: {
cacheName: 'static-js-assets',
expiration: {
maxEntries: 32,
maxAgeSeconds: 24 * 60 * 60, // 24 hours
maxEntries: 128,
maxAgeSeconds: 90 * 60 * 60 * 24, // 90 days
},
},
},
Expand All @@ -68,8 +79,8 @@ module.exports = [
options: {
cacheName: 'static-style-assets',
expiration: {
maxEntries: 32,
maxAgeSeconds: 24 * 60 * 60, // 24 hours
maxEntries: 64,
maxAgeSeconds: 90 * 60 * 60 * 24, // 90 days
},
},
},
Expand All @@ -79,37 +90,37 @@ module.exports = [
options: {
cacheName: 'static-data-assets',
expiration: {
maxEntries: 32,
maxAgeSeconds: 24 * 60 * 60, // 24 hours
maxEntries: 64,
maxAgeSeconds: 90 * 60 * 60 * 24, // 90 days
},
},
},
{
/*{
urlPattern: /\/trek\/.*$/i,
handler: 'NetworkFirst',
method: 'GET',
options: {
cacheName: 'trek-pages',
expiration: {
maxEntries: 32,
maxAgeSeconds: 7 * 24 * 60 * 60, // 7 days
maxEntries: 512,
maxAgeSeconds: 90 * 24 * 60 * 60, // 90 days
},
networkTimeoutSeconds: 10, // fall back to cache if api does not response within 10 seconds
},
},
{
},*/
/*{
urlPattern: /\/service\/.*$/i,
handler: 'NetworkFirst',
method: 'GET',
options: {
cacheName: 'touritic-content-pages',
expiration: {
maxEntries: 32,
maxAgeSeconds: 7 * 24 * 60 * 60, // 7 days
maxEntries: 64,
maxAgeSeconds: 90 * 24 * 60 * 60, // 90 days
},
networkTimeoutSeconds: 10, // fall back to cache if api does not response within 10 seconds
},
},
},*/
{
urlPattern: /\/search.*$/i,
handler: 'NetworkFirst',
Expand All @@ -124,13 +135,13 @@ module.exports = [
},
},
{
urlPattern: /.*/i,
urlPattern: /^(?!.*opentopomap|.*openstreetmap|.*\/trek\/|.*\/service\/).*$/i,
handler: 'NetworkFirst',
options: {
cacheName: 'others',
expiration: {
maxEntries: 256,
maxAgeSeconds: 24 * 60 * 60, // 24 hours
maxEntries: 512,
maxAgeSeconds: 90 * 60 * 60 * 24, // 90 days
},
networkTimeoutSeconds: 10,
},
Expand Down
6 changes: 4 additions & 2 deletions frontend/config/map.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"maximumZoomLevel": 17,
"searchMapZoom": 10,
"mapCredits": "OpenTopoMap",
"mapClassicLayerUrl": "https://a.tile.opentopomap.org/{z}/{x}/{y}.png",
"mapSatelliteLayerUrl": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
"mapClassicLayerUrl": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
"mapClassicLayerUrlOffline": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
"mapSatelliteLayerUrl": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
"zoomAvailableOffline": [13, 14, 15]
}
2 changes: 1 addition & 1 deletion frontend/customization/config/global.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"mapResultsPageSize": 250,
"maxPoiPerPage": 50,
"maxTouristicContentPerPage": 50,
"enableSensitiveAreas": true,
"enableSensitiveAreas": false,
"portalIds": [],
"apiUrl": "https://geotrekdemo.ecrins-parcnational.fr/api/v2",
"googleAnalyticsId": "G-8FSV2N4FXN",
Expand Down
5 changes: 3 additions & 2 deletions frontend/customization/config/map.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"maximumZoomLevel": 17,
"searchMapZoom": 10,
"mapCredits": "OpenTopoMap",
"mapClassicLayerUrl": "https://a.tile.opentopomap.org/{z}/{x}/{y}.png",
"mapSatelliteLayerUrl": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
"mapClassicLayerUrl": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
"mapSatelliteLayerUrl": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
"zoomAvailableOffline": [13, 14, 15]
}
3 changes: 3 additions & 0 deletions frontend/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ module.exports = withPlugins(plugins, {
pwa: {
dest: 'public',
runtimeCaching: runtimeCachingStrategy,
fallbacks: {
document: '/_offline',
},
},
/**
* environment variables that will be shared for the client and server-side
Expand Down
3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "geotrek-rando-frontend",
"version": "3.3.0",
"version": "3.4.0",
"private": true,
"scripts": {
"debug": "NODE_OPTIONS='--inspect' next ./src",
Expand Down Expand Up @@ -55,6 +55,7 @@
"leaflet": "^1.7.1",
"leaflet.locatecontrol": "0.74.0",
"leaflet.markercluster": "1.5.1",
"leaflet.offline": "^2.0.0-beta.3",
"lodash": "4.17.21",
"next": "^11.1.2",
"next-compose-plugins": "^2.2.1",
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/components/Header/BurgerMenu/BurgerMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export const BurgerMenu: React.FC<Props> = ({ config, menuItems, displayState =
title={intl.formatMessage({ id: 'header.goToSearch' })}
onClick={() => window.open(routes.SEARCH, '_self')}
/>
<BurgerMenuSection
title={intl.formatMessage({ id: 'header.offline' })}
onClick={() => window.open(routes.OFFLINE, '_self')}
/>
</Slide>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,11 @@ Object {
>
Recherche
</span>
<span
class="flex items-center pt-4 pb-4 font-bold outline-none cursor-pointer border-b pb-2 border-solid border-greySoft"
>
Contenus hors ligne
</span>
</nav>
</div>
<div>
Expand Down Expand Up @@ -489,6 +494,11 @@ Object {
>
Recherche
</span>
<span
class="flex items-center pt-4 pb-4 font-bold outline-none cursor-pointer border-b pb-2 border-solid border-greySoft"
>
Contenus hors ligne
</span>
</nav>
</div>
<div>
Expand Down
12 changes: 1 addition & 11 deletions frontend/src/components/Icons/Check/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,11 @@ export const Check: React.FC<GenericIconProps> = ({ size }) => {
>
<path
d="M15 4.5L6.75 11.8333L3 8.5"
stroke="#AA397D"
stroke="currentcolor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};

<svg width="18" height="17" viewBox="0 0 18 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M15 4.5L6.75 11.8333L3 8.5"
stroke="#AA397D"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>;
37 changes: 37 additions & 0 deletions frontend/src/components/Icons/Save/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import { GenericIconProps } from '../types';

export const Save: React.FC<GenericIconProps> = ({
color = 'currentColor',
opacity,
className,
size,
}) => {
return (
<svg
version="1.1"
id="Capa_1"
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
viewBox="0 0 290 290"
width={size}
className={className}
opacity={opacity}
fill={color}
>
<g>
<path
d="M287.602,53.599l-51.2-51.2C234.862,0.863,232.777,0,230.602,0H8.199C3.668,0,0,3.668,0,8.199v273.602
C0,286.332,3.668,290,8.199,290h273.602c4.531,0,8.199-3.668,8.199-8.199V59.397C290,57.221,289.135,55.138,287.602,53.599z
M38.456,34.678c0-3.262,2.651-5.916,5.917-5.916h160.975c3.27,0,5.918,2.654,5.918,5.916v78.323c0,3.269-2.647,5.915-5.918,5.915
H44.373c-3.266,0-5.917-2.646-5.917-5.915V34.678z M251.544,247.513c0,4.03-3.27,7.298-7.296,7.298H45.752
c-4.026,0-7.296-3.268-7.296-7.298V150.94c0-4.028,3.27-7.295,7.296-7.295h198.496c4.026,0,7.296,3.267,7.296,7.295V247.513z"
/>
<rect x="173.564" y="39.039" width="24.588" height="69.604" />
<rect x="59.489" y="174.643" width="171.021" height="8.195" />
<rect x="59.489" y="215.62" width="171.021" height="8.195" />
</g>
</svg>
);
};
29 changes: 29 additions & 0 deletions frontend/src/components/Icons/WifiOff/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import { GenericIconProps } from '../types';

export const WifiOff: React.FC<GenericIconProps> = ({
color = 'currentColor',
opacity,
className,
size,
}) => {
return (
<svg
fill="none"
width={size}
className={className}
opacity={opacity}
viewBox="0 0 24 24"
height={size}
xmlns="http://www.w3.org/2000/svg"
>
<g stroke={color} strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
<path d="m2 7.92647c1.24008-.79618 2.58126-1.44861 4-1.93376m16 1.93376c-2.8849-1.85224-6.317-2.92647-10-2.92647-.3355 0-.6689.00891-1 .02652" />
<path d="m5.17159 11.7046c1.4601-.8446 3.09108-1.4267 4.82839-1.6817m8.82842 1.6817c-.8585-.4967-1.7762-.9025-2.7398-1.2045" />
<path d="m9.07355 15.2544c.91533-.3235 1.90035-.4995 2.92645-.4995s2.0111.176 2.9265.4995" />
<path d="m11.9181 19.1465-.0161-.0161" />
<path d="m2 2 20 20" />
</g>
</svg>
);
};
Loading

0 comments on commit 67d3562

Please sign in to comment.