Skip to content

Commit 1c865b7

Browse files
author
Lalit Sharma
committed
feat: update changelog for version 1.1.49, fix Google Maps short-link parsing issues on Android, add regression tests for new parsing logic, and bump mobile version
1 parent eae79aa commit 1c865b7

4 files changed

Lines changed: 117 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.1.49] — 2026-02-27
9+
10+
### Fixed
11+
- Fixed remaining Google Maps short-link parse failures on Android by broadening coordinate extraction fallbacks to handle additional `window.CONFIG` state-array variants and static-map metadata (`center=lat,lon`) when preview `pb` tokens are missing.
12+
- Fixed short-link fallback fetch behavior by re-attempting page-content extraction even when URL expansion does not change the original short URL.
13+
14+
### Added
15+
- Added regression tests for relaxed Google state-array parsing, static-map metadata coordinate extraction, and short-link re-fetch fallback when the first response body is empty.
16+
17+
### Changed
18+
- Bumped `apps/mobile` version to `1.1.49`.
19+
820
## [1.1.48] — 2026-02-27
921

1022
### Fixed

apps/mobile/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@eclipse-timer/mobile",
3-
"version": "1.1.48",
3+
"version": "1.1.49",
44
"private": true,
55
"main": "index.js",
66
"scripts": {

apps/mobile/src/utils/sharedMapLink.ts

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ const GOOGLE_STATE_COORD_RE = new RegExp(
4141
`\\[\\[(${NUMBER_PART})\\s*,\\s*(${NUMBER_PART})\\s*,\\s*(${NUMBER_PART})\\]\\s*,\\s*\\[0\\s*,\\s*0\\s*,\\s*0\\]\\s*,\\s*\\[\\d+\\s*,\\s*\\d+\\]\\s*,\\s*(${NUMBER_PART})\\]`,
4242
"i",
4343
);
44+
const GOOGLE_STATE_COORD_FALLBACK_RE = new RegExp(
45+
`\\[\\[\\s*(${NUMBER_PART})\\s*,\\s*(${NUMBER_PART})\\s*,\\s*(${NUMBER_PART})\\s*\\]\\s*,\\s*\\[[^\\]]{1,80}\\]\\s*,\\s*\\[[^\\]]{1,80}\\]\\s*,\\s*(${NUMBER_PART})\\s*\\]`,
46+
"i",
47+
);
48+
const GOOGLE_STATICMAP_CENTER_RE = new RegExp(
49+
`center=(${NUMBER_PART})(?:%2c|%2C|,)(${NUMBER_PART})`,
50+
"i",
51+
);
4452

4553
type ExpandedShortMapUrlResult = {
4654
expandedUrl: string;
@@ -154,7 +162,15 @@ function parseSharedMapLinkFromUrl(url: URL, rawUrl: string): ParsedSharedMapLin
154162
function parseGooglePreviewCoordinatesFromText(input: string): { lat: number; lon: number } | null {
155163
if (!input) return null;
156164

157-
const match = input.match(GOOGLE_PB_COORD_RE);
165+
const match =
166+
input.match(GOOGLE_PB_COORD_RE) ??
167+
(() => {
168+
try {
169+
return decodeURIComponent(input).match(GOOGLE_PB_COORD_RE);
170+
} catch {
171+
return null;
172+
}
173+
})();
158174
if (!match) return null;
159175

160176
const [, lonRaw = "", latRaw = ""] = match;
@@ -168,7 +184,7 @@ function parseGooglePreviewCoordinatesFromText(input: string): { lat: number; lo
168184
function parseGoogleStateCoordinatesFromText(input: string): { lat: number; lon: number } | null {
169185
if (!input) return null;
170186

171-
const match = input.match(GOOGLE_STATE_COORD_RE);
187+
const match = input.match(GOOGLE_STATE_COORD_RE) ?? input.match(GOOGLE_STATE_COORD_FALLBACK_RE);
172188
if (!match) return null;
173189

174190
const [, _distanceRaw = "", lonRaw = "", latRaw = "", _zoomRaw = ""] = match;
@@ -180,8 +196,27 @@ function parseGoogleStateCoordinatesFromText(input: string): { lat: number; lon:
180196
return sanitizeCoordinates({ lat, lon });
181197
}
182198

199+
function parseGoogleStaticMapCenterFromText(input: string): { lat: number; lon: number } | null {
200+
if (!input) return null;
201+
202+
const match = input.match(GOOGLE_STATICMAP_CENTER_RE);
203+
if (!match) return null;
204+
205+
const [, latRaw = "", lonRaw = ""] = match;
206+
const lat = Number(latRaw);
207+
const lon = Number(lonRaw);
208+
if (!Number.isFinite(lat) || !Number.isFinite(lon)) return null;
209+
if (Math.abs(lat) > 90 || Math.abs(lon) > 180) return null;
210+
211+
return sanitizeCoordinates({ lat, lon });
212+
}
213+
183214
function parseGoogleCoordinatesFromText(input: string): { lat: number; lon: number } | null {
184-
return parseGooglePreviewCoordinatesFromText(input) ?? parseGoogleStateCoordinatesFromText(input);
215+
return (
216+
parseGooglePreviewCoordinatesFromText(input) ??
217+
parseGoogleStateCoordinatesFromText(input) ??
218+
parseGoogleStaticMapCenterFromText(input)
219+
);
185220
}
186221

187222
async function expandShortMapUrlWithResponse(
@@ -364,9 +399,12 @@ export async function parseSharedMapLinkAsync(
364399
}
365400

366401
let parsedFromPreviewPayload = parseGoogleCoordinatesFromText(responseText ?? "");
367-
if (!parsedFromPreviewPayload && expandedUrl !== extracted) {
368-
const expandedPageText = await fetchMapPageText(expandedUrl, options);
369-
parsedFromPreviewPayload = parseGoogleCoordinatesFromText(expandedPageText ?? "");
402+
if (!parsedFromPreviewPayload) {
403+
const fallbackText = await fetchMapPageText(
404+
expandedUrl !== extracted ? expandedUrl : extracted,
405+
options,
406+
);
407+
parsedFromPreviewPayload = parseGoogleCoordinatesFromText(fallbackText ?? "");
370408
}
371409

372410
if (!parsedFromPreviewPayload) return null;

apps/mobile/tests/shared-map-link.test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,40 @@ describe("shared map link parser", () => {
202202
});
203203
});
204204

205+
it("parses short links from relaxed google state payload format", async () => {
206+
const fetchImpl = vi.fn(async () => ({
207+
url: "https://www.google.com/maps/place/Somewhere",
208+
text: async () =>
209+
"window.CONFIG=[...,[[19529.54214059542,-1.55405635,52.276200949999996],[1,2,3],[412.5,915.75],13.1],...];",
210+
}));
211+
212+
const parsed = await parseSharedMapLinkAsync("https://maps.app.goo.gl/abc123", { fetchImpl });
213+
214+
expect(parsed).toEqual({
215+
provider: "google",
216+
lat: 52.276200949999996,
217+
lon: -1.55405635,
218+
rawUrl: "https://maps.app.goo.gl/abc123",
219+
});
220+
});
221+
222+
it("parses short links from staticmap center metadata when pb/state payloads are absent", async () => {
223+
const fetchImpl = vi.fn(async () => ({
224+
url: "https://www.google.com/maps/place/Somewhere",
225+
text: async () =>
226+
'<meta content="https://maps.google.com/maps/api/staticmap?center=52.27620095%2C-1.55405635&amp;zoom=14" property="og:image">',
227+
}));
228+
229+
const parsed = await parseSharedMapLinkAsync("https://maps.app.goo.gl/abc123", { fetchImpl });
230+
231+
expect(parsed).toEqual({
232+
provider: "google",
233+
lat: 52.27620095,
234+
lon: -1.55405635,
235+
rawUrl: "https://maps.app.goo.gl/abc123",
236+
});
237+
});
238+
205239
it("re-fetches expanded url page when short-link response body is unavailable", async () => {
206240
const fetchImpl = vi.fn(async (input: string) => {
207241
if (input === "https://maps.app.goo.gl/abc123") {
@@ -232,6 +266,32 @@ describe("shared map link parser", () => {
232266
expect(fetchImpl).toHaveBeenCalledTimes(2);
233267
});
234268

269+
it("re-fetches short url page when expanded url is unavailable and first body is empty", async () => {
270+
const fetchImpl = vi.fn(async (input: string) => {
271+
if (input === "https://maps.app.goo.gl/abc123" && fetchImpl.mock.calls.length === 1) {
272+
return {
273+
url: input,
274+
};
275+
}
276+
277+
return {
278+
url: input,
279+
text: async () =>
280+
'<meta content="https://maps.google.com/maps/api/staticmap?center=52.27620095%2C-1.55405635&amp;zoom=14" property="og:image">',
281+
};
282+
});
283+
284+
const parsed = await parseSharedMapLinkAsync("https://maps.app.goo.gl/abc123", { fetchImpl });
285+
286+
expect(parsed).toEqual({
287+
provider: "google",
288+
lat: 52.27620095,
289+
lon: -1.55405635,
290+
rawUrl: "https://maps.app.goo.gl/abc123",
291+
});
292+
expect(fetchImpl).toHaveBeenCalledTimes(2);
293+
});
294+
235295
it("returns null when expansion cannot produce parseable map coordinates", async () => {
236296
const fetchImpl = vi.fn(async () => ({
237297
url: "https://www.google.com/maps/place/Somewhere",

0 commit comments

Comments
 (0)