Compare commits

..

3 Commits

Author SHA1 Message Date
5ac0b07bd7 writing works 2025-11-01 08:23:28 -04:00
7a54689f76 you need to get the handle each time 2025-11-01 08:09:05 -04:00
32ea38177e fetch bad :( 2025-10-31 22:55:47 -04:00
10 changed files with 154 additions and 52 deletions

54
app.js
View File

@ -26,24 +26,64 @@ async function PickHandle()
await LoadHandleFiles();
}
async function LoadHandleFiles()
{
console.log("fetching room.js", handle);
if(handle)
{
try
{
const module = await import("./data/room.js"+"?rand="+Math.random());
const module = await import("./graph/room.js"+"?bust="+Math.random());
/** @type {Record<string, TYPES.GraphParts>} */
const read = module.default();
rooms.val = read;
}
catch(_e)
{
console.log("the handle exists, but the request failed. the service work must not be ready yet.")
rooms.val = {};
}
}
else
{
console.log("no fs handle has been set, cannot get room graph")
rooms.val = {};
}
}
/** @type {Van.State<Record<string, TYPES.GraphParts>>} */
const rooms = van.state({});
let handle = await FSHandle.getDirectoryHandle();
await LoadHandleFiles();
/** @type {(path:string[], part:string, time:number, data:string)=>void} */
async function WRITE(path, part, time, data)
{
const fileHandle = await FSHandle.Dig(handle, path, true);
if(fileHandle)
{
const file = await fileHandle.getFile();
const text = await file.text();
let json = {};
if(text)
{
json = JSON.parse(text);
}
let partProp = json[part];
if(!partProp)
{
partProp = [];
json[part] = partProp;
}
partProp.push([time, data]);
const writeable = await fileHandle.createWritable();
await writeable.write(JSON.stringify(json, null, 2));
await writeable.close();
}
}
/** @type {Van.State<TYPES.User|false>} */
const loggedIn = van.state(false);
@ -65,7 +105,7 @@ function Room(room_id, graphParts)
DOM.div.Plain(user.name),
()=>{
return DOM.button.Plain(
{onclick(){
{async onclick(){
rerender.val++
loggedIn.val = user;
}},
@ -122,10 +162,14 @@ function Room(room_id, graphParts)
DOM.strong(data),
)),
Div.Plain(
loggedIn.val && DOM.button({onclick(){
data.make(loggedIn.val, "NEW");
rerender.val++
loggedIn.val ? DOM.button({async onclick(){
if(loggedIn.val)
{
await data.make(loggedIn.val, "NEW");
rerender.val++;
}
}}, "Add work!")
: null
)
))
}

View File

@ -1,4 +1,5 @@
/** @import * as TYPES from "./types.ts" */
import * as FSAccess from "../store-directory-handle.js";
/** @type {TYPES.GraphBuilder} */
export function Room({user, role, part, desk, pass})
@ -11,7 +12,8 @@ export function Room({user, role, part, desk, pass})
for(let userId in user)
{
const name = user[userId];
UserList[userId] = {name, desk:new Set()};
UserList[userId] = {name, id:userId, desk:new Set()};
}
// mutate roles
@ -21,7 +23,7 @@ export function Room({user, role, part, desk, pass})
for(let roleId in role)
{
const [name, ...userIds] = role[roleId];
RoleList[roleId] = {name, user:userIds.map(uId=>UserList[/**@type{string}*/(uId)])};
RoleList[roleId] = {name, id:roleId, user:userIds.map(uId=>UserList[/**@type{string}*/(uId)])};
}
// mutate parts
@ -31,7 +33,7 @@ export function Room({user, role, part, desk, pass})
for(let partId in part)
{
const name = part[partId];
PartList[partId] = /** @type {TYPES.Part} */({name, need:[], make:[], pass:new Map()});
PartList[partId] = /** @type {TYPES.Part} */({name, id:partId, need:[], make:[], pass:new Map()});
}
// mutate desks
@ -47,6 +49,7 @@ export function Room({user, role, part, desk, pass})
/** @type {TYPES.Desk} */
const deskObj = {
name,
id:deskId,
mode,
need,
time,
@ -89,15 +92,34 @@ export function Room({user, role, part, desk, pass})
/** @type {TYPES.Pass} */
const passObj = {
name: pass[passID][0],
id:passID,
path:passID,
async load(){
// make room for this pass to each part and desk
Object.values(PartList).forEach((partObj)=>
{
partObj.pass.set(passObj, {time:0, work:[], make(user, data)
partObj.pass.set(passObj, {time:0, work:[], async make(user, data)
{
this.time = Date.now();
const handle = await FSAccess.getDirectoryHandle();
const path = ["store", context.Path, passID, user.id+".json"];
let text = await FSAccess.Read(handle, path) || "{}";
console.log("json loaded", text);
/** @type {TYPES.UserPassFile} */
const json = JSON.parse(text);
let field = json[partObj.id];
if(!field)
{
field = [];
json[partObj.id] = field;
}
field.push([this.time, data]);
text = JSON.stringify(json, null, 2);
await FSAccess.Write(handle, path, text);
this.work.push(/** @type {TYPES.Work}*/([this.time, data, user]));
partObj.make.forEach((arg)=>{Scan(arg, passObj)});
partObj.need.forEach((arg)=>{Scan(arg, passObj)});
@ -116,9 +138,11 @@ export function Room({user, role, part, desk, pass})
const [userID, userObject] = userData[i];
try
{
const resp = await fetch(`./room/${context.Path}/${passID}/${userID}.json`);
const handle = await FSAccess.getDirectoryHandle();
const text = await FSAccess.Read(handle, ["store", context.Path, passID, userID+".json"]);
console.log("json loaded", text);
/** @type {TYPES.UserPassFile} */
const json = await resp.json();
const json = JSON.parse(text);
Object.entries(json).forEach(([partID, payload])=>{

View File

@ -1,8 +1,8 @@
export type User = {name:string, desk:Set<Desk>};
export type Role = {name:string, user:User[]};
export type Desk = {name:string, need:Part[], time:number[], make:Part[], pass:Map<Pass, Flag>, mode:string, role:Role[]};
export type Pass = {name:string, path:string, live:boolean, load:()=>Promise<void>, dump:()=>void};
export type Part = {name:string, pass:Map<Pass, {time:number, work:Work[], make:(user:User, data:string)=>void}>, need:Desk[], make:Desk[]};
export type User = {name:string, id:string, desk:Set<Desk>};
export type Role = {name:string, id:string, user:User[]};
export type Desk = {name:string, id:string, need:Part[], time:number[], make:Part[], pass:Map<Pass, Flag>, mode:string, role:Role[]};
export type Pass = {name:string, id:string, path:string, live:boolean, load:()=>Promise<void>, dump:()=>void};
export type Part = {name:string, id:string, pass:Map<Pass, {time:number, work:Work[], make:(user:User, data:string)=>void}>, need:Desk[], make:Desk[]};
export type Work = [time:number, data:string, user:User];
export type Flag = {need:number[], make:number[]}

View File

@ -1,40 +1,39 @@
import * as FSAccess from "./store-directory-handle.js";
self.addEventListener('install', ()=> self.skipWaiting()); // Activate worker immediately);
self.addEventListener('activate', ()=> self.clients.claim()); // Become available to all pages);
self.addEventListener('install', ()=>{console.log("SW INSTALL"); return self.skipWaiting()}); // Activate worker immediately);
self.addEventListener('activate', ()=>{
console.log("SW ACTIVATE");
return self.clients.claim();
}); // Become available to all pages);
self.addEventListener('fetch', (event) =>event.respondWith(Interceptor(event)));
async function Interceptor(event)
{
const url = new URL(event.request.url);
const pathname = url.pathname.substring(1);
const parts = pathname.split("/");
if(parts[0] == "data" || parts[0] == "room")
{
console.log("intercept:", pathname)
const handle = await FSAccess.getDirectoryHandle();
if(handle)
{
const file = await FSAccess.drilldown(handle, parts);
if(file)
{
const content = await file.text();
return new Response(content, {
const options = {
headers: {
'Content-Type': 'application/javascript',
'Cache-Control': 'no-cache'
}
});
}
}
console.log("couldnt find:", pathname);
return new Response("404", {status:404});
}
else
/** @type {(event:{request:Request})=>Promise<Response>} */
async function Interceptor(event)
{
const url = new URL(event.request.url);
const pathname = url.pathname.substring(1);
let parts = pathname.split("/");
if(parts[0] == "graph")
{
console.log("intercept:", pathname);
const handle = await FSAccess.getDirectoryHandle();
const text = await FSAccess.Read(handle, parts);
if(text)
{
return new Response(text, options);
}
}
return fetch(event.request);
}
}

View File

@ -17,17 +17,22 @@ export async function setDirectoryHandle(handle) {
const db = await openDB();
const tx = db.transaction('handles', 'readwrite');
tx.objectStore('handles').put(handle, 'user-folder');
console.log("handle set", handle);
await tx.done;
}
// 📂 Retrieve a directory handle
/** @type {()=>Promise<FileSystemDirectoryHandle|false>} */
export async function getDirectoryHandle() {
const db = await openDB();
const tx = db.transaction('handles', 'readonly');
return new Promise((resolve, reject) => {
const getRequest = tx.objectStore('handles').get('user-folder');
getRequest.onsuccess = () => resolve(getRequest.result);
getRequest.onsuccess = () => {
return resolve(getRequest.result);
}
getRequest.onerror = () => {
console.error('Error retrieving directory handle:', getRequest.error);
return resolve(false);
@ -35,18 +40,18 @@ export async function getDirectoryHandle() {
});
}
/** @type {(handle:FileSystemDirectoryHandle, parts:string[])=>Promise<File|false>} */
export async function drilldown(handle, parts)
/** @type {(handle:FileSystemDirectoryHandle, parts:string[], create?:boolean)=>Promise<FileSystemFileHandle|false>} */
export async function Dig(handle, parts, create=false)
{
try
{
let filePointer = handle;
for(let i=0; i<parts.length-1; i++)
{
filePointer = await filePointer.getDirectoryHandle(parts[i], {create: false});
filePointer = await filePointer.getDirectoryHandle(parts[i], {create});
}
const leaf = await filePointer.getFileHandle(parts[parts.length-1], {create: false});
return await leaf.getFile();
const leaf = await filePointer.getFileHandle(parts[parts.length-1], {create});
return leaf;
}
catch(e)
{
@ -54,6 +59,36 @@ export async function drilldown(handle, parts)
}
}
/** @type {(handle:FileSystemDirectoryHandle, parts:string[])=>Promise<string|false>} */
export async function Read(handle, parts)
{
const fileHandle = await Dig(handle, parts);
if(fileHandle)
{
const file = await fileHandle.getFile();
return await file.text();
}
return false;
}
/** @type {(handle:FileSystemDirectoryHandle, parts:string[], text:string)=>Promise<boolean>} */
export async function Write(handle, parts, text)
{
const fileHandle = await Dig(handle, parts, true);
if(fileHandle)
{
const writeable = await fileHandle.createWritable();
await writeable.write(text);
await writeable.close();
return true;
}
return false;
}
// // 🔐 Check or request permission
// async function verifyPermission(handle, mode = 'readwrite') {
// const opts = { mode };