Skip to content

Commit c2688f2

Browse files
committed
feat: dynamic theme handling
1 parent b1d7256 commit c2688f2

File tree

2 files changed

+163
-96
lines changed

2 files changed

+163
-96
lines changed

src/index.js

Lines changed: 106 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
* For details, visit <https://cocreate.app/licenses/> or contact us at [email protected].
2323
*/
2424

25-
import observer from '@cocreate/observer'
26-
import localStorage from '@cocreate/local-storage'
27-
import './web-share'
28-
25+
import observer from "@cocreate/observer";
26+
import localStorage from "@cocreate/local-storage";
27+
import "./web-share";
28+
// import "./theme";
2929

3030
/**
3131
* Warn the page must be served over HTTPS
@@ -41,104 +41,114 @@ import './web-share'
4141
// }
4242

4343
function init() {
44-
const button = document.querySelector('[actions*="install"]');
45-
46-
window.addEventListener('beforeinstallprompt', (event) => {
47-
window.deferredPrompt = event;
48-
if (button)
49-
button.removeAttribute('hidden');
50-
});
51-
52-
if (button) {
53-
button.addEventListener('click', async () => {
54-
// console.log('👍', 'installBtn-clicked');
55-
const promptEvent = window.deferredPrompt;
56-
if (!promptEvent) {
57-
// The deferred prompt isn't available.
58-
return;
59-
}
60-
// Show the install prompt.
61-
promptEvent.prompt();
62-
// Log the result
63-
const result = await promptEvent.userChoice;
64-
console.log('👍', 'userChoice', result);
65-
// Reset the deferred prompt variable, since
66-
// prompt() can only be called once.
67-
window.deferredPrompt = null;
68-
// Hide the install button.
69-
button.setAttribute('hidden', '');
70-
});
71-
}
72-
73-
window.addEventListener('appinstalled', (event) => {
74-
// console.log('👍', 'appinstalled', event);
75-
// Clear the deferredPrompt so it can be garbage collected
76-
window.deferredPrompt = null;
77-
});
78-
79-
if ('serviceWorker' in navigator) {
80-
let workerPath
81-
if (window.CoCreateConfig && window.CoCreateConfig.serviceWorker)
82-
workerPath = window.CoCreateConfig.serviceWorker
83-
else
84-
workerPath = localStorage.getItem("serviceWorker") || '/service-worker.js'
85-
86-
localStorage.setItem("serviceWorker", workerPath);
87-
88-
let cache
89-
if (window.CoCreateConfig && window.CoCreateConfig.cache)
90-
cache = window.CoCreateConfig.cache
91-
else
92-
cache = localStorage.getItem("cache") || 'true'
93-
94-
localStorage.setItem("cache", cache);
95-
workerPath += `?cache=${cache}`
96-
97-
let isPwa = true
98-
if (workerPath) {
99-
navigator.serviceWorker.getRegistrations().then(registrations => {
100-
if (registrations.length === 0 || isPwa == true)
101-
window.addEventListener("load", function () {
102-
navigator.serviceWorker.getRegistration(workerPath).then(registration => {
103-
if (registration && registration.active && registration.active.scriptURL.includes(workerPath)) {
104-
console.log('Service Worker Active')
105-
} else {
106-
navigator.serviceWorker.register(workerPath)
107-
.then(reg => {
108-
reg.onupdatefound = () => {
109-
const installingWorker = reg.installing;
110-
installingWorker.onstatechange = () => {
111-
// console.log('Service Worker', installingWorker.state);
112-
}
113-
}
114-
})
115-
.catch(err => console.log('SW ERROR', err));
116-
}
117-
});
118-
});
119-
});
120-
121-
}
122-
}
44+
const button = document.querySelector('[actions*="install"]');
45+
46+
window.addEventListener("beforeinstallprompt", (event) => {
47+
window.deferredPrompt = event;
48+
if (button) button.removeAttribute("hidden");
49+
});
50+
51+
if (button) {
52+
button.addEventListener("click", async () => {
53+
// console.log('👍', 'installBtn-clicked');
54+
const promptEvent = window.deferredPrompt;
55+
if (!promptEvent) {
56+
// The deferred prompt isn't available.
57+
return;
58+
}
59+
// Show the install prompt.
60+
promptEvent.prompt();
61+
// Log the result
62+
const result = await promptEvent.userChoice;
63+
console.log("👍", "userChoice", result);
64+
// Reset the deferred prompt variable, since
65+
// prompt() can only be called once.
66+
window.deferredPrompt = null;
67+
// Hide the install button.
68+
button.setAttribute("hidden", "");
69+
});
70+
}
71+
72+
window.addEventListener("appinstalled", (event) => {
73+
// console.log('👍', 'appinstalled', event);
74+
// Clear the deferredPrompt so it can be garbage collected
75+
window.deferredPrompt = null;
76+
});
77+
78+
if ("serviceWorker" in navigator) {
79+
let workerPath;
80+
if (window.CoCreateConfig && window.CoCreateConfig.serviceWorker)
81+
workerPath = window.CoCreateConfig.serviceWorker;
82+
else
83+
workerPath =
84+
localStorage.getItem("serviceWorker") || "/service-worker.js";
85+
86+
localStorage.setItem("serviceWorker", workerPath);
87+
88+
let cache;
89+
if (window.CoCreateConfig && window.CoCreateConfig.cache)
90+
cache = window.CoCreateConfig.cache;
91+
else cache = localStorage.getItem("cache") || "true";
92+
93+
localStorage.setItem("cache", cache);
94+
workerPath += `?cache=${cache}`;
95+
96+
let isPwa = true;
97+
if (workerPath) {
98+
navigator.serviceWorker.getRegistrations().then((registrations) => {
99+
if (registrations.length === 0 || isPwa == true)
100+
window.addEventListener("load", function () {
101+
navigator.serviceWorker
102+
.getRegistration(workerPath)
103+
.then((registration) => {
104+
if (
105+
registration &&
106+
registration.active &&
107+
registration.active.scriptURL.includes(
108+
workerPath
109+
)
110+
) {
111+
console.log("Service Worker Active");
112+
} else {
113+
navigator.serviceWorker
114+
.register(workerPath)
115+
.then((reg) => {
116+
reg.onupdatefound = () => {
117+
const installingWorker =
118+
reg.installing;
119+
installingWorker.onstatechange =
120+
() => {
121+
// console.log('Service Worker', installingWorker.state);
122+
};
123+
};
124+
})
125+
.catch((err) =>
126+
console.log("SW ERROR", err)
127+
);
128+
}
129+
});
130+
});
131+
});
132+
}
133+
}
123134
}
124135

125-
init()
136+
init();
126137

127138
async function persistData() {
128-
if (navigator.storage && navigator.storage.persist) {
129-
const result = await navigator.storage.persist();
130-
// console.log(`Data persisted: ${result}`);
131-
}
139+
if (navigator.storage && navigator.storage.persist) {
140+
const result = await navigator.storage.persist();
141+
// console.log(`Data persisted: ${result}`);
142+
}
132143
}
133144

134145
observer.init({
135-
name: 'initPwa',
136-
observe: ['addedNodes'],
137-
selector: '[actions="install"]',
138-
callback: (mutation) => {
139-
init()
140-
}
146+
name: "initPwa",
147+
observe: ["addedNodes"],
148+
selector: '[actions="install"]',
149+
callback: (mutation) => {
150+
init();
151+
}
141152
});
142153

143-
144-
export default { persistData }
154+
export default { persistData };

src/theme.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
function applyThemeAttributes() {
2+
// Detect the current theme (light or dark)
3+
const isDarkMode = window.matchMedia(
4+
"(prefers-color-scheme: dark)"
5+
).matches;
6+
7+
// Select all elements with light or dark attributes
8+
const elements = document.querySelectorAll("[light], [dark]");
9+
10+
elements.forEach((element) => {
11+
// Determine the attribute to use based on the theme
12+
const themeAttr = isDarkMode ? "dark" : "light";
13+
const themeValue = element.getAttribute(themeAttr);
14+
15+
// Determine the current value of the attribute to update
16+
let attributeToUpdate;
17+
if (element.tagName === "LINK" && element.rel === "manifest") {
18+
attributeToUpdate = "href";
19+
} else if (
20+
element.tagName === "META" &&
21+
element.name === "theme-color"
22+
) {
23+
attributeToUpdate = "content";
24+
}
25+
26+
if (attributeToUpdate && themeValue) {
27+
const currentValue = element.getAttribute(attributeToUpdate);
28+
29+
// Update only if the current value does not match the new value
30+
if (currentValue !== themeValue) {
31+
if (element.tagName === "LINK" && element.rel === "manifest") {
32+
// Handle manifest link replacement
33+
const newElement = element.cloneNode(); // Clone the element
34+
newElement.setAttribute(attributeToUpdate, themeValue); // Update href
35+
36+
// Replace the old link with the new one to force fetch
37+
element.parentNode.replaceChild(newElement, element);
38+
console.log(`Manifest updated: ${themeValue}`);
39+
} else {
40+
// For other elements like meta, update the attribute directly
41+
element.setAttribute(attributeToUpdate, themeValue);
42+
if (element.tagName === "META") {
43+
console.log(`Theme color updated: ${themeValue}`);
44+
}
45+
}
46+
}
47+
}
48+
});
49+
}
50+
51+
// Apply theme attributes on page load
52+
applyThemeAttributes();
53+
54+
// Listen for changes in the user's theme preference
55+
window
56+
.matchMedia("(prefers-color-scheme: dark)")
57+
.addEventListener("change", applyThemeAttributes);

0 commit comments

Comments
 (0)