-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpopup.js
More file actions
168 lines (148 loc) · 5.42 KB
/
Copy pathpopup.js
File metadata and controls
168 lines (148 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
const syncBtn = document.getElementById('sync-btn');
const btnText = document.getElementById('btn-text');
const statusDot = document.getElementById('status-dot');
const statusText = document.getElementById('status-text');
const logEl = document.getElementById('log');
const resultEl = document.getElementById('result');
const profileLinks = document.getElementById('profile-links');
const linkProfile = document.getElementById('link-profile');
const linkChallenges= document.getElementById('link-challenges');
const SITE = 'https://garminbadges.com';
function showProfileLinks(username) {
if (!username) return;
linkProfile.href = `${SITE}/users/${username}`;
linkChallenges.href = `${SITE}/users/${username}/challenges`;
profileLinks.classList.remove('hidden');
}
// Load username on popup open — use cache if available, otherwise fetch directly from API
// (The popup runs in the extension context and can fetch any URL in host_permissions.)
async function loadUsername() {
const { username } = await chrome.storage.local.get({ username: '' });
if (username) { showProfileLinks(username); return; }
const { apiKey, apiBase } = await chrome.storage.sync.get({
apiKey: '', apiBase: 'https://api.garminbadges.com/api',
});
if (!apiKey) return;
try {
const resp = await fetch(`${apiBase}/sync/whoami`, {
headers: { 'Authorization': `Bearer ${apiKey}`, 'Accept': 'application/json' },
});
if (resp.ok) {
const user = await resp.json();
const name = user.username ?? user.name ?? null;
if (name) {
await chrome.storage.local.set({ username: name });
showProfileLinks(name);
}
} else if (resp.status === 401) {
setStatus('error', 'Invalid API key — check Settings');
}
} catch (e) {
// Network error — silently ignore, links just won't appear
}
}
loadUsername();
function setStatus(state, text) {
statusDot.className = `status-dot ${state}`;
statusText.textContent = text;
}
function appendLog(text) {
logEl.classList.remove('hidden');
const line = document.createElement('div');
line.className = 'log-line';
line.textContent = text;
logEl.appendChild(line);
logEl.scrollTop = logEl.scrollHeight;
}
function makeResultRow(label, val, muted = false) {
const row = document.createElement('div');
row.className = muted ? 'result-row muted' : 'result-row';
const l = document.createElement('span'); l.className = 'result-label'; l.textContent = label;
const v = document.createElement('span'); v.className = 'result-val'; v.textContent = val;
row.append(l, v);
return row;
}
function showResult(result) {
resultEl.classList.remove('hidden');
resultEl.replaceChildren(
makeResultRow('Added', result.added ?? 0),
makeResultRow('Updated', result.updated ?? 0),
makeResultRow('Unchanged', result.unchanged ?? 0),
makeResultRow('Removed', result.removed ?? 0, true),
...(result.skipped ? [makeResultRow('Skipped', result.skipped, true)] : []),
);
if (result.username) showProfileLinks(result.username);
}
function setSyncing(on) {
syncBtn.disabled = on;
btnText.textContent = on ? 'Syncing…' : 'Sync now';
syncBtn.classList.toggle('syncing', on);
}
function reset() {
logEl.innerHTML = '';
logEl.classList.add('hidden');
resultEl.innerHTML = '';
resultEl.classList.add('hidden');
chrome.runtime.sendMessage({ type: 'sync:reset' });
}
// Restore state from background on open
chrome.runtime.sendMessage({ type: 'popup:getState' }, (state) => {
if (!state) return;
if (state.status === 'syncing') {
setStatus('syncing', 'Syncing…');
setSyncing(true);
state.log.forEach(appendLog);
} else if (state.status === 'done') {
setStatus('done', 'Sync complete');
state.log.forEach(appendLog);
if (state.result) showResult(state.result);
} else if (state.status === 'error') {
setStatus('error', 'Sync failed');
state.log.forEach(appendLog);
}
});
// Listen for live messages during a sync
chrome.runtime.onMessage.addListener((msg) => {
if (msg.type === 'sync:progress') {
setStatus('syncing', 'Syncing…');
appendLog(msg.text);
} else if (msg.type === 'sync:done') {
setStatus('done', 'Sync complete');
setSyncing(false);
if (msg.result) showResult(msg.result);
} else if (msg.type === 'sync:error') {
setStatus('error', msg.text);
setSyncing(false);
appendLog(msg.text);
}
});
// Sync button
syncBtn.addEventListener('click', async () => {
reset();
setStatus('syncing', 'Starting…');
setSyncing(true);
// Prefer the active tab; fall back to any open Garmin Connect tab
const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
let targetTab = activeTab?.url?.includes('connect.garmin.com') ? activeTab : null;
if (!targetTab) {
const garminTabs = await chrome.tabs.query({ url: '*://connect.garmin.com/*' });
if (garminTabs.length > 0) {
targetTab = garminTabs[0];
appendLog('Using existing Garmin Connect tab…');
}
}
if (!targetTab) {
setStatus('error', 'Garmin Connect not open');
appendLog('Open connect.garmin.com and log in, then try again.');
setSyncing(false);
return;
}
chrome.scripting.executeScript({
target: { tabId: targetTab.id },
files: ['sync.js'],
});
});
// Settings button
document.getElementById('settings-btn').addEventListener('click', () => {
chrome.runtime.sendMessage({ type: 'openOptions' });
});