Skip to content

Commit 883f1b3

Browse files
committed
Enhance offline support: improve offline page formatting, update service worker caching, and add test script to Deno config
1 parent fd5bd1a commit 883f1b3

4 files changed

Lines changed: 134 additions & 77 deletions

File tree

deno.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"build": "deno run -A --env npm:astro build",
2323
"preview": "deno run -A --env npm:astro preview",
2424
"astro": "deno run -A --env npm:astro",
25-
"ci": "deno lint && deno fmt --check && deno check"
25+
"test": "deno test -A",
26+
"ci": "deno lint && deno fmt --check && deno check && deno test -A"
2627
},
2728
"fmt": {
2829
"exclude": [

public/offline.html

Lines changed: 73 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,82 @@
11
<!DOCTYPE html>
22
<html lang="en">
3-
<head>
3+
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>Goaly - Offline</title>
77
<style>
8-
body {
9-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
10-
background-color: #f9fafb;
11-
color: #1f2937;
12-
display: flex;
13-
justify-content: center;
14-
align-items: center;
15-
height: 100vh;
16-
margin: 0;
17-
text-align: center;
18-
padding: 20px;
19-
}
20-
.container {
21-
background-color: #ffffff;
22-
padding: 40px 30px;
23-
border-radius: 16px;
24-
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
25-
max-width: 400px;
26-
width: 100%;
27-
border: 1px solid #f3f4f6;
28-
}
29-
h1 {
30-
font-size: 24px;
31-
font-weight: 700;
32-
margin-top: 0;
33-
margin-bottom: 12px;
34-
color: #111827;
35-
}
36-
p {
37-
font-size: 16px;
38-
color: #4b5563;
39-
line-height: 1.5;
40-
margin-bottom: 24px;
41-
}
42-
.icon {
43-
font-size: 48px;
44-
color: #9ca3af;
45-
margin-bottom: 16px;
46-
}
47-
.retry-btn {
48-
background-color: #005F6A;
49-
color: white;
50-
border: none;
51-
padding: 12px 24px;
52-
font-size: 16px;
53-
font-weight: 600;
54-
border-radius: 8px;
55-
cursor: pointer;
56-
width: 100%;
57-
transition: background-color 0.2s;
58-
}
59-
.retry-btn:hover {
60-
background-color: #004f5a;
61-
}
8+
body {
9+
font-family:
10+
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
11+
Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
12+
"Segoe UI Symbol";
13+
background-color: #f9fafb;
14+
color: #1f2937;
15+
display: flex;
16+
justify-content: center;
17+
align-items: center;
18+
height: 100vh;
19+
margin: 0;
20+
text-align: center;
21+
padding: 20px;
22+
}
23+
.container {
24+
background-color: #ffffff;
25+
padding: 40px 30px;
26+
border-radius: 16px;
27+
box-shadow:
28+
0 4px 6px -1px rgba(0, 0, 0, 0.1),
29+
0 2px 4px -1px rgba(0, 0, 0, 0.06);
30+
max-width: 400px;
31+
width: 100%;
32+
border: 1px solid #f3f4f6;
33+
}
34+
h1 {
35+
font-size: 24px;
36+
font-weight: 700;
37+
margin-top: 0;
38+
margin-bottom: 12px;
39+
color: #111827;
40+
}
41+
p {
42+
font-size: 16px;
43+
color: #4b5563;
44+
line-height: 1.5;
45+
margin-bottom: 24px;
46+
}
47+
.icon {
48+
font-size: 48px;
49+
color: #9ca3af;
50+
margin-bottom: 16px;
51+
}
52+
.retry-btn {
53+
background-color: #005f6a;
54+
color: white;
55+
border: none;
56+
padding: 12px 24px;
57+
font-size: 16px;
58+
font-weight: 600;
59+
border-radius: 8px;
60+
cursor: pointer;
61+
width: 100%;
62+
transition: background-color 0.2s;
63+
}
64+
.retry-btn:hover {
65+
background-color: #004f5a;
66+
}
6267
</style>
63-
</head>
64-
<body>
68+
</head>
69+
<body>
6570
<div class="container">
66-
<div class="icon">🔌</div>
67-
<h1>You're Offline</h1>
68-
<p>Goaly requires an internet connection to sync your goals and track your progress. Please check your connection and try again.</p>
69-
<button class="retry-btn" onclick="window.location.reload()">Try Again</button>
71+
<div class="icon">🔌</div>
72+
<h1>You're Offline</h1>
73+
<p>
74+
Goaly requires an internet connection to sync your goals and track your
75+
progress. Please check your connection and try again.
76+
</p>
77+
<button class="retry-btn" onclick="window.location.reload()">
78+
Try Again
79+
</button>
7080
</div>
71-
</body>
72-
</html>
81+
</body>
82+
</html>

public/sw.js

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,48 @@
1-
const CACHE_NAME = 'goaly-cache-v1';
2-
const OFFLINE_URL = '/offline.html';
1+
// @ts-nocheck: Service worker types are not available here
2+
const CACHE_NAME = "goaly-cache-v1";
3+
const OFFLINE_URL = "/offline.html";
34

4-
self.addEventListener('install', (event) => {
5+
self.addEventListener("install", (event) => {
56
event.waitUntil(
67
caches.open(CACHE_NAME).then((cache) => {
78
return cache.addAll([
89
OFFLINE_URL,
9-
'/logo.png' // pre-caching logo might be a good idea for the offline page
10+
"/logo.png", // pre-caching logo might be a good idea for the offline page
1011
]);
11-
})
12+
}),
1213
);
1314
// Force the waiting service worker to become the active service worker.
1415
self.skipWaiting();
1516
});
1617

17-
self.addEventListener('activate', (event) => {
18+
self.addEventListener("activate", (event) => {
1819
event.waitUntil(
1920
caches.keys().then((cacheNames) => {
2021
return Promise.all(
2122
cacheNames.map((cacheName) => {
2223
if (cacheName !== CACHE_NAME) {
2324
return caches.delete(cacheName);
2425
}
25-
})
26+
}),
2627
);
27-
})
28+
}),
2829
);
2930
// Tell the active service worker to take control of the page immediately.
3031
self.clients.claim();
3132
});
3233

