working chat
This commit is contained in:
parent
3be5efd60c
commit
512a9025b1
@ -186,6 +186,28 @@ body {
|
||||
}
|
||||
|
||||
|
||||
.group-list .member-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.group-list .member-container button {
|
||||
margin-left: 40px;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border: 0px solid var(--primary-color);
|
||||
border-radius: 50px;
|
||||
position: absolute;
|
||||
top: -25px;
|
||||
right: -25px;
|
||||
}
|
||||
|
||||
.group-list .member-container button:hover {
|
||||
background: var(--accent-color)
|
||||
}
|
||||
|
||||
|
||||
/* Zone de chat */
|
||||
.chat-area {
|
||||
display: flex;
|
||||
@ -553,3 +575,23 @@ body {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--primary-color);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--secondary-color);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--accent-color);
|
||||
}
|
@ -49,7 +49,8 @@ class ChatElement extends HTMLElement {
|
||||
private messageState: number = 0;
|
||||
private selectedRole: string | null = null;
|
||||
private userProcessSet: Set<string> = new Set();
|
||||
private addressMap: Record<string, string> = {}; // map each address to its pairing process id
|
||||
private dmMembersSet: Set<string> = new Set();
|
||||
private addressMap: Record<string, string> = {};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@ -156,7 +157,7 @@ class ChatElement extends HTMLElement {
|
||||
if (sendButton) {
|
||||
sendButton.addEventListener('click', () => {
|
||||
this.sendMessage();
|
||||
setTimeout(() => this.reloadMemberChat(this.selectedMember), 500);
|
||||
setTimeout(async () => await this.reloadMemberChat(this.selectedMember), 600);
|
||||
messageInput.value = '';
|
||||
});
|
||||
}
|
||||
@ -168,7 +169,7 @@ class ChatElement extends HTMLElement {
|
||||
if (keyEvent.key === 'Enter' && !keyEvent.shiftKey) {
|
||||
event.preventDefault();
|
||||
this.sendMessage();
|
||||
setTimeout(() => this.reloadMemberChat(this.selectedMember), 500);
|
||||
setTimeout(async () => await this.reloadMemberChat(this.selectedMember), 600);
|
||||
messageInput.value = '';
|
||||
}
|
||||
});
|
||||
@ -285,8 +286,9 @@ class ChatElement extends HTMLElement {
|
||||
|
||||
try {
|
||||
const service = await Services.getInstance();
|
||||
const myAddresses = await service.getMemberFromDevice();
|
||||
if (!myAddresses) {
|
||||
const myProcessId = await this.getMyProcessId();
|
||||
|
||||
if (!myProcessId) {
|
||||
console.error('No paired member found');
|
||||
return;
|
||||
}
|
||||
@ -299,7 +301,7 @@ class ChatElement extends HTMLElement {
|
||||
metadata: {
|
||||
createdAt: timestamp,
|
||||
lastModified: timestamp,
|
||||
sender: myAddresses,
|
||||
sender: myProcessId,
|
||||
recipient: this.selectedMember,
|
||||
}
|
||||
};
|
||||
@ -405,10 +407,37 @@ class ChatElement extends HTMLElement {
|
||||
const memberList = document.createElement('ul');
|
||||
memberList.className = 'member-list active';
|
||||
|
||||
const prioritizedMembers: [string, any][] = [];
|
||||
const remainingMembers: [string, any][] = [];
|
||||
|
||||
for (const [processId, member] of Object.entries(members)) {
|
||||
if (this.dmMembersSet.has(processId)) {
|
||||
prioritizedMembers.push([processId, member]);
|
||||
} else {
|
||||
remainingMembers.push([processId, member]);
|
||||
}
|
||||
}
|
||||
|
||||
const sortedMembers = prioritizedMembers.concat(remainingMembers);
|
||||
|
||||
for (const [processId, member] of Object.entries(members)) {
|
||||
const memberItem = document.createElement('li');
|
||||
memberItem.className = 'member-item';
|
||||
|
||||
if (this.dmMembersSet.has(processId)) {
|
||||
memberItem.style.cssText = `
|
||||
background-color: var(--accent-color);
|
||||
transition: background-color 0.3s ease;
|
||||
cursor: pointer;
|
||||
`;
|
||||
memberItem.onmouseover = () => {
|
||||
memberItem.style.backgroundColor = 'var(--accent-color-hover)';
|
||||
};
|
||||
memberItem.onmouseout = () => {
|
||||
memberItem.style.backgroundColor = 'var(--accent-color)';
|
||||
};
|
||||
}
|
||||
|
||||
const memberContainer = document.createElement('div');
|
||||
memberContainer.className = 'member-container';
|
||||
|
||||
@ -438,10 +467,14 @@ class ChatElement extends HTMLElement {
|
||||
await this.loadMemberChat(processId);
|
||||
});
|
||||
|
||||
const editLabelButton = document.createElement('span');
|
||||
const editLabelButton = document.createElement('button');
|
||||
editLabelButton.className = 'edit-label-button';
|
||||
editLabelButton.textContent = "✏️";
|
||||
|
||||
editLabelButton.addEventListener("click", (event) => {
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
editLabelButton.addEventListener("dblclick", async (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
@ -457,11 +490,12 @@ class ChatElement extends HTMLElement {
|
||||
|
||||
putRequest.onsuccess = () => {
|
||||
emojiSpan.textContent = `${newLabel} : ${emojis}`;
|
||||
this.reloadMemberChat(processId);
|
||||
};
|
||||
});
|
||||
|
||||
memberList.appendChild(memberItem);
|
||||
memberList.appendChild(editLabelButton);
|
||||
memberContainer.appendChild(editLabelButton);
|
||||
}
|
||||
|
||||
groupList.appendChild(memberList);
|
||||
@ -498,10 +532,57 @@ class ChatElement extends HTMLElement {
|
||||
return null;
|
||||
}
|
||||
|
||||
private async lookForMyDms(): Promise<string | null> {
|
||||
const service = await Services.getInstance();
|
||||
const processes = await service.getMyProcesses();
|
||||
const myAddresses = await service.getMemberFromDevice();
|
||||
const allMembers = await service.getAllMembers();
|
||||
|
||||
this.dmMembersSet.clear();
|
||||
|
||||
try {
|
||||
for (const processId of processes) {
|
||||
const process = await service.getProcess(processId);
|
||||
const state = process.states[0];
|
||||
const description = await service.decryptAttribute(state, 'description');
|
||||
if (!description || description !== "dm") {
|
||||
continue;
|
||||
}
|
||||
const roles = await service.getRoles(process);
|
||||
if (!service.rolesContainsMember(roles, myAddresses)) {
|
||||
continue;
|
||||
}
|
||||
const members = roles.dm.members;
|
||||
for (const member of members) {;
|
||||
if (JSON.stringify(member.sp_addresses) !== JSON.stringify(myAddresses)) {
|
||||
this.dmMembersSet.add(member.sp_addresses);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updatedDmMembersSet = new Set<string>();
|
||||
for (const dmMember of this.dmMembersSet) {
|
||||
for (const [processId, member] of Object.entries(allMembers)) {
|
||||
if (JSON.stringify(member.sp_addresses) === JSON.stringify(dmMember)) {
|
||||
updatedDmMembersSet.add(processId);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.dmMembersSet = updatedDmMembersSet;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
console.log("SET DE MEMBRES AVEC QUI JE DM:", this.dmMembersSet);
|
||||
return null;
|
||||
}
|
||||
|
||||
private async loadMemberChat(pairingProcess: string) {
|
||||
try {
|
||||
const service = await Services.getInstance();
|
||||
const myAddresses = await service.getMemberFromDevice();
|
||||
const database = await Database.getInstance();
|
||||
const db = database.db;
|
||||
|
||||
if (!myAddresses) {
|
||||
console.error('No paired member found');
|
||||
return;
|
||||
@ -518,7 +599,19 @@ class ChatElement extends HTMLElement {
|
||||
|
||||
const emojis = await addressToEmoji(pairingProcess);
|
||||
|
||||
chatHeader.textContent = `Chat with ${emojis}`;
|
||||
const transaction = db.transaction("labels", "readonly");
|
||||
const store = transaction.objectStore("labels");
|
||||
const request = store.get(emojis);
|
||||
|
||||
request.onsuccess = () => {
|
||||
const label = request.result;
|
||||
chatHeader.textContent = label ? `Chat with ${label.label} (${emojis})` : `Chat with member (${emojis})`;
|
||||
};
|
||||
|
||||
request.onerror = () => {
|
||||
chatHeader.textContent = `Chat with member (${emojis})`;
|
||||
};
|
||||
|
||||
messagesContainer.innerHTML = '';
|
||||
|
||||
let dmProcessId = await this.lookForDmProcess();
|
||||
@ -599,14 +692,18 @@ class ChatElement extends HTMLElement {
|
||||
const messageElement = document.createElement('div');
|
||||
messageElement.className = 'message-container';
|
||||
|
||||
const isCurrentUser = message.metadata.sender === myAddresses[0];
|
||||
const myProcessId = await this.getMyProcessId();
|
||||
|
||||
const isCurrentUser = message.metadata.sender === myProcessId;
|
||||
messageElement.style.justifyContent = isCurrentUser ? 'flex-end' : 'flex-start';
|
||||
|
||||
const messageContent = document.createElement('div');
|
||||
messageContent.className = 'message user';
|
||||
messageContent.className = isCurrentUser ? 'message user' : 'message';
|
||||
|
||||
const pairingProcess = await this.getMyProcessId();
|
||||
const senderEmoji = await addressToEmoji(pairingProcess);
|
||||
const myEmoji = await addressToEmoji(myProcessId);
|
||||
const otherEmoji = await addressToEmoji(this.selectedMember);
|
||||
|
||||
const senderEmoji = isCurrentUser ? myEmoji : otherEmoji;
|
||||
|
||||
if (message.type === 'file') {
|
||||
let fileContent = '';
|
||||
@ -664,19 +761,29 @@ class ChatElement extends HTMLElement {
|
||||
private async reloadMemberChat(pairingProcess: string) {
|
||||
try {
|
||||
const service = await Services.getInstance();
|
||||
const myAddresses = await service.getMemberFromDevice();
|
||||
if (!myAddresses) {
|
||||
console.error('No paired member found');
|
||||
return;
|
||||
}
|
||||
|
||||
const database = await Database.getInstance();
|
||||
const db = database.db;
|
||||
|
||||
const chatHeader = this.shadowRoot?.querySelector('#chat-header');
|
||||
const messagesContainer = this.shadowRoot?.querySelector('#messages');
|
||||
|
||||
if (!messagesContainer) return;
|
||||
if (!chatHeader || !messagesContainer) return;
|
||||
|
||||
const emojis = await addressToEmoji(pairingProcess);
|
||||
|
||||
const transaction = db.transaction("labels", "readonly");
|
||||
const store = transaction.objectStore("labels");
|
||||
const request = store.get(emojis);
|
||||
|
||||
request.onsuccess = () => {
|
||||
const label = request.result;
|
||||
chatHeader.textContent = label ? `Chat with ${label.label} (${emojis})` : `Chat with member (${emojis})`;
|
||||
};
|
||||
|
||||
request.onerror = () => {
|
||||
chatHeader.textContent = `Chat with member (${emojis})`;
|
||||
};
|
||||
|
||||
messagesContainer.innerHTML = '';
|
||||
|
||||
let dmProcessId = await this.processId;
|
||||
@ -691,20 +798,14 @@ class ChatElement extends HTMLElement {
|
||||
if (dmProcess?.states) {
|
||||
for (const state of dmProcess.states) {
|
||||
const pcd_commitment = state.pcd_commitment;
|
||||
if (pcd_commitment) {
|
||||
const message_hash = pcd_commitment.message;
|
||||
if (message_hash) {
|
||||
const diff = await service.getDiffByValue(message_hash);
|
||||
const message = diff?.new_value;
|
||||
if (message === "" || message === undefined) {
|
||||
const message = await service.decryptAttribute(state, 'message');
|
||||
if (message === "" || message === undefined || message === null) {
|
||||
continue;
|
||||
}
|
||||
console.log('message', message);
|
||||
allMessages.push(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allMessages.sort((a, b) => a.metadata.createdAt - b.metadata.createdAt);
|
||||
if (allMessages.length > 0) {
|
||||
@ -713,15 +814,20 @@ class ChatElement extends HTMLElement {
|
||||
const messageElement = document.createElement('div');
|
||||
messageElement.className = 'message-container';
|
||||
|
||||
const isCurrentUser = message.metadata.sender === myAddresses[0];
|
||||
const myProcessId = await this.getMyProcessId();
|
||||
|
||||
const isCurrentUser = message.metadata.sender === myProcessId;
|
||||
messageElement.style.justifyContent = isCurrentUser ? 'flex-end' : 'flex-start';
|
||||
|
||||
const messageContent = document.createElement('div');
|
||||
messageContent.className = 'message user';
|
||||
messageContent.className = isCurrentUser ? 'message user' : 'message';
|
||||
|
||||
console.log("SENDER: ", message.metadata.sender);
|
||||
const pairingProcess = await this.getMyProcessId();
|
||||
const senderEmoji = await addressToEmoji(pairingProcess);
|
||||
|
||||
|
||||
const myEmoji = await addressToEmoji(myProcessId);
|
||||
const otherEmoji = await addressToEmoji(this.selectedMember);
|
||||
|
||||
const senderEmoji = isCurrentUser ? myEmoji : otherEmoji;
|
||||
|
||||
if (message.type === 'file') {
|
||||
let fileContent = '';
|
||||
@ -924,7 +1030,8 @@ class ChatElement extends HTMLElement {
|
||||
await this.loadAllProcesses(processSet);
|
||||
break;
|
||||
case 'members':
|
||||
this.loadAllMembers();
|
||||
await this.lookForMyDms():
|
||||
await this.loadAllMembers();
|
||||
break;
|
||||
default:
|
||||
console.error('Unknown tab type:', tabType);
|
||||
@ -1692,6 +1799,13 @@ class ChatElement extends HTMLElement {
|
||||
} else {
|
||||
console.warn('⚠️ No member selected yet. Waiting for selection...');
|
||||
}
|
||||
|
||||
window.addEventListener('process-updated', async (e: CustomEvent) => {
|
||||
const processId = e.detail.processId;
|
||||
if (processId === this.processId) {
|
||||
setTimeout(async () => await this.reloadMemberChat(this.selectedMember), 3000);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1136,6 +1136,12 @@ export default class Services {
|
||||
const existing = await this.getProcess(processId);
|
||||
if (existing) {
|
||||
console.log(`${processId} already in db`);
|
||||
|
||||
const event = new CustomEvent('process-updated', {
|
||||
detail: { processId }
|
||||
});
|
||||
window.dispatchEvent(event);
|
||||
|
||||
// We may learn an update for this process
|
||||
// TODO maybe actually check if what the relay is sending us contains more information than what we have
|
||||
// relay should always have more info than us, but we never know
|
||||
|
Loading…
x
Reference in New Issue
Block a user