docv/lib/contexts/FourNKContext.tsx

266 lines
9.7 KiB
TypeScript

"use client"
import { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react';
import MessageBus from "@/lib/4nk/MessageBus";
import { iframeUrl } from "@/app/page";
import UserStore from "@/lib/4nk/UserStore";
import { FolderChatData, FolderData } from "@/lib/4nk/models/FolderData";
// Interface enrichie qui inclut maintenant les membres ET les fichiers
export interface EnrichedFolderData extends FolderData {
processId: string,
}
// ---
type FourNKContextType = {
isConnected: boolean;
userPairingId: string | null;
processes: any;
myProcesses: string[];
folderProcesses: any;
myFolderProcesses: string[];
folderPrivateData: Record<string, Record<string, any>>;
folders: EnrichedFolderData[];
loadingFolders: boolean;
members: string[];
setFolderProcesses: React.Dispatch<React.SetStateAction<any>>;
setMyFolderProcesses: React.Dispatch<React.SetStateAction<string[]>>;
setFolderPrivateData: React.Dispatch<React.SetStateAction<Record<string, Record<string, any>>>>;
setMembers: React.Dispatch<React.SetStateAction<string[]>>;
};
const FourNKContext = createContext<FourNKContextType | undefined>(undefined);
export function FourNKProvider({ children }: { children: ReactNode }) {
const [isConnected, setIsConnected] = useState(false);
const [userPairingId, setUserPairingId] = useState<string | null>(null);
const [processes, setProcesses] = useState<any>(null);
const [members, setMembers] = useState<string[]>([]);;
const [myProcesses, setMyProcesses] = useState<string[]>([]);
const [folderProcesses, setFolderProcesses] = useState<any>(null);
const [myFolderProcesses, setMyFolderProcesses] = useState<string[]>([]);
const [folderPrivateData, setFolderPrivateData] = useState<Record<string, Record<string, any>>>({});
const [loadingFolders, setLoadingFolders] = useState(true);
const [folders, setFolders] = useState<EnrichedFolderData[]>([]);
const fetchFolderPrivateData = useCallback(async (processId: string, stateId: string) => {
try {
const messageBus = MessageBus.getInstance(iframeUrl);
await messageBus.isReady();
const data = await messageBus.getData(processId, stateId);
setFolderPrivateData(prev => ({ ...prev, [stateId]: data }));
return data;
} catch (err) {
console.error('Error fetching folder private data:', err);
return null;
}
}, []);
const loadFoldersFrom4NK = useCallback(() => {
if (!folderProcesses || !myFolderProcesses) {
return;
}
const folderData: EnrichedFolderData[] = [];
let hasAllPrivateData = true;
let hasFoldersToLoad = false;
const missingPrivateData: Array<{ processId: string, stateId: string }> = [];
const EXCLUDED_STATE_ID = "0000000000000000000000000000000000000000000000000000000000000000";
Object.entries(folderProcesses).forEach(([processId, process]: [string, any]) => {
if (!myFolderProcesses.includes(processId)) return;
const validStates = process.states.filter(
(state: any) => state && state.state_id !== EXCLUDED_STATE_ID
);
if (validStates.length === 0) return;
const referenceState = validStates.find(
(state: any) => state.pcd_commitment?.folderNumber
);
const mainFolderNumber = referenceState?.pcd_commitment.folderNumber;
if (!mainFolderNumber) {
return;
}
let basePrivateData: any;
let mergedMessages: FolderChatData[] = [];
let mergedMessagesOwner: FolderChatData[] = [];
validStates.forEach((state: any) => {
hasFoldersToLoad = true;
const stateToProcess = state;
const privateData = folderPrivateData[stateToProcess.state_id];
// Si on n'a pas les données pour cet état de référence...
if (!privateData) {
hasAllPrivateData = false;
missingPrivateData.push({ processId, stateId: stateToProcess.state_id });
return; // On quitte ce process, on réessaiera au prochain rendu
}
// console.log("Données déchiffrées pour le state:", stateToProcess.state_id, privateData);
if (privateData.messages_owner instanceof Map) {
const messageOwnerObj = Object.fromEntries(privateData.messages_owner);
mergedMessagesOwner.push(messageOwnerObj);
}
if (privateData.messages instanceof Map) {
const messageObj = Object.fromEntries(privateData.messages);
mergedMessages.push(messageObj);
}
if (privateData.folderNumber) {
basePrivateData = privateData;
}
});
if (!basePrivateData) return;
folderData.push({
processId: processId,
folderNumber: mainFolderNumber,
name: basePrivateData.name || `Dossier ${mainFolderNumber}`,
description: basePrivateData.description || '',
created_at: basePrivateData.created_at || new Date().toISOString(),
updated_at: basePrivateData.updated_at || new Date().toISOString(),
notes: basePrivateData.notes || [],
messages: mergedMessages || [],
messages_owner: mergedMessagesOwner || [],
attachedFiles: basePrivateData.attachedFiles || [],
});
});
if (hasFoldersToLoad && !hasAllPrivateData) {
setLoadingFolders(true);
const firstMissing = missingPrivateData[0];
if (firstMissing) {
if (!folderPrivateData[firstMissing.stateId]) {
fetchFolderPrivateData(firstMissing.processId, firstMissing.stateId);
}
}
} else {
setFolders(folderData);
setLoadingFolders(false);
}
}, [folderProcesses, myFolderProcesses, folderPrivateData, fetchFolderPrivateData, setFolders, setLoadingFolders]);
const loadMembersFrom4NK = useCallback(() => {
if (!processes || !userPairingId) return;
const memberList: string[] = [];
const EXCLUDED_STATE_ID = "0000000000000000000000000000000000000000000000000000000000000000";
Object.entries(processes).forEach(([processId, process]: [string, any]) => {
const validStates = process.states.filter(
(state: any) => state && state.state_id !== EXCLUDED_STATE_ID
);
if (validStates.length === 0) return;
const referenceState = validStates.find(
(state: any) => state.pcd_commitment?.pairedAddresses
);
if (!referenceState) return;
const userAddress = referenceState.commited_in
memberList.push(userAddress);
});
// Filtrer la liste pour enlever l'ID de l'utilisateur connecté
const filteredMemberList = memberList.filter(
member => member !== userPairingId
);
// Sauvegarder la liste filtrée dans l'état
setMembers(filteredMemberList);
}, [processes, userPairingId]);
// Chargement initial des données 4NK
useEffect(() => {
const userStore = UserStore.getInstance();
const connected = userStore.isConnected();
const pairingId = userStore.getUserPairingId();
setIsConnected(connected);
setUserPairingId(pairingId);
const handleConnectionFlow = async () => {
if (!connected) {
setLoadingFolders(false);
return;
}
setLoadingFolders(true);
try {
const messageBus = MessageBus.getInstance(iframeUrl);
await messageBus.isReady();
let pid = pairingId;
if (!pid) {
pid = await messageBus.createUserPairing();
if (pid) {
userStore.pair(pid);
setUserPairingId(pid);
}
}
const procs = await messageBus.getProcesses();
const myProcs = await messageBus.getMyProcesses();
setProcesses(procs);
setFolderProcesses(procs);
setMyProcesses(myProcs);
setMyFolderProcesses(myProcs);
} catch (err) {
console.error("❌ Error during global connection flow:", err);
setLoadingFolders(false);
}
};
handleConnectionFlow();
}, [isConnected]);
// Re-calculer les dossiers lorsque les données changent
useEffect(() => {
loadFoldersFrom4NK();
}, [loadFoldersFrom4NK]);
// Re-calculer les membres lorsque les données changent
useEffect(() => {
loadMembersFrom4NK();
}, [loadMembersFrom4NK]);
const value = {
isConnected,
userPairingId,
processes,
myProcesses,
folderProcesses,
myFolderProcesses,
folderPrivateData,
folders,
loadingFolders,
members,
setFolderProcesses,
setMyFolderProcesses,
setFolderPrivateData,
setMembers,
};
return (
<FourNKContext.Provider value={value}>
{children}
</FourNKContext.Provider>
);
}
export function use4NK() {
const context = useContext(FourNKContext);
if (context === undefined) {
throw new Error('use4NK must be used within a FourNKProvider');
}
return context;
}