Skip to content

Commit 0af2f24

Browse files
committed
feat(parser): ✨ support all Google Timeline/Takeout JSON formats
Add parsing for 4 additional Google Location History formats: - Semantic Location History monthly files (timelineObjects with activitySegment.simplifiedRawPath and placeVisit) - Timeline Edits.json (timelineEdits with rawSignal.signal.position) - Visit segments in semanticSegments (visit.topCandidate.placeLocation) - Flat array format with latitude/longitude fields Update GoogleGuide with tabbed UI showing two export methods: - Phone export (recommended): Google Maps > Timeline > Export - Google Takeout: Records.json, Timeline Edits.json, monthly files Since 2024, Google moved Timeline data to on-device storage so Takeout may no longer have a Location History section. The phone export method now provides the most complete data.
1 parent 74ca234 commit 0af2f24

2 files changed

Lines changed: 242 additions & 99 deletions

File tree

src/components/GoogleGuide.tsx

Lines changed: 84 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,91 @@
11
'use client'
22

3+
import { useState } from 'react'
34
import { X, ExternalLink, Circle } from 'lucide-react'
45

56
interface GoogleGuideProps {
67
isOpen: boolean
78
onClose: () => void
89
}
910

10-
const steps = [
11+
const methods = [
1112
{
12-
number: 1,
13-
title: 'Go to Google Takeout',
14-
items: [
15-
'Visit takeout.google.com',
16-
'Deselect all, then select only "Location History"',
13+
label: 'From your phone (recommended)',
14+
steps: [
15+
{
16+
number: 1,
17+
title: 'Open Google Maps',
18+
items: [
19+
'Tap your profile picture',
20+
'Go to "Your Timeline"',
21+
],
22+
},
23+
{
24+
number: 2,
25+
title: 'Export your data',
26+
items: [
27+
'Tap ⋮ (more) → Settings → Export Timeline data',
28+
'This downloads a location-history.json file',
29+
],
30+
},
31+
{
32+
number: 3,
33+
title: 'Upload to Travelback',
34+
items: [
35+
'Drop the JSON file here',
36+
'Use the timeline slider to select a date range',
37+
],
38+
},
1739
],
18-
action: {
19-
label: 'Open Google Takeout',
20-
href: 'https://takeout.google.com',
21-
},
2240
},
2341
{
24-
number: 2,
25-
title: 'Choose export settings',
26-
items: [
27-
'Select JSON format',
28-
'Choose "Export once"',
29-
'Click "Create export"',
30-
],
31-
},
32-
{
33-
number: 3,
34-
title: 'Download & extract',
35-
items: [
36-
'Wait for the email from Google',
37-
'Download the zip file',
38-
'Extract and find "Records.json" or "Location History.json"',
39-
],
40-
},
41-
{
42-
number: 4,
43-
title: 'Upload to Travelback',
44-
items: [
45-
'Drop the JSON file into Travelback',
46-
'Use the timeline slider to select a date range',
47-
'Play and export your journey!',
42+
label: 'From Google Takeout',
43+
steps: [
44+
{
45+
number: 1,
46+
title: 'Go to Google Takeout',
47+
items: [
48+
'Visit takeout.google.com',
49+
'Deselect all, then select "Location History"',
50+
],
51+
action: {
52+
label: 'Open Google Takeout',
53+
href: 'https://takeout.google.com',
54+
},
55+
},
56+
{
57+
number: 2,
58+
title: 'Download & extract',
59+
items: [
60+
'Create export → wait for email → download zip',
61+
'Any of these files work: Records.json, Timeline Edits.json, or monthly Semantic Location History files (e.g. 2024_JANUARY.json)',
62+
],
63+
},
64+
{
65+
number: 3,
66+
title: 'Upload to Travelback',
67+
items: [
68+
'Drop any of the JSON files here',
69+
'Use the timeline slider to select a date range',
70+
],
71+
},
4872
],
4973
},
5074
]
5175

5276
const tips = [
77+
'Since 2024, Google stores Timeline data on-device — phone export usually has the most complete data',
5378
'Large files (100MB+) may take a moment to parse',
5479
'Use the timeline selector to zoom into specific trips',
55-
'Google Location History updates may be delayed',
5680
]
5781

5882
export default function GoogleGuide({ isOpen, onClose }: GoogleGuideProps) {
83+
const [tab, setTab] = useState(0)
84+
5985
if (!isOpen) return null
6086

87+
const active = methods[tab]
88+
6189
return (
6290
<div
6391
className="absolute inset-0 z-20 flex items-center justify-center"
@@ -69,7 +97,7 @@ export default function GoogleGuide({ isOpen, onClose }: GoogleGuideProps) {
6997
<div className="flex items-center justify-between px-5 py-4 pb-3 sticky top-0 z-10"
7098
style={{ background: 'inherit', borderRadius: 'var(--r-glass) var(--r-glass) 0 0' }}>
7199
<h3 className="text-lg font-bold" style={{ color: 'var(--t1)' }}>
72-
Export Google Location History
100+
Export Google Timeline
73101
</h3>
74102
<button
75103
onClick={onClose}
@@ -81,20 +109,34 @@ export default function GoogleGuide({ isOpen, onClose }: GoogleGuideProps) {
81109
</button>
82110
</div>
83111

84-
{/* Steps */}
112+
{/* Method tabs */}
113+
<div className="px-5 flex gap-2 mb-3">
114+
{methods.map((m, i) => (
115+
<button
116+
key={i}
117+
onClick={() => setTab(i)}
118+
className="px-3 py-1.5 text-xs font-medium rounded-full cursor-pointer transition-colors"
119+
style={{
120+
background: tab === i ? 'rgb(var(--gl))' : 'var(--bg-gi)',
121+
color: tab === i ? '#fff' : 'var(--t3)',
122+
}}
123+
>
124+
{m.label}
125+
</button>
126+
))}
127+
</div>
128+
129+
{/* Steps for active tab */}
85130
<div className="px-5 pb-3 space-y-2">
86-
{steps.map((step) => (
131+
{active.steps.map((step) => (
87132
<div
88133
key={step.number}
89134
className="gi flex gap-3 px-3 py-2.5" style={{ borderRadius: '10px' }}
90135
>
91-
{/* Number circle */}
92136
<div className="flex-shrink-0 w-7 h-7 rounded-full flex items-center justify-center"
93137
style={{ background: 'rgb(var(--gl))' }}>
94138
<span className="text-white text-sm font-bold">{step.number}</span>
95139
</div>
96-
97-
{/* Content */}
98140
<div className="flex-1 min-w-0">
99141
<p className="font-semibold mb-1" style={{ color: 'var(--t1)' }}>
100142
{step.title}
@@ -106,7 +148,7 @@ export default function GoogleGuide({ isOpen, onClose }: GoogleGuideProps) {
106148
</li>
107149
))}
108150
</ul>
109-
{step.action && (
151+
{'action' in step && step.action && (
110152
<a
111153
href={step.action.href}
112154
target="_blank"

0 commit comments

Comments
 (0)