Build Markdown news site with local AI generation and web push

This commit is contained in:
spectre committed 2026-09-12 19:06:14 +02:00
1 parent ad30f12116
commit 199dec8a1e
26 files changed
+2277 -1

No files matched your search

+73
View File
@@ -0,0 +1,73 @@
(async()=>{
const panel=document.querySelector('#push-prompt');
const toggle=document.querySelector('#push-toggle');
const enable=document.querySelector('#push-enable');
const dismiss=document.querySelector('#push-dismiss');
const message=document.querySelector('#push-message');
const status=document.querySelector('#push-status');
let registration;
let config;
let subscription;
const remember=()=>{try{localStorage.setItem('truenews-push-dismissed',String(Date.now()));}catch{}};
const dismissed=()=>{try{return Date.now()-Number(localStorage.getItem('truenews-push-dismissed')||0)<30*86400000;}catch{return false;}};
const api=async(action,value)=>{
const response=await fetch(`/api/push/${action}`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(value)});
if(!response.ok)throw new Error('Could not save your preference. Please try again.');
};
const show=()=>{panel.hidden=false;};
const update=()=>{
toggle.textContent=subscription?'Notifications on':'Get new stories';
enable.textContent=subscription?'Turn off notifications':'Enable notifications';
message.textContent=subscription?'You’ll get a notification when a new article is published.':'Get a browser notification when a new story is published. You can turn this off anytime.';
};
try {
const response=await fetch('/api/push/config');
if(!response.ok)return;
config=await response.json();
if(!config.enabled)return;
toggle.hidden=false;
const supported=window.isSecureContext && 'serviceWorker' in navigator && 'PushManager' in window && 'Notification' in window;
if(!supported){
const explain=()=>{
show();enable.hidden=true;
const apple=/iPad|iPhone|iPod/.test(navigator.userAgent)||(navigator.platform==='MacIntel'&&navigator.maxTouchPoints>1);
message.textContent=apple?'On iPhone or iPad, add TrueNews to your Home Screen, open it from there, then enable notifications.':'Notifications need a supported browser and a secure HTTPS connection.';
};
toggle.addEventListener('click',explain);
if((/iPad|iPhone|iPod/.test(navigator.userAgent)||(navigator.platform==='MacIntel'&&navigator.maxTouchPoints>1))&&!dismissed())explain();
dismiss.addEventListener('click',()=>{panel.hidden=true;remember();});
return;
}
registration=await navigator.serviceWorker.register('/sw.js');
await navigator.serviceWorker.ready;
subscription=await registration.pushManager.getSubscription();
if(subscription && Notification.permission==='granted') {
// Restore the server record if a device revisits after a transient failure.
await api('subscribe',subscription.toJSON()).catch(()=>{status.textContent='Could not sync notifications. You can retry or turn them off below.';});
}
update();
toggle.addEventListener('click',()=>{status.textContent='';update();show();});
dismiss.addEventListener('click',()=>{panel.hidden=true;remember();});
enable.addEventListener('click',async()=>{
enable.disabled=true;status.textContent='';
try {
if(subscription){
await api('unsubscribe',subscription.toJSON());
await subscription.unsubscribe();subscription=null;remember();update();status.textContent='Notifications turned off.';
}else{
// Permission must be requested directly from this user gesture.
const permission=await Notification.requestPermission();
if(permission!=='granted'){
remember();status.textContent=permission==='denied'?'Notifications are blocked. You can change this in your browser’s site settings.':'No problem. You can enable notifications here later.';return;
}
const key=Uint8Array.from(atob(config.publicKey.replace(/-/g,'+').replace(/_/g,'/')),c=>c.charCodeAt(0));
const created=await registration.pushManager.subscribe({userVisibleOnly:true,applicationServerKey:key});
try {await api('subscribe',created.toJSON());}catch(error){await created.unsubscribe();throw error;}
subscription=created;update();status.textContent='You’re subscribed. Watch for the next story.';
}
}catch(error){status.textContent=error.message || 'Could not update notifications. Please try again.';}
finally{enable.disabled=false;}
});
if(!subscription && Notification.permission==='default' && !dismissed()) show();
}catch{toggle.hidden=true;panel.hidden=true;}
})();