Skip to content

Commit b4a0037

Browse files
authored
Shorten query strings (#234)
* Draft updated query strings * Updates for branch patterns * Remove jsoncrush * CHANGELOG * Avoid using parts of the config that might not be set * Handle custom URL * branchId
1 parent 64903d2 commit b4a0037

File tree

7 files changed

+99
-29
lines changed

7 files changed

+99
-29
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## Unreleased
22

3+
- Shorten URLs by reducing map state kept in the hash to the minimum
4+
35
## 0.17.0
46

57
- Make sure Mapbox and Maplibre attributions don't overlap the styles dropdown

dist/bundle.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/bundle.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/App.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
let writingHash = false;
4141
4242
const hashShouldUpdate = () =>
43-
location.hash.slice(1) !== createHashString(settings)?.nextHash;
43+
location.hash.slice(1) !== createHashString(settings, config)?.nextHash;
4444
4545
// Set maps and presets initially using settings
4646
mapsStore.set(settings.maps.map((map, index) => ({ ...map, index })));
@@ -118,7 +118,7 @@
118118
const throttledWriteHash = throttle(() => {
119119
if (hashShouldUpdate()) {
120120
writingHash = true;
121-
writeHash(settings);
121+
writeHash(settings, config);
122122
}
123123
}, 250);
124124

src/components/MapStyleInputWrapper.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@
164164
name: `${pattern.name ?? pattern.id}: ${s} on...`,
165165
branchId: pattern?.id,
166166
id: s,
167+
branchStyle: s,
167168
type: pattern.type,
168169
dropdownType: 'branch',
169170
selected: !!(

src/query.js

Lines changed: 89 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import isEqual from 'lodash.isequal';
22
import { round } from './math';
33
import { linkLocations as linkLocationsStore } from './stores';
4+
import { createBranchUrl } from './branch-utils';
45

56
let linkedLocations;
67
linkLocationsStore.subscribe(value => (linkedLocations = value));
78

89
// Keys that should be encoded/decoded as arrays
9-
const jsonKeys = ['maps', 'locations'];
10+
const jsonKeys = ['locations'];
1011

1112
// Keys that should be encoded/decoded as boolean values
1213
const booleanKeys = ['showCollisions', 'showBoundaries', 'showDiff'];
@@ -31,6 +32,72 @@ function toQueryString(obj) {
3132
return qs;
3233
}
3334

35+
const findStylePreset = (id, stylePresets) => {
36+
const flatPresets = [
37+
...stylePresets.filter(p => p.type !== 'sublist'),
38+
...stylePresets.filter(p => p.type === 'sublist').flatMap(p => p.presets),
39+
];
40+
41+
return flatPresets.find(preset => preset.id === id);
42+
};
43+
44+
const findBranchPattern = (map, branchPatterns) => {
45+
const branchPattern = branchPatterns.find(p => p.id === map.branchId);
46+
if (!branchPattern) return null;
47+
return {
48+
...branchPattern,
49+
url: createBranchUrl(branchPattern.pattern, map.branch, map.branchStyle),
50+
};
51+
};
52+
53+
const encodeMaps = (maps, config) => {
54+
const mapsToEncode = maps.map(m => {
55+
let encodeMap = { id: m.id };
56+
let configMap;
57+
58+
if (m.branchId) {
59+
configMap = findBranchPattern(m, config?.branchPatterns ?? []);
60+
encodeMap = {
61+
branchId: configMap.id,
62+
branchStyle: m.branchStyle,
63+
branch: m.branch,
64+
};
65+
} else {
66+
configMap = findStylePreset(m.id, config.stylePresets);
67+
}
68+
69+
// If not set yet, must be a custom URL
70+
if (!configMap) {
71+
return Object.fromEntries(
72+
Object.entries(m).filter(([k]) => k !== 'style')
73+
);
74+
}
75+
76+
// Only renderer if not default
77+
if (m.renderer !== configMap.renderer) {
78+
encodeMap.renderer = m.renderer;
79+
}
80+
81+
return encodeMap;
82+
});
83+
84+
return JSON.stringify(mapsToEncode);
85+
};
86+
87+
const decodeMaps = (str, config) => {
88+
const maps = JSON.parse(str);
89+
const decodedMaps = maps
90+
.map(m => {
91+
let configMap = findStylePreset(m.id, config?.stylePresets ?? []);
92+
if (!configMap)
93+
configMap = findBranchPattern(m, config?.branchPatterns ?? []);
94+
if (!configMap) return m;
95+
return { ...configMap, ...m };
96+
})
97+
.filter(v => v);
98+
return decodedMaps;
99+
};
100+
34101
const encodeMapParams = ({ zoom, center, pitch, bearing }) => {
35102
return [
36103
round(zoom, 2),
@@ -71,37 +138,33 @@ function fromQueryString(qs) {
71138
return params;
72139
}
73140

74-
// Remove values set to null
141+
// Remove values set to null / false
75142
const cleanSettings = stateObj => {
76143
let nextState = Object.keys(stateObj).reduce((acc, k) => {
77144
const value = stateObj[k];
145+
// Only include booleanKeys if not default
146+
if (booleanKeys.includes(k) && !value) return acc;
78147
if (value !== null) acc[k] = value;
79148
return acc;
80149
}, {});
81150
return nextState;
82151
};
83152

84-
export const createHashString = mapSettings => {
85-
let newMapSettings = JSON.parse(JSON.stringify(mapSettings));
86-
if (newMapSettings.maps?.length ?? 0) {
87-
const newMaps = newMapSettings.maps;
88-
89-
// Remove map styles before hashing
90-
newMapSettings.maps = newMaps.map(m => {
91-
delete m.style;
92-
return m;
93-
});
94-
}
95-
153+
export const createHashString = (mapSettings, config) => {
96154
let nonMapSettings = Object.fromEntries(
97-
Object.entries(newMapSettings)
155+
Object.entries(mapSettings)
98156
.filter(([k, v]) => !mapLocationKeys.includes(k) && k !== 'locations')
99-
.map(([k, v]) => [k, jsonKeys.includes(k) ? JSON.stringify(v) : v])
157+
.map(([k, v]) => {
158+
let encodedValue = v;
159+
if (k === 'maps') encodedValue = encodeMaps(v, config);
160+
else if (jsonKeys.includes(k)) encodedValue = JSON.stringify(v);
161+
return [k, encodedValue];
162+
})
100163
);
101164

102165
nonMapSettings = cleanSettings(nonMapSettings);
103166

104-
const currentHash = readHash(window.location.hash);
167+
const currentHash = readHash(window.location.hash, config);
105168

106169
const requiresHistoryItem = Object.entries(nonMapSettings).some(kv => {
107170
let [k, v] = kv;
@@ -120,13 +183,13 @@ export const createHashString = mapSettings => {
120183

121184
if (linkedLocations) {
122185
updatedSettings = {
123-
map: encodeMapParams(newMapSettings),
186+
map: encodeMapParams(mapSettings),
124187
...updatedSettings,
125188
};
126189
} else {
127190
updatedSettings = {
128191
locations: JSON.stringify(
129-
newMapSettings.locations.map(location => encodeMapParams(location))
192+
mapSettings.locations.map(location => encodeMapParams(location))
130193
),
131194
...updatedSettings,
132195
};
@@ -137,21 +200,25 @@ export const createHashString = mapSettings => {
137200
return { nextHash, requiresHistoryItem };
138201
};
139202

140-
export function writeHash(mapSettings) {
141-
const { nextHash, requiresHistoryItem } = createHashString(mapSettings);
203+
export function writeHash(mapSettings, config) {
204+
const { nextHash, requiresHistoryItem } = createHashString(
205+
mapSettings,
206+
config
207+
);
142208
if (!requiresHistoryItem) {
143209
window.location.replace(`${window.location.pathname}#${nextHash}`);
144210
} else {
145211
window.location.hash = nextHash;
146212
}
147213
}
148214

149-
export function readHash(qs) {
215+
export function readHash(qs, config) {
150216
// Remove unset values, convert value as necessary
151217
let urlState = Object.fromEntries(
152218
Object.entries(fromQueryString(qs))
153219
.filter(([k, v]) => !!v)
154220
.map(([k, v]) => {
221+
if (k === 'maps') return [k, decodeMaps(v, config)];
155222
if (jsonKeys.includes(k)) return [k, JSON.parse(v)];
156223
if (booleanKeys.includes(k)) return [k, v === 'true'];
157224
if (numericKeys.includes(k)) return [k, +v || 0];

src/settings.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ export const getSettings = config => {
88
viewMode,
99
maps,
1010
stylePresets,
11-
...readHash(window.location.hash),
11+
...readHash(window.location.hash, config),
1212
};
1313
};

0 commit comments

Comments
 (0)