From f23d3ddb7e1763f79af71b9b387d6ebcd148fb20 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Fri, 26 Jul 2024 16:50:17 +0900 Subject: [PATCH 1/6] Add links to tile provider authentication docs --- src/components/ModalExport.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/components/ModalExport.tsx b/src/components/ModalExport.tsx index 2806106b..8c33bd8e 100644 --- a/src/components/ModalExport.tsx +++ b/src/components/ModalExport.tsx @@ -113,6 +113,13 @@ export default class ModalExport extends React.Component {

Download a JSON style to your computer.

+

+ MapTiler,  + ThunderForest, + and Stadia Maps  + may require access keys or other authentication to access map tiles. + Refer to their documentation for details. +

Date: Wed, 28 Aug 2024 14:29:48 +0900 Subject: [PATCH 2/6] Support exporting Stadia Maps API keys with styles --- cypress/e2e/modals.cy.ts | 12 ++++++++++++ src/components/ModalExport.tsx | 6 ++++++ src/components/ModalSettings.tsx | 8 ++++++++ src/libs/field-spec-additional.ts | 4 ++++ src/libs/style.ts | 27 +++++++++++++++++++++------ 5 files changed, 51 insertions(+), 6 deletions(-) diff --git a/cypress/e2e/modals.cy.ts b/cypress/e2e/modals.cy.ts index ec3e4d77..02cb244f 100644 --- a/cypress/e2e/modals.cy.ts +++ b/cypress/e2e/modals.cy.ts @@ -160,6 +160,18 @@ describe("modals", () => { ).shouldInclude({ "maputnik:thunderforest_access_token": apiKey }); }); + it("stadia access token", () => { + let apiKey = "testing123"; + when.setValue( + "modal:settings.maputnik:stadia_access_token", + apiKey + ); + when.click("modal:settings.name"); + then( + get.styleFromLocalStorage().then((style) => style.metadata) + ).shouldInclude({ "maputnik:stadia_access_token": apiKey }); + }); + it("style renderer", () => { cy.on("uncaught:exception", () => false); // this is due to the fact that this is an invalid style for openlayers when.select("modal:settings.maputnik:renderer", "ol"); diff --git a/src/components/ModalExport.tsx b/src/components/ModalExport.tsx index bc75e172..f3c85783 100644 --- a/src/components/ModalExport.tsx +++ b/src/components/ModalExport.tsx @@ -137,6 +137,12 @@ class ModalExportInternal extends React.Component { value={(this.props.mapStyle.metadata || {} as any)['maputnik:thunderforest_access_token']} onChange={this.changeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")} /> +
diff --git a/src/components/ModalSettings.tsx b/src/components/ModalSettings.tsx index 1989c4c8..991e8804 100644 --- a/src/components/ModalSettings.tsx +++ b/src/components/ModalSettings.tsx @@ -156,6 +156,14 @@ class ModalSettingsInternal extends React.Component onChange={onChangeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")} /> + + ({ label: t("Thunderforest Access Token"), doc: t("Public access token for Thunderforest services.") }, + stadia_access_token: { + label: t("Stadia Maps API Key"), + doc: t("API key for Stadia Maps.") + }, style_renderer: { label: t("Style Renderer"), doc: t("Choose the default Maputnik renderer for this style."), diff --git a/src/libs/style.ts b/src/libs/style.ts index 1c71ae93..9192582b 100644 --- a/src/libs/style.ts +++ b/src/libs/style.ts @@ -55,10 +55,6 @@ function indexOfLayer(layers: LayerSpecification[], layerId: string) { } function getAccessToken(sourceName: string, mapStyle: StyleSpecification, opts: {allowFallback?: boolean}) { - if(sourceName === "thunderforest_transport" || sourceName === "thunderforest_outdoors") { - sourceName = "thunderforest" - } - const metadata = mapStyle.metadata || {} as any; let accessToken = metadata[`maputnik:${sourceName}_access_token`] @@ -74,7 +70,24 @@ function replaceSourceAccessToken(mapStyle: StyleSpecification, sourceName: stri if(!source) return mapStyle if(!("url" in source) || !source.url) return mapStyle - const accessToken = getAccessToken(sourceName, mapStyle, opts) + let authSourceName = sourceName + let setAccessToken = (url: string) => url.replace('{key}', accessToken) + if(sourceName === "thunderforest_transport" || sourceName === "thunderforest_outdoors") { + authSourceName = "thunderforest" + } + else if (("url" in source) && source.url?.match(/\.stadiamaps\.com/)) { + // A few weird things here worth commenting on... + // + // 1. The code currently assumes openmaptiles == MapTiler, + // so we need to check the source URL. + // 2. Stadia Maps does not always require an API key, + // so there is no placeholder in our styles. + // We append it at the end during exporting if necessary. + authSourceName = "stadia" + setAccessToken = (url: string) => `${url}?api_key=${accessToken}` + } + + const accessToken = getAccessToken(authSourceName, mapStyle, opts) if(!accessToken) { // Early exit. @@ -85,7 +98,7 @@ function replaceSourceAccessToken(mapStyle: StyleSpecification, sourceName: stri ...mapStyle.sources, [sourceName]: { ...source, - url: source.url.replace('{key}', accessToken) + url: setAccessToken(source.url) } } const changedStyle = { @@ -120,6 +133,8 @@ function stripAccessTokens(mapStyle: StyleSpecification) { ...mapStyle.metadata as any }; delete changedMetadata['maputnik:openmaptiles_access_token']; + delete changedMetadata['maputnik:thunderforest_access_token']; + delete changedMetadata['maputnik:stadia_access_token']; return { ...mapStyle, metadata: changedMetadata From f95e814fe53e5bd7a956da4ace4695bffc20d034 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Wed, 28 Aug 2024 14:35:30 +0900 Subject: [PATCH 3/6] Add translations --- README.md | 6 ++++++ src/locales/he/translation.json | 2 ++ src/locales/ja/translation.json | 2 ++ src/locales/zh/translation.json | 5 ++++- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cc7bae53..5ca2606a 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,12 @@ npm run lint-css npm run sort-styles ``` +Update the translation files (run this whenever touching user-facing strings). +After running, check your working copy for files and add/correct as needed. + +``` +npm run i18n:refresh +``` ## Tests For E2E testing we use [Cypress](https://www.cypress.io/) diff --git a/src/locales/he/translation.json b/src/locales/he/translation.json index b11f26cd..3e6415f2 100644 --- a/src/locales/he/translation.json +++ b/src/locales/he/translation.json @@ -179,6 +179,8 @@ "Public access token for MapTiler Cloud.": "Public access token for MapTiler Cloud.", "Thunderforest Access Token": "Thunderforest Access Token", "Public access token for Thunderforest services.": "Public access token for Thunderforest services.", + "Stadia Maps API Key": "Stadia Maps API Key", + "API key for Stadia Maps.": "API key for Stadia Maps", "Style Renderer": "צייר הסטייל", "Choose the default Maputnik renderer for this style.": "בחירת צייר ברירת המחדל של מפוטניק עבור הסטייל הזה" } diff --git a/src/locales/ja/translation.json b/src/locales/ja/translation.json index 31751a81..52f92a31 100644 --- a/src/locales/ja/translation.json +++ b/src/locales/ja/translation.json @@ -179,6 +179,8 @@ "Public access token for MapTiler Cloud.": "MapTiler Cloud の公開用アクセストークン", "Thunderforest Access Token": "Thunderforest アクセストークン", "Public access token for Thunderforest services.": "Thunderforest サービスの公開用アクセストークン", + "Stadia Maps API Key": "Stadia Maps API キー", + "API key for Stadia Maps.": "Stadia Maps の API キー", "Style Renderer": "スタイルレンダラ", "Choose the default Maputnik renderer for this style.": "このスタイルのデフォルトの Maputnik レンダラを選択してください", "Layer options": "レイヤー設定", diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json index dd26994b..1e1cfca5 100644 --- a/src/locales/zh/translation.json +++ b/src/locales/zh/translation.json @@ -82,7 +82,7 @@ "Close modal": "关闭模态框", "Debug": "调试", "Options": "选项", - "<0>Open in OSM — Opens the current view on openstreetmap.org": "在 openstreetmap.org 打开当前视图", + "<0>Open in OSM — Opens the current view on openstreetmap.org": "在 openstreetmap.org 打开当前视图", "Export Style": "导出样式", "Download Style": "下载样式", "Download a JSON style to your computer.": "将JSON样式下载到您的电脑。", @@ -179,8 +179,11 @@ "Public access token for MapTiler Cloud.": "MapTiler Cloud 的公共访问令牌。", "Thunderforest Access Token": "Thunderforest 访问令牌", "Public access token for Thunderforest services.": "Thunderforest 服务的公共访问令牌。", + "Stadia Maps API Key": "Stadia Maps API 密钥", + "API key for Stadia Maps.": "Stadia Maps 的 API 密钥", "Style Renderer": "样式渲染器", "Choose the default Maputnik renderer for this style.": "为这种样式选择默认的Maputnik渲染器。", + "<0>Open in OSM — Opens the current view on openstreetmap.org": "在 openstreetmap.org 打开当前视图", "Layer options": "图层选项", "Paint properties": "绘制属性", "Layout properties": "布局属性", From 25fc5942260c541bea390eb8fd3372c0b1773477 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Wed, 28 Aug 2024 22:49:48 +0900 Subject: [PATCH 4/6] Localization clean-up + documentation --- README.md | 4 ++++ src/locales/he/translation.json | 2 +- src/locales/ja/translation.json | 2 +- src/locales/zh/translation.json | 1 - 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5ca2606a..785e546a 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,10 @@ After running, check your working copy for files and add/correct as needed. npm run i18n:refresh ``` +You can test the UI in different languages using the dropdown in the top menu +(Maputnik does not automatically localize based on browser language settings +at the moment). + ## Tests For E2E testing we use [Cypress](https://www.cypress.io/) diff --git a/src/locales/he/translation.json b/src/locales/he/translation.json index 3e6415f2..3ac5fb5f 100644 --- a/src/locales/he/translation.json +++ b/src/locales/he/translation.json @@ -82,7 +82,7 @@ "Close modal": "סגירת חלונית", "Debug": "דיבאג", "Options": "אפשרויות", - "<0>Open in OSM — Opens the current view on openstreetmap.org": "<0>פתיחה ב-OSM - פתיחה של התצוגה הנוכחית ב- openstreetmap.org", + "<0>Open in OSM — Opens the current view on openstreetmap.org": "<0>פתיחה ב-OSM - פתיחה של התצוגה הנוכחית ב- openstreetmap.org", "Export Style": "ייצוא של הסטייל", "Download Style": "הורדה של הסטייל", "Download a JSON style to your computer.": "הורדה של הסטייל למחשב", diff --git a/src/locales/ja/translation.json b/src/locales/ja/translation.json index 52f92a31..6f9f1b50 100644 --- a/src/locales/ja/translation.json +++ b/src/locales/ja/translation.json @@ -82,7 +82,7 @@ "Close modal": "モーダルを閉じる", "Debug": "デバッグ", "Options": "設定", - "<0>Open in OSM — Opens the current view on openstreetmap.org": "現在のビューを <0>openstreetmap.org で開く", + "<0>Open in OSM — Opens the current view on openstreetmap.org": "現在のビューを <0>openstreetmap.org で開く", "Export Style": "スタイルをエクスポート", "Download Style": "スタイルをダウンロード", "Download a JSON style to your computer.": "パソコンにJSONスタイルをダウンロードします。", diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json index 1e1cfca5..aaf9e225 100644 --- a/src/locales/zh/translation.json +++ b/src/locales/zh/translation.json @@ -82,7 +82,6 @@ "Close modal": "关闭模态框", "Debug": "调试", "Options": "选项", - "<0>Open in OSM — Opens the current view on openstreetmap.org": "在 openstreetmap.org 打开当前视图", "Export Style": "导出样式", "Download Style": "下载样式", "Download a JSON style to your computer.": "将JSON样式下载到您的电脑。", From 91600f88c5d297aec3df0f3978f8328fe25d46a0 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Wed, 28 Aug 2024 23:30:16 +0900 Subject: [PATCH 5/6] Refactor based on code review suggestion --- src/libs/style.ts | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/libs/style.ts b/src/libs/style.ts index 9192582b..750f488b 100644 --- a/src/libs/style.ts +++ b/src/libs/style.ts @@ -71,20 +71,13 @@ function replaceSourceAccessToken(mapStyle: StyleSpecification, sourceName: stri if(!("url" in source) || !source.url) return mapStyle let authSourceName = sourceName - let setAccessToken = (url: string) => url.replace('{key}', accessToken) if(sourceName === "thunderforest_transport" || sourceName === "thunderforest_outdoors") { authSourceName = "thunderforest" } else if (("url" in source) && source.url?.match(/\.stadiamaps\.com/)) { - // A few weird things here worth commenting on... - // - // 1. The code currently assumes openmaptiles == MapTiler, - // so we need to check the source URL. - // 2. Stadia Maps does not always require an API key, - // so there is no placeholder in our styles. - // We append it at the end during exporting if necessary. + // The code currently usually assumes openmaptiles == MapTiler, + // so we need to check the source URL. authSourceName = "stadia" - setAccessToken = (url: string) => `${url}?api_key=${accessToken}` } const accessToken = getAccessToken(authSourceName, mapStyle, opts) @@ -94,11 +87,21 @@ function replaceSourceAccessToken(mapStyle: StyleSpecification, sourceName: stri return mapStyle; } + let sourceUrl: string + if (authSourceName == "stadia") { + // Stadia Maps does not always require an API key, + // so there is no placeholder in our styles. + // We append it at the end of the URL when exporting if necessary. + sourceUrl = `${source.url}?api_key=${accessToken}` + } else { + sourceUrl = source.url.replace('{key}', accessToken) + } + const changedSources = { ...mapStyle.sources, [sourceName]: { ...source, - url: setAccessToken(source.url) + url: sourceUrl } } const changedStyle = { From d3b781b8e7ce03a2eaea6c84f137f4d0913673cf Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Thu, 29 Aug 2024 01:34:45 +0900 Subject: [PATCH 6/6] [WIP] Failed attempt at linking out to docs in help text --- src/components/Doc.tsx | 12 +++++++++--- src/components/ModalExport.tsx | 7 ------- src/libs/field-spec-additional.ts | 11 +++++++---- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/components/Doc.tsx b/src/components/Doc.tsx index c0fa40fe..6a5949b9 100644 --- a/src/components/Doc.tsx +++ b/src/components/Doc.tsx @@ -18,14 +18,15 @@ type DocProps = { 'sdk-support'?: { [key: string]: typeof headers } + docUrl?: string } -}; +}; // & WithTranslation? export default class Doc extends React.Component { render () { const {fieldSpec} = this.props; - const {doc, values} = fieldSpec; + const {doc, values, docUrl} = fieldSpec; const sdkSupport = fieldSpec['sdk-support']; const renderValues = ( @@ -85,7 +86,12 @@ export default class Doc extends React.Component {
} + {docUrl && +
+ console.log('Link clicked')}>Learn More (TODO i18n) +
+ } ); } -} \ No newline at end of file +} diff --git a/src/components/ModalExport.tsx b/src/components/ModalExport.tsx index f3c85783..1fa6b32b 100644 --- a/src/components/ModalExport.tsx +++ b/src/components/ModalExport.tsx @@ -116,13 +116,6 @@ class ModalExportInternal extends React.Component {

{t("Download a JSON style to your computer.")}

-

- MapTiler,  - ThunderForest, - and Stadia Maps  - may require access keys or other authentication to access map tiles. - Refer to their documentation for details. -

({ maputnik: { maptiler_access_token: { label: t("MapTiler Access Token"), - doc: t("Public access token for MapTiler Cloud.") + doc: t("Public access token for MapTiler Cloud."), + docUrl: "https://docs.maptiler.com/cloud/api/authentication-key/" }, thunderforest_access_token: { label: t("Thunderforest Access Token"), - doc: t("Public access token for Thunderforest services.") + doc: t("Public access token for Thunderforest services."), + docUrl: "https://www.thunderforest.com/docs/apikeys/", }, stadia_access_token: { label: t("Stadia Maps API Key"), - doc: t("API key for Stadia Maps.") + doc: t("API key for Stadia Maps."), + docUrl: "https://docs.stadiamaps.com/authentication/", }, style_renderer: { label: t("Style Renderer"), - doc: t("Choose the default Maputnik renderer for this style."), + doc: t("Choose the default Maputnik renderer for this style.") }, } })