Compare commits

..

No commits in common. "5ac0b07bd716718220dfb9a4f5631b94de234fc9" and "6005e6566e2fe37b097a2cf4636440497bb07167" have entirely different histories.

10 changed files with 52 additions and 154 deletions

64
app.js
View File

@ -27,63 +27,23 @@ async function PickHandle()
} }
async function LoadHandleFiles() async function LoadHandleFiles()
{ {
console.log("fetching room.js", handle); try
if(handle)
{ {
try const module = await import("./data/room.js"+"?rand="+Math.random());
{ /** @type {Record<string, TYPES.GraphParts>} */
const module = await import("./graph/room.js"+"?bust="+Math.random()); const read = module.default();
/** @type {Record<string, TYPES.GraphParts>} */ rooms.val = read;
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 catch(_e)
{ {
console.log("no fs handle has been set, cannot get room graph")
rooms.val = {}; rooms.val = {};
} }
} }
/** @type {Van.State<Record<string, TYPES.GraphParts>>} */ /** @type {Van.State<Record<string, TYPES.GraphParts>>} */
const rooms = van.state({}); const rooms = van.state({});
let handle = await FSHandle.getDirectoryHandle(); let handle = await FSHandle.getDirectoryHandle();
await LoadHandleFiles(); 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>} */ /** @type {Van.State<TYPES.User|false>} */
const loggedIn = van.state(false); const loggedIn = van.state(false);
@ -105,7 +65,7 @@ function Room(room_id, graphParts)
DOM.div.Plain(user.name), DOM.div.Plain(user.name),
()=>{ ()=>{
return DOM.button.Plain( return DOM.button.Plain(
{async onclick(){ {onclick(){
rerender.val++ rerender.val++
loggedIn.val = user; loggedIn.val = user;
}}, }},
@ -162,14 +122,10 @@ function Room(room_id, graphParts)
DOM.strong(data), DOM.strong(data),
)), )),
Div.Plain( Div.Plain(
loggedIn.val ? DOM.button({async onclick(){ loggedIn.val && DOM.button({onclick(){
if(loggedIn.val) data.make(loggedIn.val, "NEW");
{ rerender.val++
await data.make(loggedIn.val, "NEW");
rerender.val++;
}
}}, "Add work!") }}, "Add work!")
: null
) )
)) ))
} }

View File

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

View File

@ -1,8 +1,8 @@
export type User = {name:string, id:string, desk:Set<Desk>}; export type User = {name:string, desk:Set<Desk>};
export type Role = {name:string, id:string, user:User[]}; export type Role = {name: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 Desk = {name: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 Pass = {name: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 Part = {name: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 Work = [time:number, data:string, user:User];
export type Flag = {need:number[], make:number[]} export type Flag = {need:number[], make:number[]}

View File

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

View File

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