33-
self.addEventListener('fetch', (event) => {
34+
self.addEventListener("fetch", (event) => {
3435
// Only handle GET requests for HTML pages
35-
if (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html')) {
36+
if (
37+
event.request.method === "GET" &&
38+
event.request.headers.get("accept").includes("text/html")
39+
) {
3640
event.respondWith(
3741
fetch(event.request)
3842
.catch(() => {
3943
// If network fails, serve the offline page from cache
4044
return caches.match(OFFLINE_URL);
41-
})
45+
}),
4246
);
4347
}
4448
});

src/lib/scheduler.test.js

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,50 @@ Deno.test("scheduleGoal - 3-hour night block does not over-schedule", async () =
3131
const goal = db.prepare("SELECT * FROM goals WHERE user_id = ? AND name = ?")
3232
.get(user.id, "Night Time Gaming");
3333

34-
// Call scheduleGoal to schedule the goal
35-
await scheduleGoal(user, goal);
34+
const originalFetch = globalThis.fetch;
35+
// @ts-ignore: mock fetch for tests
36+
globalThis.fetch = (url, options) => {
37+
const urlStr = String(url);
38+
const method = options?.method || "GET";
39+
if (urlStr.includes("token")) {
40+
return Promise.resolve(
41+
/** @type {any} */ ({
42+
ok: true,
43+
json: () => Promise.resolve({ access_token: "mock_token" }),
44+
}),
45+
);
46+
}
47+
if (urlStr.includes("calendar/v3/calendars")) {
48+
if (method === "POST") {
49+
return Promise.resolve(
50+
/** @type {any} */ ({
51+
ok: true,
52+
json: () => Promise.resolve({ id: `mock_event_${Date.now()}` }),
53+
}),
54+
);
55+
}
56+
return Promise.resolve(
57+
/** @type {any} */ ({
58+
ok: true,
59+
json: () => Promise.resolve({ items: [] }),
60+
}),
61+
);
62+
}
63+
// Fallback
64+
return Promise.resolve(
65+
/** @type {any} */ ({
66+
ok: true,
67+
json: () => Promise.resolve({}),
68+
}),
69+
);
70+
};
71+
72+
try {
73+
// Call scheduleGoal to schedule the goal
74+
await scheduleGoal(user, goal);
75+
} finally {
76+
globalThis.fetch = originalFetch;
77+
}
3678

3779
// Fetch the created instances
3880
const instances = db.prepare(

0 commit comments

Comments
 (0)