Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export function registerCliHandlers(
'graphhopper',
{ profile: params.profile },
settings,
app,
);
return [
`Profile: ${result.profileUsed}`,
Expand Down
15 changes: 12 additions & 3 deletions src/geosearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ export class GeoSearcher {
| geosearch.GoogleProvider = null;
private settings: PluginSettings;
private urlConvertor: UrlConvertor;
private app: App;

constructor(app: App, settings: PluginSettings) {
this.settings = settings;
this.urlConvertor = new UrlConvertor(app, settings);
this.app = app;

if (settings.searchProvider == 'osm') {
if (!settings.osmUser) {
new Notice(
Expand All @@ -45,8 +48,11 @@ export class GeoSearcher {
},
});
} else if (settings.searchProvider == 'google') {
//TODO: this can be improved so that it auto-updates when `apiKey` is changed
this.searchProvider = new geosearch.GoogleProvider({
apiKey: settings.geocodingApiKey,
apiKey: app.secretStorage.getSecret(
settings.geocodingApiKeySecret,
),
});
}
}
Expand Down Expand Up @@ -76,13 +82,16 @@ export class GeoSearcher {
if (
this.settings.searchProvider == 'google' &&
this.settings.useGooglePlacesNew2025 &&
this.settings.geocodingApiKey
this.settings.geocodingApiKeySecret
) {
try {
const placesResults = await googlePlacesSearch(
query,
this.settings,
searchArea?.getCenter(),
this.app.secretStorage.getSecret(
this.settings.geocodingApiKeySecret,
),
);
for (const result of placesResults) {
results.push({
Expand Down Expand Up @@ -129,10 +138,10 @@ export async function googlePlacesSearch(
query: string,
settings: PluginSettings,
centerOfSearch: leaflet.LatLng | null,
googleApiKey: string,
): Promise<GeoSearchResult[]> {
if (settings.searchProvider != 'google' || !settings.useGooglePlacesNew2025)
return [];
const googleApiKey = settings.geocodingApiKey;

// Request body for the new Places API
const requestBody = {
Expand Down
1 change: 1 addition & 0 deletions src/mapContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1832,6 +1832,7 @@ export class MapContainer {
marker.location,
menu,
this.settings,
this.app,
);
menu.showAtMouseEvent(ev.originalEvent);
}
Expand Down
4 changes: 4 additions & 0 deletions src/menus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ export function populateRouting(
geolocation,
submenu,
settings,
app,
);
});
}
Expand Down Expand Up @@ -538,6 +539,7 @@ export function populateRouting(
geolocation,
menu,
settings,
app,
);
menu.showAtMouseEvent(originalEvent);
}
Expand All @@ -553,6 +555,7 @@ export function populateRouteToPoint(
geolocation: leaflet.LatLng,
menu: Menu,
settings: settings.PluginSettings,
app: App,
) {
// The first priority is to choose the user-selected routing source.
// If there isn't any, we try the real-time (GPS) location.
Expand Down Expand Up @@ -586,6 +589,7 @@ export function populateRouteToPoint(
{ profile: cleanedProfile },
mapContainer,
settings,
app,
);
});
});
Expand Down
14 changes: 10 additions & 4 deletions src/routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { MapContainer } from 'src/mapContainer';
import MapViewPlugin from 'src/main';
import { type PluginSettings } from 'src/settings';
import * as leaflet from 'leaflet';
import { request, Notice } from 'obsidian';
import { request, Notice, App } from 'obsidian';
import { type GeoJSON } from 'geojson';

type RoutingProvider = 'graphhopper';
Expand All @@ -26,8 +26,12 @@ export async function calcRoute(
provider: RoutingProvider,
params: RoutingParams,
settings: PluginSettings,
app: App,
): Promise<RoutingResult> {
if (!settings.routingGraphHopperApiKey) {
const apiKey = app.secretStorage.getSecret(
settings.routingGraphHopperApiKeySecret,
);
if (!apiKey) {
throw new Error(
'No GraphHopper API key configured in Map View settings.',
);
Expand All @@ -44,7 +48,7 @@ export async function calcRoute(
...settings.routingGraphHopperExtra,
};
const resultContent: any = await request({
url: `https://graphhopper.com/api/1/route?key=${settings.routingGraphHopperApiKey}`,
url: `https://graphhopper.com/api/1/route?key=${apiKey}`,
method: 'POST',
body: JSON.stringify(requestBody),
headers: {
Expand Down Expand Up @@ -78,8 +82,9 @@ export async function doRouting(
params: RoutingParams,
map: MapContainer,
settings: PluginSettings,
app: App,
) {
if (!settings.routingGraphHopperApiKey) {
if (!settings.routingGraphHopperApiKeySecret) {
new Notice(
'You must first provide a GraphHopper API key in the settings.',
);
Expand All @@ -92,6 +97,7 @@ export async function doRouting(
provider,
params,
settings,
app,
);
map.addFloatingRoute(routingResult);
} catch (e) {
Expand Down
43 changes: 42 additions & 1 deletion src/settings.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { LatLng, type PathOptions } from 'leaflet';
import { type SplitDirection, Notice } from 'obsidian';
import { type SplitDirection, App, Notice } from 'obsidian';
import { type MapState, type LegacyMapState, mergeStates } from 'src/mapState';
import type MapViewPlugin from 'src/main';
import * as consts from 'src/consts';
Expand Down Expand Up @@ -52,7 +52,11 @@ export type PluginSettings = {
searchProvider: 'osm' | 'google';
osmUser: string;
searchDelayMs: number;
/**
* @deprecated - Use "geocodingApiKeySecret"
*/
geocodingApiKey: string;
geocodingApiKeySecret: string;
useGooglePlacesNew2025: boolean;
googlePlacesDataFields: string;
saveHistory: boolean;
Expand All @@ -72,7 +76,11 @@ export type PluginSettings = {
zoomOnGeolinkPreview: number;
handleGeolinkContextMenu: boolean;
routingUrl: string;
/**
* @deprecated - use "routingGraphHopperApiKeySecret"
*/
routingGraphHopperApiKey: string;
routingGraphHopperApiKeySecret: string;
routingGraphHopperProfiles: string;
routingGraphHopperExtra: any;
cacheAllTiles: boolean;
Expand All @@ -92,6 +100,8 @@ export type DepracatedFields = {
defaultTags?: string[];
snippetLines?: number;
useGooglePlaces?: boolean;
routingGraphHopperApiKey: string;
geocodingApiKey: string;
};

export type MapLightDark = 'auto' | 'light' | 'dark';
Expand Down Expand Up @@ -285,6 +295,7 @@ export const DEFAULT_SETTINGS: PluginSettings = {
osmUser: '',
searchDelayMs: 250,
geocodingApiKey: '',
geocodingApiKeySecret: '',
useGooglePlacesNew2025: false,
googlePlacesDataFields: '',
mapSources: [
Expand Down Expand Up @@ -317,6 +328,7 @@ export const DEFAULT_SETTINGS: PluginSettings = {
routingUrl:
'https://www.google.com/maps/dir/?api=1&origin={x0},{y0}&destination={x1},{y1}',
routingGraphHopperApiKey: '',
routingGraphHopperApiKeySecret: '',
routingGraphHopperProfiles: 'foot, bike, car',
routingGraphHopperExtra: {},
cacheAllTiles: true,
Expand Down Expand Up @@ -484,6 +496,28 @@ export function convertLegacyGooglePlaces(settings: PluginSettings): boolean {
return changed;
}

export function convertLegacyAPIKeysToSecretStorage(
settings: PluginSettings & DepracatedFields,
app: App,
): boolean {
let changed = false;
if (settings.geocodingApiKey) {
const key = 'obsidian-map-view-geocoding-apikey';
app.secretStorage.setSecret(key, settings.geocodingApiKey);
settings.geocodingApiKeySecret = key;
delete settings.geocodingApiKey;
changed = true;
}
if (settings.routingGraphHopperApiKey) {
const key = 'obsidian-map-view-routing-graphhopper-apikey';
app.secretStorage.setSecret(key, settings.routingGraphHopperApiKey);
settings.geocodingApiKeySecret = key;
delete settings.routingGraphHopperApiKey;
changed = true;
}
return changed;
}

export function convertMarkerIconRulesToDisplayRules(
settings: PluginSettings & DepracatedFields,
) {
Expand Down Expand Up @@ -606,6 +640,13 @@ export async function convertLegacySettings(
);
}

if (convertLegacyAPIKeysToSecretStorage(settings, plugin.app)) {
changed = true;
new Notice(
'Map View: Legacy API keys for Geocoding and/or converted to the new Secret Storage API. You may need to re-link the secret in settings afterwards.',
);
}

completePartialSavedStates(settings);

if (changed) plugin.saveSettings();
Expand Down
45 changes: 23 additions & 22 deletions src/settingsTab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
TextAreaComponent,
Setting,
DropdownComponent,
SecretComponent,
Component,
} from 'obsidian';

import MapViewPlugin from 'src/main';
Expand Down Expand Up @@ -142,21 +144,15 @@ export class SettingsTab extends PluginSettingTab {
.setDesc(
'If using Google as the geocoding search provider, paste the API key here. See the plugin documentation for more details. Changes are applied after restart.',
)
.addText((component) => {
component
.setValue(this.plugin.settings.geocodingApiKey)
.onChange(async (value) => {
this.plugin.settings.geocodingApiKey = value;
await this.plugin.saveSettings();
component.inputEl.style.borderColor = value
? ''
: 'red';
});
component.inputEl.style.borderColor = this.plugin.settings
.geocodingApiKey
? ''
: 'red';
});
.addComponent((component) =>
new SecretComponent(this.app, component)
.setValue(this.plugin.settings.geocodingApiKeySecret)
.onChange((value) => {
this.plugin.settings.geocodingApiKeySecret = value;
this.plugin.saveSettings();
component.style.borderColor = value ? '' : 'red';
}),
);
let googlePlacesControl = new Setting(containerEl)
.setName('Use Google Places for searches')
.setDesc(
Expand Down Expand Up @@ -818,19 +814,24 @@ export class SettingsTab extends PluginSettingTab {
this.plugin.saveSettings();
});
});

new Setting(containerEl)
.setName('GraphHopper API key')
.setDesc(
'You may obtain a free or a paid key from GraphHopper to enable native routing in Map View.',
)
.addText((component) => {
component
.setValue(this.plugin.settings.routingGraphHopperApiKey)
.onChange(async (value: string) => {
this.plugin.settings.routingGraphHopperApiKey = value;
.addComponent((component) =>
new SecretComponent(this.app, component)
.setValue(
this.plugin.settings.routingGraphHopperApiKeySecret,
)
.onChange((value) => {
this.plugin.settings.routingGraphHopperApiKeySecret =
value;
this.plugin.saveSettings();
});
});
}),
);

new Setting(containerEl)
.setName('GraphHopper profiles')
.setDesc(
Expand Down
5 changes: 4 additions & 1 deletion src/urlConvertor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export class UrlConvertor {
async getGeolocationFromGoogleLink(
url: string,
settings: PluginSettings,
app: App,
): Promise<leaflet.LatLng> {
const content = await request({ url: url });
if (this.settings.debug) console.log('Google link: searching url', url);
Expand All @@ -136,7 +137,9 @@ export class UrlConvertor {
const placeName = placeNameMatch[1];
if (this.settings.debug)
console.log('Google link: found place name = ', placeName);
const googleApiKey = settings.geocodingApiKey;
const googleApiKey = app.secretStorage.getSecret(
settings.geocodingApiKeySecret,
);
const params = {
query: placeName,
key: googleApiKey,
Expand Down
1 change: 1 addition & 0 deletions src/viewControls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@ export class RoutingControl extends leaflet.Control {
marker.location,
menu,
this.settings,
this.app,
);
menu.showAtMouseEvent(ev);
}
Expand Down