Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<meta content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" name="viewport" />
<meta name="theme-color" content="#ffffff" />
<link rel="manifest" href="/manifest.json" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<%= yield :meta %>

<%= stylesheet_link_tag 'application', media: 'all', 'data-turbo-track': 'reload' %>
<script src="https://use.fontawesome.com/releases/v5.15.4/js/all.js" data-mutate-approach="sync"></script>
<%= javascript_importmap_tags %>
<script>
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js");
}
</script>
</head>

<body class="<%= controller.controller_name %> <%= controller.action_name %>" data-theme="<%= theme_preference %>">
Expand Down
8 changes: 8 additions & 0 deletions app/views/layouts/devise.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<meta content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" name="viewport" />
<meta name="theme-color" content="#ffffff" />
<link rel="manifest" href="/manifest.json" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />

<%= stylesheet_link_tag 'application', media: 'all', 'data-turbo-track': 'reload' %>
<%= javascript_importmap_tags %>
<script src="https://use.fontawesome.com/releases/v5.15.4/js/all.js" data-mutate-approach="sync"></script>
<script>
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js");
}
</script>
</head>

<body data-theme="<%= theme_preference %>">
Expand Down
8 changes: 8 additions & 0 deletions app/views/layouts/home.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<meta content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" name="viewport" />
<meta name="theme-color" content="#ffffff" />
<link rel="manifest" href="/manifest.json" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />

<%= stylesheet_link_tag 'application', media: 'all', 'data-turbo-track': 'reload' %>
<%= javascript_importmap_tags %>
<script src="https://use.fontawesome.com/releases/v5.15.4/js/all.js" data-mutate-approach="sync"></script>
<script>
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js");
}
</script>
</head>

<body class="<%= controller.controller_name %> <%= controller.action_name %>" data-theme="<%= theme_preference %>">
Expand Down
Binary file modified public/apple-touch-icon-precomposed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/apple-touch-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/icons/icon-192x192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/icons/icon-512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/icons/icon-dark-192x192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/icons/icon-dark-512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "Draft",
"short_name": "Draft",
"description": "A focused writing application",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#ffffff",
"icons": [
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
32 changes: 32 additions & 0 deletions public/offline.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Draft - Offline</title>
<style>
body {
font-family: 'Roboto', sans-serif;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
margin: 0;
background: #f5f5f5;
color: #333;
}
.offline {
text-align: center;
padding: 2rem;
}
h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }
p { color: #666; }
</style>
</head>
<body>
<div class="offline">
<h1>You're offline</h1>
<p>Check your connection and try again.</p>
</div>
</body>
</html>
45 changes: 45 additions & 0 deletions public/sw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const CACHE_NAME = "draft-v1";
const OFFLINE_URL = "/offline.html";

const PRECACHE_URLS = [
"/offline.html"
];

self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(PRECACHE_URLS))
);
self.skipWaiting();
});

self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key)))
)
);
self.clients.claim();
});

self.addEventListener("fetch", (event) => {
if (event.request.mode === "navigate") {
event.respondWith(
fetch(event.request).catch(() => caches.match(OFFLINE_URL))
);
return;
}

event.respondWith(
caches.match(event.request).then((cached) => {
if (cached) return cached;
return fetch(event.request).then((response) => {
if (!response || response.status !== 200 || response.type !== "basic") {
return response;
}
const toCache = response.clone();
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, toCache));
return response;
});
})
);
});
48 changes: 48 additions & 0 deletions test/integration/pwa_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
require 'test_helper'

class PwaTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers

test 'manifest.json is served with correct content type' do
get '/manifest.json'
assert_response :success
manifest = JSON.parse(response.body)
assert_equal 'Draft', manifest['name']
assert_equal 'standalone', manifest['display']
assert manifest['icons'].any? { |icon| icon['sizes'] == '512x512' }
end

test 'service worker is served' do
get '/sw.js'
assert_response :success
assert_includes response.body, 'CACHE_NAME'
end

test 'offline page is served' do
get '/offline.html'
assert_response :success
assert_includes response.body, "You're offline"
end

test 'home layout includes PWA tags' do
sign_in users(:bob)
get root_path
assert_response :success

assert_select 'link[rel="manifest"][href="/manifest.json"]'
assert_select 'meta[name="theme-color"]'
assert_select 'link[rel="apple-touch-icon"]'
assert_includes response.body, 'navigator.serviceWorker.register'
end

test 'application layout includes PWA tags' do
sign_in users(:bob)
get stories_path
assert_response :success

assert_select 'link[rel="manifest"][href="/manifest.json"]'
assert_select 'meta[name="theme-color"]'
assert_select 'link[rel="apple-touch-icon"]'
assert_includes response.body, 'navigator.serviceWorker.register'
end
end
Loading