diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 6ca468b..ecc2428 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -10,18 +10,18 @@ Please select an option -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New Feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] Documentation update -- [ ] Other (please provide a description below) +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New Feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Documentation update +- [ ] Other (please provide a description below) ## Testing Instructions Please provide testing instructions for the reviewer here -- [ ] Manual testing (Please provide the testing instructions) -- [ ] Automated testing +- [ ] Manual testing (Please provide the testing instructions) +- [ ] Automated testing ## Screenshots @@ -29,10 +29,10 @@ Please provide testing instructions for the reviewer here ## Review Checklist -- [ ] Code follows style & code guidelines -- [ ] Has linked issue -- [ ] Pull request includes description -- [ ] Version and changelog were updated -- [ ] Testing Instructions are provided -- [ ] Tests are verified -- [ ] Commits follow our [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) guidelines +- [ ] Code follows style & code guidelines +- [ ] Has linked issue +- [ ] Pull request includes description +- [ ] Version and changelog were updated +- [ ] Testing Instructions are provided +- [ ] Tests are verified +- [ ] Commits follow our [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) guidelines diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml index 2eb9b11..7e81c18 100644 --- a/.github/workflows/auto-release.yml +++ b/.github/workflows/auto-release.yml @@ -1,81 +1,81 @@ name: Release Plugin on: - push: - tags: - - "*" + push: + tags: + - '*' jobs: - define-variables: - name: Defining and extracting Variables - runs-on: ubuntu-latest - outputs: - version: ${{ steps.get_version.outputs.VERSION }} - label: ${{ steps.get_label.outputs.LABEL }} - pre_release: ${{ steps.prerelease_check.outputs.PRE_RELEASE }} - steps: - - uses: actions/checkout@v3 - - name: Get Version - id: get_version - # E.g. converts ref/tags/v0.0.1 -> 0.0.1 - run: echo "VERSION=$(echo $GITHUB_REF | cut -d / -f 3 | cut -c 1-)" >> $GITHUB_OUTPUT - - name: Get Label - id: get_label - # E.g. converts 0.0.1-beta.1 -> beta - run: echo "LABEL=$(echo ${{ steps.get_version.outputs.VERSION }} | awk -F '-' '{split($2, arr, "."); print arr[1]}')" >> $GITHUB_OUTPUT - - name: Check for Pre-Release or Release - id: prerelease_check - # perform secret check & put boolean result as an output - run: | - if [ "${{ steps.get_label.outputs.LABEL }}" != '' ]; then - echo "PRE_RELEASE=true" >> $GITHUB_OUTPUT - else - echo "PRE_RELEASE=false" >> $GITHUB_OUTPUT - fi - create-release: - needs: [define-variables] - name: Create Github Release - if: needs.define-variables.outputs.pre_release == 'false' - runs-on: ubuntu-latest - env: - NPM_PACKAGE_VERSION: ${{ needs.define-variables.outputs.version }} - steps: - - uses: actions/checkout@v3 - - name: Build Plugin - run: | - npm install - npm run version - npm run build - - uses: "marvinpinto/action-automatic-releases@latest" - name: Publish Release - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - prerelease: false - title: ${{ needs.define-variables.outputs.version }} - files: | - main.js - manifest.json - styles.css - create-prerelease: - needs: [define-variables] - name: Create Github Pre-Release - if: needs.define-variables.outputs.pre_release == 'true' - runs-on: ubuntu-latest - env: - NPM_PACKAGE_VERSION: ${{ needs.define-variables.outputs.version }} - steps: - - uses: actions/checkout@v3 - - name: Build Plugin - run: | - npm install - npm run version - npm run build - - uses: "marvinpinto/action-automatic-releases@latest" - name: Publish Release - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - prerelease: true - title: ${{ needs.define-variables.outputs.version }} - files: | - main.js - manifest.json - styles.css + define-variables: + name: Defining and extracting Variables + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get_version.outputs.VERSION }} + label: ${{ steps.get_label.outputs.LABEL }} + pre_release: ${{ steps.prerelease_check.outputs.PRE_RELEASE }} + steps: + - uses: actions/checkout@v3 + - name: Get Version + id: get_version + # E.g. converts ref/tags/v0.0.1 -> 0.0.1 + run: echo "VERSION=$(echo $GITHUB_REF | cut -d / -f 3 | cut -c 1-)" >> $GITHUB_OUTPUT + - name: Get Label + id: get_label + # E.g. converts 0.0.1-beta.1 -> beta + run: echo "LABEL=$(echo ${{ steps.get_version.outputs.VERSION }} | awk -F '-' '{split($2, arr, "."); print arr[1]}')" >> $GITHUB_OUTPUT + - name: Check for Pre-Release or Release + id: prerelease_check + # perform secret check & put boolean result as an output + run: | + if [ "${{ steps.get_label.outputs.LABEL }}" != '' ]; then + echo "PRE_RELEASE=true" >> $GITHUB_OUTPUT + else + echo "PRE_RELEASE=false" >> $GITHUB_OUTPUT + fi + create-release: + needs: [define-variables] + name: Create Github Release + if: needs.define-variables.outputs.pre_release == 'false' + runs-on: ubuntu-latest + env: + NPM_PACKAGE_VERSION: ${{ needs.define-variables.outputs.version }} + steps: + - uses: actions/checkout@v3 + - name: Build Plugin + run: | + npm install + npm run version + npm run build + - uses: 'marvinpinto/action-automatic-releases@latest' + name: Publish Release + with: + repo_token: '${{ secrets.GITHUB_TOKEN }}' + prerelease: false + title: ${{ needs.define-variables.outputs.version }} + files: | + main.js + manifest.json + styles.css + create-prerelease: + needs: [define-variables] + name: Create Github Pre-Release + if: needs.define-variables.outputs.pre_release == 'true' + runs-on: ubuntu-latest + env: + NPM_PACKAGE_VERSION: ${{ needs.define-variables.outputs.version }} + steps: + - uses: actions/checkout@v3 + - name: Build Plugin + run: | + npm install + npm run version + npm run build + - uses: 'marvinpinto/action-automatic-releases@latest' + name: Publish Release + with: + repo_token: '${{ secrets.GITHUB_TOKEN }}' + prerelease: true + title: ${{ needs.define-variables.outputs.version }} + files: | + main.js + manifest.json + styles.css diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f00b8e5..e653148 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,44 +1,46 @@ name: Build on: - pull_request: - branches: - - main - push: - branches: - - main + pull_request: + branches: + - main + - feature/v2 + push: + branches: + - main + - feature/v2 jobs: - build-library: - name: Build Plugin - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Setup Node - uses: actions/setup-node@v3 - with: - node-version: 20 - - name: Install dependencies - run: npm install - - name: Build plugin - run: npm run build - run-validations: - name: Validate Code - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Setup Node - uses: actions/setup-node@v3 - with: - node-version: 20 - - name: Install dependencies - run: npm install - - name: Validate code format - run: npm run check - - name: Validate changelog - uses: mindsers/changelog-reader-action@v2.2.0 - with: - # Path to the CHANGELOG file containing the log entries - path: ./CHANGELOG.md - # Specifies if the CHANGELOG.md file should be validated and the behavior of the action - validation_level: error + build-library: + name: Build Plugin + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 20 + - name: Install dependencies + run: npm install + - name: Build plugin + run: npm run build + run-validations: + name: Validate Code + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 20 + - name: Install dependencies + run: npm install + - name: Validate code format + run: npm run check + - name: Validate changelog + uses: mindsers/changelog-reader-action@v2.2.0 + with: + # Path to the CHANGELOG file containing the log entries + path: ./CHANGELOG.md + # Specifies if the CHANGELOG.md file should be validated and the behavior of the action + validation_level: error diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..d9be5ad --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "printWidth": 100, + "singleQuote": true, + "useTabs": true, + "tabWidth": 2, + "semi": true, + "endOfLine": "lf" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 34bc9ff..28f9659 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,40 +5,48 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Next] - 2024-09-20 + +### Changed + +- [⚙️] Code structure to support new processor blocks +- [⚙️] Added `.prettierrc` config to force correct formatting (e.g. single quotes) +- [⚙️] Adde better typings for some functions + ## [1.3.0] - 2024-08-06 ### Added -- [🚀] Added option to search for a location inside location block (@nehnehneh) -- [🚀] Supporting alternate syntax using [latitude, longitude] array (#18) -- [🚀] Allow reverse order of coordinates (e.g. if copied from Google Maps) via setting (#21) +- [🚀] Added option to search for a location inside location block (@nehnehneh) +- [🚀] Supporting alternate syntax using [latitude, longitude] array (#18) +- [🚀] Allow reverse order of coordinates (e.g. if copied from Google Maps) via setting (#21) ### Changed -- [⚙️] ci: added step to check commit messages on build workflow +- [⚙️] ci: added step to check commit messages on build workflow ### Fixed -- [🐛] Fixed issue with custom marker icon not being displayed correctly +- [🐛] Fixed issue with custom marker icon not being displayed correctly ## [1.2.0] - 2024-07-26 ### Added -- [🚀] Adding support for custom zoom level (thanks to @nehnehneh) +- [🚀] Adding support for custom zoom level (thanks to @nehnehneh) ## [1.1.1] - 2024-06-11 ### Fixed -- [💎] Renaming `Custom Marker Icon URL` to `Custom marker URL` to follow Obsidian guidelines -- [💎] Showing a toast notice if the user uses an invalid combination of maki icon and custom marker url -- [💎] Showing a notice if the plugin has updated so that the user may check for new features +- [💎] Renaming `Custom Marker Icon URL` to `Custom marker URL` to follow Obsidian guidelines +- [💎] Showing a toast notice if the user uses an invalid combination of maki icon and custom marker url +- [💎] Showing a notice if the plugin has updated so that the user may check for new features ## [1.1.0] - 2024-05-14 ### Added -- [🚀] Adding support for custom icons and different maki icons (#15) -- [🚀] Adding support for different map styles (#17) -- [📚] Adding documentation new documentation file +- [🚀] Adding support for custom icons and different maki icons (#15) +- [🚀] Adding support for different map styles (#17) +- [📚] Adding documentation new documentation file diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..6230ec7 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +@aaronczichon \ No newline at end of file diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index c25382f..437c4b2 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -47,20 +47,20 @@ search: 123 smith street fitzroy Search phrases can be: -- Addresses, e.g.: +- Addresses, e.g.: - - 123 Smith Street Fitzroy + - 123 Smith Street Fitzroy -- Locations or place names, e.g.: +- Locations or place names, e.g.: - - Barcelona - - Brooklyn New York - - Bondi Beach Sydney + - Barcelona + - Brooklyn New York + - Bondi Beach Sydney -- Landmarks or points of interest, e.g.: - - The Dolomites Italy - - Parliament house canberra - - Melbourne Cricket Ground +- Landmarks or points of interest, e.g.: + - The Dolomites Italy + - Parliament house canberra + - Melbourne Cricket Ground The search is reasonably intelligent and you do not need to be exact in syntax, or provide full addresses etc. When returning a result the plugin will provide the full address as a caption to the image (as it shows in Mapbox data). @@ -116,14 +116,14 @@ The result looks like this: Following values for the code block are supported: -- streets-v12 -- outdoors-v12 -- light-v11 -- dark-v11 -- satellite-v9 -- satellite-streets-v12 -- navigation-day-v1 -- navigation-night-v1 +- streets-v12 +- outdoors-v12 +- light-v11 +- dark-v11 +- satellite-v9 +- satellite-streets-v12 +- navigation-day-v1 +- navigation-night-v1 **Hint** Maki icon can only defined in the code block and can't be defined globally. If no custom marker or maki icon is defined, the map falls back to the default marker icon (a home icon). diff --git a/esbuild.config.mjs b/esbuild.config.mjs index e4197e9..7091e3c 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -1,6 +1,6 @@ -import esbuild from "esbuild"; -import process from "process"; -import builtins from "builtin-modules"; +import esbuild from 'esbuild'; +import process from 'process'; +import builtins from 'builtin-modules'; const banner = `/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD @@ -8,36 +8,36 @@ if you want to view the source, please visit the github repository of this plugi */ `; -const prod = process.argv[2] === "production"; +const prod = process.argv[2] === 'production'; const context = await esbuild.context({ banner: { js: banner, }, - entryPoints: ["src/main.ts"], + entryPoints: ['src/main.ts'], bundle: true, external: [ - "obsidian", - "electron", - "@codemirror/autocomplete", - "@codemirror/collab", - "@codemirror/commands", - "@codemirror/language", - "@codemirror/lint", - "@codemirror/search", - "@codemirror/state", - "@codemirror/view", - "@lezer/common", - "@lezer/highlight", - "@lezer/lr", + 'obsidian', + 'electron', + '@codemirror/autocomplete', + '@codemirror/collab', + '@codemirror/commands', + '@codemirror/language', + '@codemirror/lint', + '@codemirror/search', + '@codemirror/state', + '@codemirror/view', + '@lezer/common', + '@lezer/highlight', + '@lezer/lr', ...builtins, ], - format: "cjs", - target: "es2018", - logLevel: "info", - sourcemap: prod ? false : "inline", + format: 'cjs', + target: 'es2018', + logLevel: 'info', + sourcemap: prod ? false : 'inline', treeShaking: true, - outfile: "main.js", + outfile: 'main.js', }); if (prod) { diff --git a/package.json b/package.json index 9d86532..b926df0 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", "version": "node version-bump.mjs && git add manifest.json versions.json package.json", "prepare": "husky", - "prettier": "prettier --write .", - "check": "prettier --check ." + "prettier": "prettier --config ./.prettierrc --write .", + "check": "prettier --config ./.prettierrc --check ." }, "keywords": [], "author": "Aaron Czichon ", diff --git a/src/functions/map.func.ts b/src/functions/map.func.ts index f179892..5b969b4 100644 --- a/src/functions/map.func.ts +++ b/src/functions/map.func.ts @@ -1,18 +1,21 @@ -import { LocationPluginSettings } from "../settings/plugin-settings.types"; +import { Notice } from 'obsidian'; +import { + LocationBlockConfiguration, + LocationPluginSettings, +} from '../settings/plugin-settings.types'; export const getMarkerUrl = ( - codeMarker: string, - makiIcon: string, settings: LocationPluginSettings, + codeMarker?: string, + makiIcon?: string, ) => { // If a marker URL is set in code block use it if (codeMarker) return `url-${encodeURIComponent(codeMarker)}`; // If a marker URL is set in settings use it - if (settings.markerUrl) - return `url-${encodeURIComponent(settings.markerUrl)}`; + if (settings.markerUrl) return `url-${encodeURIComponent(settings.markerUrl)}`; // if no marker URL is set at all, use the default - return `pin-${settings.markerSize}-${makiIcon || "home"}+${settings.markerColor}`; + return `pin-${settings.markerSize}-${makiIcon || 'home'}+${settings.markerColor}`; }; /** @@ -28,24 +31,30 @@ export const getMarkerUrl = ( */ export const getStaticMapImageUrl = ( settings: LocationPluginSettings, - latitude: string = "", - longitude: string = "", - codeMarker: string = "", - makiIcon: string = "", - style: string = "", - zoom: string = "", + config: LocationBlockConfiguration, ): string => { - const markerUrl = getMarkerUrl(codeMarker, makiIcon, settings); + const markerUrl = getMarkerUrl(settings, config.markerUrl, config.makiIcon); - if (codeMarker && makiIcon) { - throw "Both marker URL and Maki icon are set. Setting both is not a valid combination."; + if (config.markerUrl && config.makiIcon) { + throw 'Both marker URL and Maki icon are set. Setting both is not a valid combination.'; } - const mapStyle = style || settings.mapStyle; - - const mapZoom = zoom || settings.mapZoom; - - const imageUrl = `https://api.mapbox.com/styles/v1/mapbox/${mapStyle}/static/${markerUrl}(${longitude},${latitude})/${longitude},${latitude},${mapZoom}/800x400?access_token=${settings.mapboxToken}`; + const mapStyle = config.style || settings.mapStyle; + const mapZoom = config.zoom || settings.mapZoom; + const imageUrl = `https://api.mapbox.com/styles/v1/mapbox/${mapStyle}/static/${markerUrl}(${config.longitude},${config.latitude})/${config.longitude},${config.latitude},${mapZoom}/800x400?access_token=${settings.mapboxToken}`; return imageUrl; }; + +/** + * Checks if mapbox token is available and shows a notice if not. + * @param settings object of location plugin settings + * @returns true if a valid mapbox token is configured, false otherwise + */ +export const hasMapboxToken = (settings: LocationPluginSettings) => { + if (!settings.mapboxToken) { + new Notice('Mapbox access token is not set. Please set it in the plugin settings.', 5000); + return false; + } + return true; +}; diff --git a/src/functions/process-code.func.ts b/src/functions/process-code.func.ts deleted file mode 100644 index 905bd9e..0000000 --- a/src/functions/process-code.func.ts +++ /dev/null @@ -1,93 +0,0 @@ -export const processCodeBlock = (source: string) => { - const rows = source.split("\n"); - - let latitude = findLatitude(rows); - let longitude = findLongitude(rows); - const markerUrl = findMarkerUrl(rows); - const makiIcon = findMarkerIcon(rows); - const mapStyle = findMapStyle(rows); - const mapZoom = findMapZoom(rows); - const searchQuery = findSearchQuery(rows); - const latLong = findLatitudeAndLongitude(rows); - latitude = latLong ? latLong[0] : latitude; - longitude = latLong ? latLong[1] : longitude; - - return { - latitude, - longitude, - markerUrl, - makiIcon, - mapStyle, - mapZoom, - searchQuery, - }; -}; - -const findLatitudeAndLongitude = (rows: string[]) => { - const regex = /^\[\s*(-?\d+(\.\d+)?),\s*(-?\d+(\.\d+)?)\s*\]$/; - let latLong = rows.find((l) => regex.test(l.toLowerCase())); - if (latLong) { - const match = latLong.match(regex); - if (match) { - return [match[1], match[3]]; - } - } -}; - -const findLatitude = (rows: string[]) => { - let latitude = rows.find((l) => l.toLowerCase().startsWith("latitude:")); - if (latitude) - latitude = latitude.toLocaleLowerCase().replace("latitude:", "").trim(); - return latitude; -}; - -const findLongitude = (rows: string[]) => { - let longitude = rows.find((l) => l.toLowerCase().startsWith("longitude:")); - if (longitude) - longitude = longitude - .toLocaleLowerCase() - .replace("longitude:", "") - .trim(); - return longitude; -}; - -const findMarkerUrl = (rows: string[]) => { - let markerUrl = rows.find((l) => l.toLowerCase().startsWith("marker-url:")); - if (markerUrl) - markerUrl = markerUrl - .toLocaleLowerCase() - .replace("marker-url:", "") - .trim(); - return markerUrl; -}; - -const findMarkerIcon = (rows: string[]) => { - let makiIcon = rows.find((l) => l.toLowerCase().startsWith("maki:")); - if (makiIcon) - makiIcon = makiIcon.toLocaleLowerCase().replace("maki:", "").trim(); - return makiIcon; -}; - -const findMapStyle = (rows: string[]) => { - let mapStyle = rows.find((l) => l.toLowerCase().startsWith("style:")); - if (mapStyle) - mapStyle = mapStyle.toLocaleLowerCase().replace("style:", "").trim(); - return mapStyle; -}; - -const findMapZoom = (rows: string[]) => { - let mapZoom = rows.find((l) => l.toLowerCase().startsWith("zoom:")); - if (mapZoom) - mapZoom = mapZoom.toLocaleLowerCase().replace("zoom:", "").trim(); - return mapZoom; -}; - -const findSearchQuery = (rows: string[]) => { - let searchQuery = rows.find((l) => l.toLowerCase().startsWith("search:")); - if (searchQuery) - searchQuery = searchQuery - .toLocaleLowerCase() - .replace("search:", "") - .trim(); - return searchQuery; -}; diff --git a/src/functions/process-location-search.func.ts b/src/functions/process-location-search.func.ts index 3b85d5c..9b0f9f4 100644 --- a/src/functions/process-location-search.func.ts +++ b/src/functions/process-location-search.func.ts @@ -1,9 +1,6 @@ -import { requestUrl } from "obsidian"; +import { requestUrl } from 'obsidian'; -export const processLocationSearch = async ( - query: string = "", - accessToken: string, -) => { +export const processLocationSearch = async (query: string = '', accessToken: string) => { const mapbox_id: string = await hitSuggestAPI(query, accessToken); const result: any = await hitRetrieveAPI(mapbox_id, accessToken); const properties = result.json.features[0].properties; @@ -19,7 +16,7 @@ const hitSuggestAPI = async (query: string, accessToken: string) => { const suggestObject = await requestUrl(suggestUrl); const result = await suggestObject.json; const mapbox_id = result.suggestions[0].mapbox_id; - console.log("***mapbox_id found***" + mapbox_id); + console.log('***mapbox_id found***' + mapbox_id); return mapbox_id; }; diff --git a/src/functions/version-hint.func.ts b/src/functions/version-hint.func.ts index 1547bf9..2fff2c9 100644 --- a/src/functions/version-hint.func.ts +++ b/src/functions/version-hint.func.ts @@ -1,4 +1,4 @@ -import { Notice, Plugin } from "obsidian"; +import { Notice, Plugin } from 'obsidian'; export const checkVersionUpdate = async (plugin: Plugin) => { const data = await plugin.loadData(); diff --git a/src/main.ts b/src/main.ts index 932bde9..d1e72b6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,141 +1,32 @@ -import { Notice, Plugin } from "obsidian"; -import { getStaticMapImageUrl } from "./functions/map.func"; -import { processCodeBlock } from "./functions/process-code.func"; -import { processLocationSearch } from "./functions/process-location-search.func"; -import { checkVersionUpdate } from "./functions/version-hint.func"; -import { LocationSettingTab } from "./settings/plugin-settings.tab"; -import { - DEFAULT_SETTINGS, - LocationPluginSettings, -} from "./settings/plugin-settings.types"; +import { Plugin } from 'obsidian'; +import { checkVersionUpdate } from './functions/version-hint.func'; +import { processLocationCodeBlock } from './processors/process-code.func'; +import { LocationSettingTab } from './settings/plugin-settings.tab'; +import { DEFAULT_SETTINGS, LocationPluginSettings } from './settings/plugin-settings.types'; export default class MapboxPlugin extends Plugin { settings: LocationPluginSettings; async onload() { - // Load the settings initially + // Load the settings initially and check if this is a new version of the plugin. await this.loadSettings(); + await checkVersionUpdate(this); - this.registerMarkdownCodeBlockProcessor( - "location", - this.processLocationCodeBlock, + // Register the processors for the given code blocks. + // Code blocks are used in this plugin to render the maps. + this.registerMarkdownCodeBlockProcessor('location', (source: string, el: HTMLElement) => + processLocationCodeBlock(source, el, this.settings), ); + // Adding the UI settings tab to the Obsidian preferences. this.addSettingTab(new LocationSettingTab(this.app, this)); - - await checkVersionUpdate(this); } - public processLocationCodeBlock = async ( - source: string, - el: HTMLElement, - ) => { - try { - const extractedData = processCodeBlock(source); - let mapboxAccessToken = this.settings.mapboxToken; - if (!mapboxAccessToken) { - this.showNotice( - "Mapbox access token is not set. Please set it in the plugin settings.", - ); - return; - } - - if ( - !extractedData.searchQuery && - (!extractedData.latitude || !extractedData.longitude) - ) { - this.showNotice( - "Error processing location code block. Either Longitude and Latitude are required together, or a search term.", - ); - return; - } - - // check if being run as a search then retrieve and render the image - let lat = extractedData.latitude; - let long = extractedData.longitude; - let address = ""; - if (extractedData.searchQuery) { - const [latitude, longitude, fullAddress] = - await processLocationSearch( - extractedData.searchQuery, - mapboxAccessToken, - ); - lat = latitude.toString(); - long = longitude.toString(); - address = fullAddress; - } - // if we need to flip the order of the coordinates - // then we need to do it before rendering the image - if (this.settings.reverseOrder) { - const temp = lat; - lat = long; - long = temp; - } - this.addStaticImageToContainer( - this.settings, - extractedData, - el, - lat, - long, - ); - if (!extractedData.searchQuery) return; - - // if a search query was used, render the address as text - // render in the address of the location as text - // allows user to confirm the location is the intended one - const searchInfoElement = document.createElement("p"); - searchInfoElement.innerText = address; - searchInfoElement.classList.add("mapbox-image-info"); - el.appendChild(searchInfoElement); - // if not a search then use the long-lat coordinates input by user - } catch (e) { - this.showNotice( - "Error processing location code block. Please check syntax or missing settings.", - ); - } - }; - - private addStaticImageToContainer = ( - settings: LocationPluginSettings, - extractedData: any, - el: HTMLElement, - latitude?: string, - longitude?: string, - ) => { - try { - const imageUrl = getStaticMapImageUrl( - settings, - latitude, - longitude, - extractedData.markerUrl, - extractedData.makiIcon, - extractedData.mapStyle, - extractedData.mapZoom, - ); - - const imageElement = document.createElement("img"); - imageElement.src = imageUrl; - imageElement.classList.add("mapbox-image"); - - el.appendChild(imageElement); - } catch (e) { - this.showNotice(e); - } - }; - public loadSettings = async () => { - this.settings = Object.assign( - {}, - DEFAULT_SETTINGS, - await this.loadData(), - ); + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); }; public saveSettings = async () => { await this.saveData(this.settings); }; - - private showNotice = (message: string) => { - new Notice(message, 5000); - }; } diff --git a/src/processors/process-code.func.ts b/src/processors/process-code.func.ts new file mode 100644 index 0000000..89569be --- /dev/null +++ b/src/processors/process-code.func.ts @@ -0,0 +1,160 @@ +import { Notice } from 'obsidian'; +import { getStaticMapImageUrl, hasMapboxToken } from '../functions/map.func'; +import { processLocationSearch } from '../functions/process-location-search.func'; +import { + LocationBlockConfiguration, + LocationPluginSettings, +} from '../settings/plugin-settings.types'; + +export const processLocationCodeBlock = async ( + source: string, + el: HTMLElement, + settings: LocationPluginSettings, +) => { + try { + const extractedData = processCodeBlock(source); + + // If no Mapbox token is set, we don't need to process the code block. + if (!hasMapboxToken(settings)) return; + + if (!extractedData.searchQuery && (!extractedData.latitude || !extractedData.longitude)) { + new Notice( + 'Error processing location code block. Either Longitude and Latitude are required together, or a search term.', + 5000, + ); + return; + } + + // check if being run as a search then retrieve and render the image + let address = ''; + if (extractedData.searchQuery) { + const [latitude, longitude, fullAddress] = await processLocationSearch( + extractedData.searchQuery, + settings.mapboxToken, + ); + extractedData.latitude = latitude.toString(); + extractedData.longitude = longitude.toString(); + address = fullAddress; + } + // if we need to flip the order of the coordinates + // then we need to do it before rendering the image + if (settings.reverseOrder) { + const temp = extractedData.latitude; + extractedData.latitude = extractedData.longitude; + extractedData.longitude = temp; + } + addStaticImageToContainer(settings, extractedData, el); + if (!extractedData.searchQuery) return; + + // if a search query was used, render the address as text + // render in the address of the location as text + // allows user to confirm the location is the intended one + const searchInfoElement = document.createElement('p'); + searchInfoElement.innerText = address; + searchInfoElement.classList.add('mapbox-image-info'); + el.appendChild(searchInfoElement); + // if not a search then use the long-lat coordinates input by user + } catch (e) { + new Notice( + 'Error processing location code block. Please check syntax or missing settings.', + 5000, + ); + } +}; + +export const processCodeBlock = (source: string) => { + const rows = source.split('\n'); + + let latitude = findLatitude(rows); + let longitude = findLongitude(rows); + const markerUrl = findMarkerUrl(rows); + const makiIcon = findMarkerIcon(rows); + const mapStyle = findMapStyle(rows); + const mapZoom = findMapZoom(rows); + const searchQuery = findSearchQuery(rows); + const latLong = findLatitudeAndLongitude(rows); + latitude = latLong ? latLong[0] : latitude; + longitude = latLong ? latLong[1] : longitude; + + const config: LocationBlockConfiguration = { + latitude, + longitude, + markerUrl, + style: mapStyle, + zoom: mapZoom, + searchQuery, + makiIcon, + }; + return config; +}; + +const addStaticImageToContainer = ( + settings: LocationPluginSettings, + extractedData: LocationBlockConfiguration, + el: HTMLElement, +) => { + try { + const imageUrl = getStaticMapImageUrl(settings, extractedData); + + const imageElement = document.createElement('img'); + imageElement.src = imageUrl; + imageElement.classList.add('mapbox-image'); + + el.appendChild(imageElement); + } catch (e) { + new Notice(e); + } +}; + +const findLatitudeAndLongitude = (rows: string[]) => { + const regex = /^\[\s*(-?\d+(\.\d+)?),\s*(-?\d+(\.\d+)?)\s*\]$/; + let latLong = rows.find((l) => regex.test(l.toLowerCase())); + if (latLong) { + const match = latLong.match(regex); + if (match) { + return [match[1], match[3]]; + } + } +}; + +const findLatitude = (rows: string[]) => { + let latitude = rows.find((l) => l.toLowerCase().startsWith('latitude:')); + if (latitude) latitude = latitude.toLocaleLowerCase().replace('latitude:', '').trim(); + return latitude; +}; + +const findLongitude = (rows: string[]) => { + let longitude = rows.find((l) => l.toLowerCase().startsWith('longitude:')); + if (longitude) longitude = longitude.toLocaleLowerCase().replace('longitude:', '').trim(); + return longitude; +}; + +const findMarkerUrl = (rows: string[]) => { + let markerUrl = rows.find((l) => l.toLowerCase().startsWith('marker-url:')); + if (markerUrl) markerUrl = markerUrl.toLocaleLowerCase().replace('marker-url:', '').trim(); + return markerUrl; +}; + +const findMarkerIcon = (rows: string[]) => { + let makiIcon = rows.find((l) => l.toLowerCase().startsWith('maki:')); + if (makiIcon) makiIcon = makiIcon.toLocaleLowerCase().replace('maki:', '').trim(); + return makiIcon; +}; + +const findMapStyle = (rows: string[]) => { + let mapStyle = rows.find((l) => l.toLowerCase().startsWith('style:')); + if (mapStyle) mapStyle = mapStyle.toLocaleLowerCase().replace('style:', '').trim(); + return mapStyle; +}; + +const findMapZoom = (rows: string[]) => { + let mapZoom = rows.find((l) => l.toLowerCase().startsWith('zoom:')); + if (mapZoom) mapZoom = mapZoom.toLocaleLowerCase().replace('zoom:', '').trim(); + return mapZoom; +}; + +const findSearchQuery = (rows: string[]) => { + let searchQuery = rows.find((l) => l.toLowerCase().startsWith('search:')); + if (searchQuery) searchQuery = searchQuery.toLocaleLowerCase().replace('search:', '').trim(); + return searchQuery; +}; diff --git a/src/settings/plugin-settings.control.ts b/src/settings/plugin-settings.control.ts index 2a1d9f2..3b9099c 100644 --- a/src/settings/plugin-settings.control.ts +++ b/src/settings/plugin-settings.control.ts @@ -1,16 +1,13 @@ -import { Notice, Setting } from "obsidian"; -import MapboxPlugin from "../main"; +import { Notice, Setting } from 'obsidian'; +import MapboxPlugin from '../main'; -export const apiTokenSetting = ( - containerEl: HTMLElement, - plugin: MapboxPlugin, -) => { +export const apiTokenSetting = (containerEl: HTMLElement, plugin: MapboxPlugin) => { new Setting(containerEl) - .setName("Mapbox API token") - .setDesc("Please provide your mapbox API token.") + .setName('Mapbox API token') + .setDesc('Please provide your mapbox API token.') .addText((text) => text - .setPlaceholder("pk.ey...") + .setPlaceholder('pk.ey...') .setValue(plugin.settings.mapboxToken) .onChange(async (value) => { plugin.settings.mapboxToken = value; @@ -19,19 +16,16 @@ export const apiTokenSetting = ( ); }; -export const markerUrlSetting = ( - containerEl: HTMLElement, - plugin: MapboxPlugin, -) => { +export const markerUrlSetting = (containerEl: HTMLElement, plugin: MapboxPlugin) => { new Setting(containerEl) - .setName("Custom marker URL") + .setName('Custom marker URL') .setDesc( - "You can define a custom marker icon by providing a URL which will be used as default marker icon.", + 'You can define a custom marker icon by providing a URL which will be used as default marker icon.', ) - .setTooltip("Recommended size: 50x50px, PNG or JPG format.") + .setTooltip('Recommended size: 50x50px, PNG or JPG format.') .addText((text) => text - .setPlaceholder("https://example.com/icon.png") + .setPlaceholder('https://example.com/icon.png') .setValue(plugin.settings.markerUrl) .onChange(async (value) => { plugin.settings.markerUrl = value; @@ -40,45 +34,37 @@ export const markerUrlSetting = ( ); }; -export const markerSizeSetting = ( - containerEl: HTMLElement, - plugin: MapboxPlugin, -) => { +export const markerSizeSetting = (containerEl: HTMLElement, plugin: MapboxPlugin) => { new Setting(containerEl) - .setName("Marker size") - .setDesc( - "Size of the marker on the map. This is ignored if a custom marker url is set.", - ) + .setName('Marker size') + .setDesc('Size of the marker on the map. This is ignored if a custom marker url is set.') .addDropdown((text) => text - .addOption("s", "small") - .addOption("m", "medium") - .addOption("l", "large") + .addOption('s', 'small') + .addOption('m', 'medium') + .addOption('l', 'large') .setValue(plugin.settings.markerSize) .onChange(async (value) => { - plugin.settings.markerSize = value as "s" | "m" | "l"; + plugin.settings.markerSize = value as 's' | 'm' | 'l'; await plugin.saveSettings(); }), ); }; -export const mapStyleSetting = ( - containerEl: HTMLElement, - plugin: MapboxPlugin, -) => { +export const mapStyleSetting = (containerEl: HTMLElement, plugin: MapboxPlugin) => { new Setting(containerEl) - .setName("Default map style") - .setDesc("Select the default style which is used for your maps.") + .setName('Default map style') + .setDesc('Select the default style which is used for your maps.') .addDropdown((text) => text - .addOption("streets-v12", "Streets") - .addOption("outdoors-v12", "Outdoors") - .addOption("light-v11", "Light") - .addOption("dark-v11", "Dark") - .addOption("satellite-v9", "Satellite") - .addOption("satellite-streets-v12", "Satellite Streets") - .addOption("navigation-day-v1", "Navigation Day") - .addOption("navigation-night-v1", "Navigation Night") + .addOption('streets-v12', 'Streets') + .addOption('outdoors-v12', 'Outdoors') + .addOption('light-v11', 'Light') + .addOption('dark-v11', 'Dark') + .addOption('satellite-v9', 'Satellite') + .addOption('satellite-streets-v12', 'Satellite Streets') + .addOption('navigation-day-v1', 'Navigation Day') + .addOption('navigation-night-v1', 'Navigation Night') .setValue(plugin.settings.mapStyle) .onChange(async (value) => { plugin.settings.mapStyle = value; @@ -87,37 +73,29 @@ export const mapStyleSetting = ( ); }; -export const markerColorSetting = ( - containerEl: HTMLElement, - plugin: MapboxPlugin, -) => { +export const markerColorSetting = (containerEl: HTMLElement, plugin: MapboxPlugin) => { new Setting(containerEl) - .setName("Marker color") - .setDesc("Color of the marker on the map.") + .setName('Marker color') + .setDesc('Color of the marker on the map.') .addColorPicker((text) => - text - .setValue(`#${plugin.settings.markerColor}`) - .onChange(async (value) => { - plugin.settings.markerColor = value.replace("#", ""); - await plugin.saveSettings(); - }), + text.setValue(`#${plugin.settings.markerColor}`).onChange(async (value) => { + plugin.settings.markerColor = value.replace('#', ''); + await plugin.saveSettings(); + }), ); }; -export const mapZoomSetting = ( - containerEl: HTMLElement, - plugin: MapboxPlugin, -) => { +export const mapZoomSetting = (containerEl: HTMLElement, plugin: MapboxPlugin) => { new Setting(containerEl) - .setName("Map zoom") - .setDesc("Set the default zoom for the map image.") + .setName('Map zoom') + .setDesc('Set the default zoom for the map image.') .addDropdown((text) => text - .addOption("20", "20 - closest") - .addOption("15", "15") - .addOption("10", "10") - .addOption("5", "5") - .addOption("1", "1 - furthest") + .addOption('20', '20 - closest') + .addOption('15', '15') + .addOption('10', '10') + .addOption('5', '5') + .addOption('1', '1 - furthest') .setValue(plugin.settings.mapZoom) .onChange(async (value) => { plugin.settings.mapZoom = value; @@ -126,24 +104,17 @@ export const mapZoomSetting = ( ); }; -export const coordinatesReverseOrder = ( - containerEl: HTMLElement, - plugin: MapboxPlugin, -) => { +export const coordinatesReverseOrder = (containerEl: HTMLElement, plugin: MapboxPlugin) => { new Setting(containerEl) - .setName("Reverse order") + .setName('Reverse order') .setDesc( - "If you copy coordinates in a different order (e.g. from Google Maps) you can automatically reverse them.", + 'If you copy coordinates in a different order (e.g. from Google Maps) you can automatically reverse them.', ) .addToggle((reverse) => - reverse - .setValue(plugin.settings.reverseOrder) - .onChange(async (value) => { - plugin.settings.reverseOrder = value; - await plugin.saveSettings(); - new Notice( - "Coordinates order reversed. This affects all your location code blocks.", - ); - }), + reverse.setValue(plugin.settings.reverseOrder).onChange(async (value) => { + plugin.settings.reverseOrder = value; + await plugin.saveSettings(); + new Notice('Coordinates order reversed. This affects all your location code blocks.'); + }), ); }; diff --git a/src/settings/plugin-settings.tab.ts b/src/settings/plugin-settings.tab.ts index 975abcf..f1fbf80 100644 --- a/src/settings/plugin-settings.tab.ts +++ b/src/settings/plugin-settings.tab.ts @@ -1,5 +1,5 @@ -import { App, PluginSettingTab } from "obsidian"; -import MapboxPlugin from "../main"; +import { App, PluginSettingTab } from 'obsidian'; +import MapboxPlugin from '../main'; import { apiTokenSetting, coordinatesReverseOrder, @@ -8,7 +8,7 @@ import { markerColorSetting, markerSizeSetting, markerUrlSetting, -} from "./plugin-settings.control"; +} from './plugin-settings.control'; export class LocationSettingTab extends PluginSettingTab { plugin: MapboxPlugin; diff --git a/src/settings/plugin-settings.types.ts b/src/settings/plugin-settings.types.ts index e8b107b..71eb983 100644 --- a/src/settings/plugin-settings.types.ts +++ b/src/settings/plugin-settings.types.ts @@ -1,6 +1,6 @@ export interface LocationPluginSettings { mapboxToken: string; - markerSize: "s" | "m" | "l"; + markerSize: 's' | 'm' | 'l'; markerColor: string; markerUrl: string; mapStyle: string; @@ -9,11 +9,21 @@ export interface LocationPluginSettings { } export const DEFAULT_SETTINGS: Partial = { - mapboxToken: "", - markerSize: "l", - markerColor: "ff0000", - markerUrl: "", - mapStyle: "streets-v12", - mapZoom: "15", + mapboxToken: '', + markerSize: 'l', + markerColor: 'ff0000', + markerUrl: '', + mapStyle: 'streets-v12', + mapZoom: '15', reverseOrder: false, }; + +export type LocationBlockConfiguration = { + latitude?: string; + longitude?: string; + searchQuery?: string; + makiIcon?: string; + markerUrl?: string; + style?: string; + zoom?: string; +}; diff --git a/version-bump.mjs b/version-bump.mjs index 4fb44ae..8706ceb 100644 --- a/version-bump.mjs +++ b/version-bump.mjs @@ -1,12 +1,12 @@ -import { readFileSync, writeFileSync } from "fs"; +import { readFileSync, writeFileSync } from 'fs'; const targetVersion = process.env.NPM_PACKAGE_VERSION; // read minAppVersion from manifest.json and bump version to target version -let manifest = JSON.parse(readFileSync("manifest.json", "utf8")); +let manifest = JSON.parse(readFileSync('manifest.json', 'utf8')); const { minAppVersion } = manifest; manifest.version = targetVersion; -writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t")); +writeFileSync('manifest.json', JSON.stringify(manifest, null, '\t')); // update versions.json with target version and minAppVersion from manifest.json // let versions = JSON.parse(readFileSync("versions.json", "utf8")); @@ -14,6 +14,6 @@ writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t")); // writeFileSync("versions.json", JSON.stringify(versions, null, "\t")); // update package.json with version update -let packageJson = JSON.parse(readFileSync("package.json", "utf8")); +let packageJson = JSON.parse(readFileSync('package.json', 'utf8')); packageJson.version = targetVersion; -writeFileSync("package.json", JSON.stringify(packageJson, null, "\t")); +writeFileSync('package.json', JSON.stringify(packageJson, null, '\t'));