modif privatri

This commit is contained in:
2025-09-19 13:59:54 +02:00
8 changed files with 169 additions and 4946 deletions

View File

@@ -609,3 +609,28 @@ apx.indexedDB.del = async (db, storeName, key) => {
request.onerror = (error) => reject(error);
});
};
apx.indexedDB.getAllKeys = async (db, storeName) => {
return new Promise((resolve, reject) => {
const request = indexedDB.open(db, 1);
request.onsuccess = (event) => {
const db = event.target.result;
const transaction = db.transaction(storeName, "readonly");
const store = transaction.objectStore(storeName);
const keysRequest = store.getAllKeys();
keysRequest.onsuccess = (event) => {
resolve(event.target.result);
};
keysRequest.onerror = (event) => {
reject(event);
};
};
request.onerror = (error) => reject(error);
});
}

View File

@@ -1,6 +1,6 @@
<section class="w-fit min-w-110 h-screen max-h-screen bg-base-100 border-r border-solid border-r-base-300 relative flex flex-col">
<section class="w-fit md:min-w-110 h-screen max-h-screen bg-base-100 border-r border-solid border-r-base-300 relative flex flex-col">
<div class="w-full h-12 max-h-12 flex items-center justify-evenly mt-4 mb-4 flex-shrink-0">
<div class="dropdown h-full">
<div class="dropdown h-full hidden md:block">
<div tabindex="0" role="button" class="h-full flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" class="h-full fill-current text-base-content">
<path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"/>
@@ -14,7 +14,27 @@
{{/appOptions}}
</ul>
</div>
<label class="input rounded-lg md-4">
<div class="drawer md:hidden">
<input id="my-drawer" type="checkbox" class="drawer-toggle"/>
<div class="drawer-content">
<label for="my-drawer" class="btn btn-ghost drawer-button h-full flex items-center hover:bg-transparent hover:shadow-none border-none">
<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" class="h-full fill-current text-base-content">
<path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"/>
</svg>
</label>
</div>
<div class="drawer-side">
<label for="my-drawer" aria-label="close sidebar" class="drawer-overlay"></label>
<ul class="menu bg-base-200 text-base-content min-h-full w-80 p-4">
{{#appOptions}}
<li>
<a data-template="{{template}}" class="rounded-lg">{{{title}}}</a>
</li>
{{/appOptions}}
</ul>
</div>
</div>
<label class="hidden md:flex input rounded-lg md-4">
<svg class="h-4 w-4 text-base-content" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g stroke-linejoin="round" stroke-linecap="round" stroke-width="2.5" fill="none" stroke="currentColor">
<circle cx="11" cy="11" r="8"></circle>
@@ -23,7 +43,7 @@
</svg>
<input id="threadSearchBar" type="search" required placeholder="Search for threads" class="flex-1 bg-transparent outline-none placeholder:opacity-75 placeholder:text-base-content text-base-content" />
</label>
<div class="dropdown dropdown-end">
<div class="hidden md:block dropdown dropdown-end">
<div tabindex="0" role="button" class="m-1">
<svg xmlns="http://www.w3.org/2000/svg" height="28px" viewBox="0 -960 960 960" width="28px" class="h-full fill-current text-base-content">
<path d="M440-160q-17 0-28.5-11.5T400-200v-240L168-736q-15-20-4.5-42t36.5-22h560q26 0 36.5 22t-4.5 42L560-440v240q0 17-11.5 28.5T520-160h-80Zm40-308 198-252H282l198 252Zm0 0Z"/>
@@ -40,7 +60,7 @@
</div>
</div>
<div id="threadsContainer" class="flex-1 overflow-y-auto flex flex-col"></div>
<a class="w-16 h-16 btn bg-base-content rounded-full p-0 shadow-lg flex items-center justify-center absolute bottom-8 right-8" data-template="createThread">
<a class="hidden md:flex w-16 h-16 btn bg-base-content rounded-full p-0 shadow-lg items-center justify-center absolute bottom-8 right-8">
<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" class="h-full fill-current text-base-100">
<path d="M120-160v-600q0-33 23.5-56.5T200-840h480q33 0 56.5 23.5T760-760v203q-10-2-20-2.5t-20-.5q-10 0-20 .5t-20 2.5v-203H200v400h283q-2 10-2.5 20t-.5 20q0 10 .5 20t2.5 20H240L120-160Zm160-440h320v-80H280v80Zm0 160h200v-80H280v80Zm400 280v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM200-360v-400 400Z"/>
</svg>
@@ -67,19 +87,20 @@
</div>
</div>
<div id="messagesContainer" class="w-full flex-1 min-h-0 bg-base-200 overflow-y-auto flex flex-col p-4 gap-y-6"></div>
<div class="flex items-center justify-center w-full h-fit pt-4 pb-4 pl-8 pr-8 gap-x-4">
<button id="attachmentsBtn" class="hover:bg-base-200 transition rounded-full p-2 flex items-center justify-center">
<input id="attachmentsInput" type="file" multiple accept="image/png, image/jpeg" class="hidden"/>
<svg xmlns="http://www.w3.org/2000/svg" height="28px" viewBox="0 -960 960 960" width="28px" class="h-full fill-current text-base-content opacity-50">
<path d="M720-330q0 104-73 177T470-80q-104 0-177-73t-73-177v-370q0-75 52.5-127.5T400-880q75 0 127.5 52.5T580-700v350q0 46-32 78t-78 32q-46 0-78-32t-32-78v-370h80v370q0 13 8.5 21.5T470-320q13 0 21.5-8.5T500-350v-350q-1-42-29.5-71T400-800q-42 0-71 29t-29 71v370q-1 71 49 120.5T470-160q70 0 119-49.5T640-330v-390h80v390Z"/>
</svg>
</button>
<div class="flex items-center rounded-full w-full h-10 bg-base-300">
<input id="messageInput" type="text" placeholder="Write a message..." class="flex-1 bg-transparent border-transparent outline-none px-4" />
<button id="sendMessageBtn" type="button" class="flex justify-center items-center ml-2 p-2 rounded-full hover:bg-base-200 transition">
<div class="flex items-center justify-center w-full h-fit pt-4 pb-4 pl-2 pr-4 md:pl-8 md:pr-12 gap-x-2 md:gap-x-4">
<button id="attachmentsBtn" class="hover:bg-base-200 transition rounded-full p-2 flex items-center justify-center">
<input id="attachmentsInput" type="file" multiple accept="image/png, image/jpeg" class="hidden"/>
<svg xmlns="http://www.w3.org/2000/svg" height="28px" viewBox="0 -960 960 960" width="28px" class="h-full fill-current text-base-content opacity-50">
<path d="M120-160v-640l760 320-760 320Zm80-120 474-200-474-200v140l240 60-240 60v140Zm0 0v-400 400Z" />
<path d="M720-330q0 104-73 177T470-80q-104 0-177-73t-73-177v-370q0-75 52.5-127.5T400-880q75 0 127.5 52.5T580-700v350q0 46-32 78t-78 32q-46 0-78-32t-32-78v-370h80v370q0 13 8.5 21.5T470-320q13 0 21.5-8.5T500-350v-350q-1-42-29.5-71T400-800q-42 0-71 29t-29 71v370q-1 71 49 120.5T470-160q70 0 119-49.5T640-330v-390h80v390Z"/>
</svg>
</button>
<div class="flex items-center rounded-full w-full h-10 bg-base-300">
<input id="messageInput" type="text" placeholder="Write a message..." class="flex-1 bg-transparent border-transparent outline-none px-4" />
<button id="sendMessageBtn" type="button" class="flex justify-center items-center ml-2 p-2 rounded-full hover:bg-base-200 transition">
<svg xmlns="http://www.w3.org/2000/svg" height="28px" viewBox="0 -960 960 960" width="28px" class="h-full fill-current text-base-content opacity-50">
<path d="M120-160v-640l760 320-760 320Zm80-120 474-200-474-200v140l240 60-240 60v140Zm0 0v-400 400Z" />
</svg>
</button>
</div>
</div>
</section>

View File

@@ -3,6 +3,9 @@ var apx = apx || {};
apx.privatri = {};
apx.privatri.templates = {};
const apiUrl = "http://admin.apxtri.farm.test/api/apxtri/privatri"
const tribe = "apxtri";
apx.privatri.loadwco = async (id, dataObj = {}, ctx = null) => {
// check if not authenticate, do nothing cause by default screensignin and wait authentification
// if authenticate, if url xhash then redirect if no url then change wco-link=screenmyworld
@@ -22,18 +25,65 @@ apx.privatri.loadwco = async (id, dataObj = {}, ctx = null) => {
return Mustache.render(template, dataObj);
};
apx.privatri.syncronizeBackend = async (alias, lastConnection) => {
// const response = await fetch
const response = [];
async function getOldestPrivatriids(db, storeName) {
const keysArray = await apx.indexedDB.getAllKeys(db, storeName);
const oldestPerUuid = {};
for (const message of response) {
await apx.indexedDB.set("privatri", "messages", message);
for (const key of keysArray) {
const [uuid, timestampStr] = key.split("_");
const timestamp = Number(timestampStr);
if (!oldestPerUuid[uuid] || timestamp < oldestPerUuid[uuid].timestamp) {
oldestPerUuid[uuid] = { key, timestamp };
};
};
const oldestKeysArray = Object.values(oldestPerUuid).map(obj => obj.key)
const threadsArray = [];
for (key of oldestKeysArray) {
threadsArray.push(await apx.indexedDB.get(db, storeName, key));
};
return threadsArray;
};
apx.privatri.syncronizeBackend = async (alias, lastConnection) => {
const threadsArray = await getOldestPrivatriids("privatri", "messages");
for (const threadObj of threadsArray) {
try {
const response = await fetch(`${apiUrl}/${tribe}/${threadObj.thread}?since=${lastConnection}`);
if (response.ok === false) {
throw new Error("HTTP error");
};
console.log(response);
// for (const message of response) {
// await apx.indexedDB.set("privatri", "messages", message);
// };
} catch (error) {
const displayToastAlert = async (message) => {
return await apx.privatri.loadwco("toastAlert", { message });
};
document.querySelector("body").insertAdjacentHTML("beforeend", await displayToastAlert("An error occurred while synchronizing messages. Please try again later."));
};
};
};
apx.privatri.templates.scripts = {
createThread: async () => {
const bodyEl = document.querySelector("body");
const autoDeletionBtnElArray = document.querySelectorAll("li.autoDeletionBtn");
const displayToastAlert = async (message) => {
return await apx.privatri.loadwco("toastAlert", { message });
};
autoDeletionBtnElArray.forEach(btn => {
btn.addEventListener("click", () => {
@@ -70,10 +120,32 @@ apx.privatri.templates.scripts = {
};
})(publicKey);
// Faire un post sur l'endpoint /privatri
await apx.indexedDB.set("privatri", "threads", { uuid: messageObj.thread, privateKey: privateKey });
await apx.indexedDB.set("privatri", "messages", messageObj);
try {
const response = await fetch(`${apiUrl}/${tribe}/${messageObj.thread}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"xdays": admin.headers.xdays,
"xalias": admin.headers.xalias,
"xlang": admin.headers.xlang,
"xtribe": admin.headers.xtribe,
"xapp": admin.headers.xapp,
"xuuid": admin.headers.xuuid
},
body: JSON.stringify(messageObj)
});
if (response.ok === false) {
throw new Error("HTTP error");
};
await apx.indexedDB.set("privatri", "threads", { uuid: messageObj.thread, privateKey: privateKey });
await apx.indexedDB.set("privatri", "messages", messageObj);
bodyEl.insertAdjacentHTML("beforeend", await displayToastAlert("Thread created successfully."));
} catch (error) {
bodyEl.insertAdjacentHTML("beforeend", await displayToastAlert("An error occurred while creating the thread. Please try again later."));
};
});
},
inviteAlias: async () => {
@@ -92,7 +164,7 @@ apx.privatri.templates.scripts = {
height: 425,
type: "svg",
data: url,
image: "./assets/icon.png",
image: "static/img/icons/privatri.png",
dotsOptions: {
color: "#ffffff",
type: "rounded"
@@ -142,9 +214,10 @@ apx.privatri.templates.scripts = {
const privatriidArray = await getOldestPrivatriids("privatri", "messages");
privatriidArray.forEach(async privatriid => {
if (privatriid.split("_")[0] === uuid) {
aliasesArray = (await apx.indexedDB.get("privatri", "messages", privatriid)).aliases;
const ownerAlias = (await apx.indexedDB.get("privatri", "messages", privatriid)).owner;
if (privatriid.thread === uuid) {
messageObj = privatriid;
aliasesArray = messageObj.aliases;
const ownerAlias = messageObj.owner;
for (const alias of aliasesArray) {
aliasListContainerEl.insertAdjacentHTML("beforeend", await newAlias({
@@ -209,8 +282,8 @@ apx.privatri.templates.scripts = {
let ownerAlias = "";
privatriidArray.forEach(async privatriid => {
if (privatriid.split("_")[0] === uuid) {
messageObj = await apx.indexedDB.get("privatri", "messages", privatriid);
if (privatriid.thread === uuid) {
messageObj = privatriid;
ownerAlias = messageObj.owner;
threadNameInputEl.value = (await apx.crypto.decryptMessage(messageObj.title, privateKey)).data;
@@ -251,61 +324,6 @@ apx.privatri.templates.scripts = {
}
};
async function getOldestPrivatriids(dbName, storeName) {
return new Promise((resolve, reject) => {
const request = indexedDB.open(dbName, 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains("threads")) {
db.createObjectStore("threads", { keyPath: "uuid" });
};
if (!db.objectStoreNames.contains("messages")) {
db.createObjectStore("messages", { keyPath: "privatriid" });
};
};
request.onsuccess = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(storeName)) {
resolve([]);
return;
};
const transaction = db.transaction(storeName, "readonly");
const store = transaction.objectStore(storeName);
const cursorRequest = store.openCursor();
const uuidMap = {};
cursorRequest.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
const obj = cursor.value;
const [uuid, timestamp] = obj.privatriid.split("_");
if (!uuidMap[uuid] || Number(timestamp) < uuidMap[uuid].timestamp) {
uuidMap[uuid] = { privatriid: obj.privatriid, timestamp: Number(timestamp) };
};
cursor.continue();
} else {
const result = Object.values(uuidMap).map(event => event.privatriid);
resolve(result);
};
};
cursorRequest.onerror = (event) => reject(event);
};
request.onerror = (event) => reject(event);
});
};
apx.ready(async () => {
document.querySelector("body").innerHTML = await apx.privatri.loadwco("main", JSON.parse(localStorage.getItem("admin")).tpldata.privatri_main_privatri);
@@ -321,16 +339,17 @@ apx.ready(async () => {
await (async () => {
const lastConnection = JSON.parse(localStorage.getItem("lastConnection")) || Date.now();
await apx.privatri.syncronizeBackend(apx.data.headers.xalias, lastConnection);
// await apx.privatri.syncronizeBackend(apx.data.headers.xalias, lastConnection);
const privatriidArray = await getOldestPrivatriids("privatri", "messages");
console.log(privatriidArray);
const thread = async (name, uuid) => {
return await apx.privatri.loadwco("thread", { uuid, name });
};
for (const privatriid of privatriidArray) {
const obj = await apx.indexedDB.get("privatri", "messages", privatriid)
for (const privatriidObj of privatriidArray) {
const obj = await apx.indexedDB.get("privatri", "messages", privatriidObj.privatriid)
const privateKey = (await apx.indexedDB.get("privatri", "threads", obj.thread)).privateKey;
const name = (await apx.crypto.decryptMessage(obj.title, privateKey)).data;
@@ -542,14 +561,21 @@ apx.ready(async () => {
messageInputEl.value = "";
// Faire un post sur l'endpoint /privatri
await apx.indexedDB.set("privatri", "messages", messageObj);
try {
const response = await fetch(`${apiUrl}/`);
if (response.ok === false) {
throw new Error("HTTP error");
};
await apx.indexedDB.set("privatri", "messages", messageObj);
} catch (error) {
bodyEl.insertAdjacentHTML("beforeend", await displayToastAlert("An error occurred while sending the message. Please try again later."));
};
};
});
window.addEventListener("beforeunload", () => {
// if (apx)
localStorage.setItem("lastConnection", JSON.stringify(Date.now()));
});

View File

@@ -1,7 +1,7 @@
<ul data-uuid="{{uuid}}" class="list hover:bg-base-200 transition">
<li class="list-row items-center">
<li class="gap-x-0 md:gap-x-4 list-row items-center">
<img class="size-12 rounded-full bg-base-content" src=""/>
<div class="text-xl font-medium">{{name}}</div>
<span class="badge h-8 bg-info text-info-content font-semibold border-transparent">17</span>
<div class="hidden md:block text-xl font-medium">{{name}}</div>
<span class="hidden md:flex badge h-8 bg-info text-info-content font-semibold border-transparent">17</span>
</li>
</ul>

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -13,7 +13,7 @@
}
</script>
</head>
<body class="bg-gray-200 flex items-center justify-center min-h-screen">
<body class="bg-base-200 flex items-center justify-center min-h-screen">
<!-- Smartphone container -->
<div class="w-full h-screen sm:w-[600px] bg-white rounded-none sm:rounded-2xl shadow-xl flex flex-col overflow-hidden">

View File

@@ -1,93 +0,0 @@
{
"pages": {
"apxid": {
"version": 1,
"profils": [
"anonymous"
],
"tpl": {
"apxauthmain": "apxtri/objects/wco/apxauth/main",
"apxauthscreensignup": "apxtri/objects/wco/apxauth/screensignup",
"apxauthscreensignin": "apxtri/objects/wco/apxauth/screensignin",
"apxauthscreenlogout": "apxtri/objects/wco/apxauth/screenlogout",
"apxauthscreenmyworld": "apxtri/objects/wco/apxauth/screenmyworld",
"apxauthscreeninformation": "apxtri/objects/wco/apxauth/screeninformation",
"apxauthscreenforgetkey": "apxtri/objects/wco/apxauth/screenforgetkey",
"simplemobnavnavbuttonh": "apxtri/objects/wco/simplemobnav/navbuttonh.mustache",
"simplemobnavnavlist": "apxtri/objects/wco/simplemobnav/navlist.mustache",
"simplemobnavnavbutton": "apxtri/objects/wco/simplemobnav/navbuttonh.mustache",
"simplemobnavmain": "apxtri/objects/wco/simplemobnav/main.mustache"
},
"tpldata": {
"apxid_signature_apxauth": "apxtri/objects/wwws/admin/src/tpldata/apxid_signature_apxauth",
"apxid_authentification_simplemobnav": "apxtri/objects/wwws/admin/src/tpldata/apxid_authentification_simplemobnav",
"apxid_mydata_simplemobnav": "apxtri/objects/wwws/admin/src/tpldata/apxid_mydata_simplemobnav",
"apxid_walletmanager_simplemobnav": "apxtri/objects/wwws/admin/src/tpldata/apxid_walletmanager_simplemobnav"
},
"schema": [
"apxtri/objects/pagans",
"apxtri/objects/persons"
],
"ref": {
"Checkjson": "apxtri/models/tplstrings/Checkjson",
"Notification": "apxtri/models/tplstrings/Notifications",
"Middlewares": "apxtri/models/tplstrings/Middlewares",
"Odmdb": "apxtri/models/tplstrings/Odmdb",
"Pagans": "apxtri/models/tplstrings/Pagans",
"Persons": "apxtri/models/tplstrings/Persons"
}
},
"privatri": {
"version": 1,
"profils": [
"anonymous"
],
"tpl": {
"privatrimain": "apxtri/objects/wco/privatri/main.mustache",
"privatriAlias": "apxtri/objects/wco/privatri/alias.mustache",
"privatriCreateThread": "apxtri/objects/wco/privatri/createThread.mustache",
"privatriEditMessage": "apxtri/objects/wco/privatri/editMessage.mustache",
"privatriInviteAlias": "apxtri/objects/wco/privatri/inviteAlias.mustache",
"privatriMessage": "apxtri/objects/wco/privatri/message.mustache",
"privatriThread": "apxtri/objects/wco/privatri/thread.mustache",
"privatriThreadAliasList": "apxtri/objects/wco/privatri/threadAliasList.mustache",
"privatriThreadSettings": "apxtri/objects/wco/privatri/threadSettings.mustache",
"privatriToastAlert": "apxtri/objects/wco/privatri/toastAlert.mustache"
},
"tpldata": {
"privatri_main_privatri": "apxtri/objects/wwws/admin/src/tpldata/privatri_main_privatri"
},
"schema": [],
"ref": {
"Checkjson": "apxtri/models/tplstrings/Checkjson",
"Notification": "apxtri/models/tplstrings/Notifications",
"Middlewares": "apxtri/models/tplstrings/Middlewares"
}
},
"admindata": {
"version": 1,
"profils": [
"anonymous"
],
"tpl": {
"adminskullverticalnav": "apxtri/objects/wco/adminskull/verticalnav",
"adminskullresult": "apxtri/objects/wco/adminskull/result",
"adminskullmain": "apxtri/objects/wco/adminskull/main",
"adminskullheadnav": "apxtri/objects/wco/adminskull/headnav"
},
"tpldata": {},
"schema": [
"apxtri/objects/pagans",
"apxtri/objects/persons"
],
"ref": {
"Odmdb": "apxtri/models/tplstrings/Odmdb",
"Pagans": "apxtri/models//tplstrings/Pagans",
"Persons": "apxtri/models/tplstrings/Persons",
"Checkjson": "apxtri/models/tplstrings/Checkjson",
"Notification": "apxtri/models/tplstrings/Notifications",
"Middlewares": "apxtri/models/tplstrings/Middlewares"
}
}
}
}