diff --git a/infrastructure/control-panel/.gitignore b/infrastructure/control-panel/.gitignore index da4616b7..42250352 100644 --- a/infrastructure/control-panel/.gitignore +++ b/infrastructure/control-panel/.gitignore @@ -22,8 +22,5 @@ Thumbs.db vite.config.js.timestamp-* vite.config.ts.timestamp-* -# Paraglide -src/lib/paraglide - *storybook.log storybook-static diff --git a/infrastructure/control-panel/biome.json b/infrastructure/control-panel/biome.json new file mode 100644 index 00000000..4bc34f1f --- /dev/null +++ b/infrastructure/control-panel/biome.json @@ -0,0 +1,7 @@ +{ + "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", + "extends": ["../../biome.json"], + "organizeImports": { + "include": ["src/**/*.ts", "src/**/*.svelte"] + } +} diff --git a/infrastructure/control-panel/src/app.css b/infrastructure/control-panel/src/app.css index 2c0d12d5..403bc8e2 100644 --- a/infrastructure/control-panel/src/app.css +++ b/infrastructure/control-panel/src/app.css @@ -32,40 +32,40 @@ .small { @apply text-xs/[1.5] font-normal text-black; } -} -@theme { - /* Custom theme */ - --color-primary: #8e52ff; - --color-primary-100: #e8dcff; - --color-primary-200: #d2baff; - --color-primary-300: #bb97ff; - --color-primary-400: #a575ff; - --color-primary-500: #8e52ff; + :root { + /* Custom theme */ + --color-primary: #8e52ff; + --color-primary-100: #e8dcff; + --color-primary-200: #d2baff; + --color-primary-300: #bb97ff; + --color-primary-400: #a575ff; + --color-primary-500: #8e52ff; - --color-secondary: #73efd5; - --color-secondary-100: #e3fcf7; - --color-secondary-200: #c7f9ee; - --color-secondary-300: #abf6e6; - --color-secondary-400: #8ff2dd; - --color-secondary-500: #73efd5; + --color-secondary: #73efd5; + --color-secondary-100: #e3fcf7; + --color-secondary-200: #c7f9ee; + --color-secondary-300: #abf6e6; + --color-secondary-400: #8ff2dd; + --color-secondary-500: #73efd5; - --color-white: #ffffff; - --color-gray: #f5f5f5; + --color-white: #ffffff; + --color-gray: #f5f5f5; - --color-black: #1f1f1f; - --color-black-100: #d2d2d2; - --color-black-300: #a5a5a5; - --color-black-500: #797979; - --color-black-700: #4c4c4c; - --color-black-900: #1f1f1f; + --color-black: #1f1f1f; + --color-black-100: #d2d2d2; + --color-black-300: #a5a5a5; + --color-black-500: #797979; + --color-black-700: #4c4c4c; + --color-black-900: #1f1f1f; - --color-danger: #ff5255; - --color-danger-100: #ffdcdd; - --color-danger-200: #ffb1a7; - --color-danger-300: #ff968e; - --color-danger-400: #ff7b77; - --color-danger-500: #ff5255; + --color-danger: #ff5255; + --color-danger-100: #ffdcdd; + --color-danger-200: #ffb1a7; + --color-danger-300: #ff968e; + --color-danger-400: #ff7b77; + --color-danger-500: #ff5255; - --color-green: #0fb340; + --color-green: #0fb340; + } } diff --git a/infrastructure/control-panel/src/app.html b/infrastructure/control-panel/src/app.html index f08d5fac..1391f884 100644 --- a/infrastructure/control-panel/src/app.html +++ b/infrastructure/control-panel/src/app.html @@ -1,5 +1,5 @@ - + diff --git a/infrastructure/control-panel/src/hooks.server.ts b/infrastructure/control-panel/src/hooks.server.ts deleted file mode 100644 index ad77efcf..00000000 --- a/infrastructure/control-panel/src/hooks.server.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Handle } from '@sveltejs/kit'; -import { paraglideMiddleware } from '$lib/paraglide/server'; - -const handleParaglide: Handle = ({ event, resolve }) => - paraglideMiddleware(event.request, ({ request, locale }) => { - event.request = request; - - return resolve(event, { - transformPageChunk: ({ html }) => html.replace('%paraglide.lang%', locale) - }); - }); - -export const handle: Handle = handleParaglide; diff --git a/infrastructure/control-panel/src/hooks.ts b/infrastructure/control-panel/src/hooks.ts deleted file mode 100644 index e75600b3..00000000 --- a/infrastructure/control-panel/src/hooks.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { deLocalizeUrl } from '$lib/paraglide/runtime'; - -export const reroute = (request) => deLocalizeUrl(request.url).pathname; diff --git a/infrastructure/control-panel/src/routes/demo/+page.svelte b/infrastructure/control-panel/src/routes/demo/+page.svelte deleted file mode 100644 index a815390c..00000000 --- a/infrastructure/control-panel/src/routes/demo/+page.svelte +++ /dev/null @@ -1 +0,0 @@ -paraglide diff --git a/infrastructure/control-panel/src/routes/demo/paraglide/+page.svelte b/infrastructure/control-panel/src/routes/demo/paraglide/+page.svelte deleted file mode 100644 index 0ccba11c..00000000 --- a/infrastructure/control-panel/src/routes/demo/paraglide/+page.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - -

{m.hello_world({ name: 'SvelteKit User' })}

-
- - -
-

- If you use VSCode, install the Sherlock i18n extension for a better i18n experience. -

diff --git a/infrastructure/control-panel/vite.config.ts b/infrastructure/control-panel/vite.config.ts index 2fa21a2c..bf699a8d 100644 --- a/infrastructure/control-panel/vite.config.ts +++ b/infrastructure/control-panel/vite.config.ts @@ -1,15 +1,7 @@ -import { paraglideVitePlugin } from '@inlang/paraglide-js'; -import tailwindcss from '@tailwindcss/vite'; import { sveltekit } from '@sveltejs/kit/vite'; +import tailwindcss from '@tailwindcss/vite'; import { defineConfig } from 'vite'; export default defineConfig({ - plugins: [ - tailwindcss(), - sveltekit(), - paraglideVitePlugin({ - project: './project.inlang', - outdir: './src/lib/paraglide' - }) - ] + plugins: [tailwindcss(), sveltekit()] }); diff --git a/infrastructure/web3-adapter/src/evault/evault.ts b/infrastructure/web3-adapter/src/evault/evault.ts index 02859180..f8eed7e3 100644 --- a/infrastructure/web3-adapter/src/evault/evault.ts +++ b/infrastructure/web3-adapter/src/evault/evault.ts @@ -190,7 +190,10 @@ export class EVaultClient { private async requestPlatformToken(): Promise { try { const response = await fetch( - new URL("/platforms/certification", this.registryUrl).toString(), + new URL( + "/platforms/certification", + this.registryUrl, + ).toString(), { method: "POST", headers: { @@ -204,7 +207,7 @@ export class EVaultClient { throw new Error(`HTTP error! status: ${response.status}`); } - const data = await response.json() as PlatformTokenResponse; + const data = (await response.json()) as PlatformTokenResponse; const now = Date.now(); const expiresAt = data.expiresAt || now + 3600000; // Default 1 hour diff --git a/infrastructure/web3-adapter/src/index.ts b/infrastructure/web3-adapter/src/index.ts index 2eeed42b..6941eb11 100644 --- a/infrastructure/web3-adapter/src/index.ts +++ b/infrastructure/web3-adapter/src/index.ts @@ -1,11 +1,11 @@ import * as fs from "node:fs/promises"; import path from "node:path"; +import axios from "axios"; +import { v4 as uuidv4 } from "uuid"; import { MappingDatabase } from "./db"; import { EVaultClient } from "./evault/evault"; import { fromGlobal, toGlobal } from "./mapper/mapper"; import type { IMapping } from "./mapper/mapper.types"; -import axios from "axios"; -import { v4 as uuidv4 } from "uuid"; /** * Standalone function to spin up an eVault @@ -17,14 +17,14 @@ import { v4 as uuidv4 } from "uuid"; export async function spinUpEVault( registryUrl: string, provisionerUrl: string, - verificationCode?: string + verificationCode?: string, ): Promise<{ w3id: string; uri: string }> { const DEMO_CODE_W3DS = "d66b7138-538a-465f-a6ce-f6985854c3f4"; const finalVerificationCode = verificationCode || DEMO_CODE_W3DS; try { const entropyResponse = await axios.get( - new URL("/entropy", registryUrl).toString() + new URL("/entropy", registryUrl).toString(), ); const registryEntropy = entropyResponse.data.token; @@ -36,11 +36,13 @@ export async function spinUpEVault( registryEntropy, namespace, verificationId: finalVerificationCode, - } + }, ); if (!provisionResponse.data.success) { - throw new Error(`Failed to provision eVault: ${provisionResponse.data.message || 'Unknown error'}`); + throw new Error( + `Failed to provision eVault: ${provisionResponse.data.message || "Unknown error"}`, + ); } return { @@ -49,9 +51,13 @@ export async function spinUpEVault( }; } catch (error) { if (axios.isAxiosError(error)) { - throw new Error(`Failed to spin up eVault: ${error.response?.data?.message || error.message}`); + throw new Error( + `Failed to spin up eVault: ${error.response?.data?.message || error.message}`, + ); } - throw new Error(`Failed to spin up eVault: ${error instanceof Error ? error.message : 'Unknown error'}`); + throw new Error( + `Failed to spin up eVault: ${error instanceof Error ? error.message : "Unknown error"}`, + ); } } @@ -91,32 +97,40 @@ export async function createGroupEVault( owner: string; charter?: string; }, - verificationCode?: string + verificationCode?: string, ): Promise<{ w3id: string; uri: string; manifestId: string }> { const DEMO_CODE_W3DS = "d66b7138-538a-465f-a6ce-f6985854c3f4"; const finalVerificationCode = verificationCode || DEMO_CODE_W3DS; try { // Step 1: Spin up the eVault - const evault = await spinUpEVault(registryUrl, provisionerUrl, finalVerificationCode); - + const evault = await spinUpEVault( + registryUrl, + provisionerUrl, + finalVerificationCode, + ); + // Step 2: Create GroupManifest with exponential backoff const manifestId = await createGroupManifestWithRetry( registryUrl, evault.w3id, - groupData + groupData, ); return { w3id: evault.w3id, uri: evault.uri, - manifestId + manifestId, }; } catch (error) { if (axios.isAxiosError(error)) { - throw new Error(`Failed to create group eVault: ${error.response?.data?.message || error.message}`); + throw new Error( + `Failed to create group eVault: ${error.response?.data?.message || error.message}`, + ); } - throw new Error(`Failed to create group eVault: ${error instanceof Error ? error.message : 'Unknown error'}`); + throw new Error( + `Failed to create group eVault: ${error instanceof Error ? error.message : "Unknown error"}`, + ); } } @@ -135,10 +149,10 @@ async function createGroupManifestWithRetry( owner: string; charter?: string; }, - maxRetries = 10 + maxRetries = 10, ): Promise { const now = new Date().toISOString(); - + const groupManifest: GroupManifest = { eName: w3id, name: groupData.name, @@ -154,13 +168,15 @@ async function createGroupManifestWithRetry( for (let attempt = 1; attempt <= maxRetries; attempt++) { try { - console.log(`Attempting to create GroupManifest in eVault (attempt ${attempt}/${maxRetries})`); - + console.log( + `Attempting to create GroupManifest in eVault (attempt ${attempt}/${maxRetries})`, + ); + const response = await axios.get( - new URL(`resolve?w3id=${w3id}`, registryUrl).toString() + new URL(`resolve?w3id=${w3id}`, registryUrl).toString(), ); const endpoint = new URL("/graphql", response.data.uri).toString(); - + const { GraphQLClient } = await import("graphql-request"); const client = new GraphQLClient(endpoint); @@ -181,28 +197,38 @@ async function createGroupManifestWithRetry( metaEnvelope: { id: string; ontology: string; - parsed: any; + parsed: unknown; }; }; } - const result = await client.request(STORE_META_ENVELOPE, { - input: { - ontology: "550e8400-e29b-41d4-a716-446655440001", // GroupManifest schema ID - payload: groupManifest, - acl: ["*"], + const result = await client.request( + STORE_META_ENVELOPE, + { + input: { + ontology: "550e8400-e29b-41d4-a716-446655440001", // GroupManifest schema ID + payload: groupManifest, + acl: ["*"], + }, }, - }); + ); const manifestId = result.storeMetaEnvelope.metaEnvelope.id; - console.log("GroupManifest created successfully in eVault:", manifestId); + console.log( + "GroupManifest created successfully in eVault:", + manifestId, + ); return manifestId; - } catch (error) { - console.error(`Failed to create GroupManifest in eVault (attempt ${attempt}/${maxRetries}):`, error); + console.error( + `Failed to create GroupManifest in eVault (attempt ${attempt}/${maxRetries}):`, + error, + ); if (attempt === maxRetries) { - console.error("Max retries reached, giving up on GroupManifest creation"); + console.error( + "Max retries reached, giving up on GroupManifest creation", + ); throw error; } @@ -229,7 +255,7 @@ export class Web3Adapter { dbPath: string; registryUrl: string; platform: string; - provisionerUrl?: string; + provisionerUrl?: string; }, ) { this.readPaths(); @@ -277,8 +303,8 @@ export class Web3Adapter { data.id as string, ); - if (!this.mapping[tableName]) return - console.log("We get here?") + if (!this.mapping[tableName]) return; + console.log("We get here?"); // If we already have a mapping, use that global ID if (existingGlobalId) { if (this.lockedIds.includes(existingGlobalId)) return; @@ -372,15 +398,22 @@ export class Web3Adapter { */ async spinUpEVault( verificationCode?: string, - provisionerUrl?: string + provisionerUrl?: string, ): Promise<{ w3id: string; uri: string }> { - const finalProvisionerUrl = provisionerUrl || this.config.provisionerUrl; - + const finalProvisionerUrl = + provisionerUrl || this.config.provisionerUrl; + if (!finalProvisionerUrl) { - throw new Error("Provisioner URL is required. Please provide it in config or as parameter."); + throw new Error( + "Provisioner URL is required. Please provide it in config or as parameter.", + ); } - return spinUpEVault(this.config.registryUrl, finalProvisionerUrl, verificationCode); + return spinUpEVault( + this.config.registryUrl, + finalProvisionerUrl, + verificationCode, + ); } /** @@ -401,14 +434,22 @@ export class Web3Adapter { charter?: string; }, verificationCode?: string, - provisionerUrl?: string + provisionerUrl?: string, ): Promise<{ w3id: string; uri: string; manifestId: string }> { - const finalProvisionerUrl = provisionerUrl || this.config.provisionerUrl; - + const finalProvisionerUrl = + provisionerUrl || this.config.provisionerUrl; + if (!finalProvisionerUrl) { - throw new Error("Provisioner URL is required. Please provide it in config or as parameter."); + throw new Error( + "Provisioner URL is required. Please provide it in config or as parameter.", + ); } - return createGroupEVault(this.config.registryUrl, finalProvisionerUrl, groupData, verificationCode); + return createGroupEVault( + this.config.registryUrl, + finalProvisionerUrl, + groupData, + verificationCode, + ); } } diff --git a/package.json b/package.json index 967dfabb..7d78ed1c 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,18 @@ }, "pnpm": { "onlyBuiltDependencies": [ - "esbuild" + "@firebase/util", + "@tailwindcss/oxide", + "bufferutil", + "core-js", + "cpu-features", + "esbuild", + "msw", + "protobufjs", + "sharp", + "ssh2", + "svelte-preprocess", + "unrs-resolver" ] } } \ No newline at end of file diff --git a/platforms/blabsy/package.json b/platforms/blabsy/package.json index 7db34c56..c8f64ef8 100644 --- a/platforms/blabsy/package.json +++ b/platforms/blabsy/package.json @@ -8,7 +8,7 @@ "dev:emulators": "concurrently npm:dev npm:emulators", "build": "next build", "start": "next start", - "format": "prettier --check .", + "format": "prettier --write .", "lint": "next lint", "test": "jest --watch", "test:ci": "jest --ci" diff --git a/platforms/blabsy/src/components/chat/add-members.tsx b/platforms/blabsy/src/components/chat/add-members.tsx index dcfaa384..32b76965 100644 --- a/platforms/blabsy/src/components/chat/add-members.tsx +++ b/platforms/blabsy/src/components/chat/add-members.tsx @@ -8,7 +8,16 @@ import { XMarkIcon } from '@heroicons/react/24/outline'; import Image from 'next/image'; -import { doc, getDoc, Timestamp, collection, getDocs, query, where, limit } from 'firebase/firestore'; +import { + doc, + getDoc, + Timestamp, + collection, + getDocs, + query, + where, + limit +} from 'firebase/firestore'; import { db } from '@lib/firebase/app'; import type { User } from '@lib/types/user'; import { Loading } from '@components/ui/loading'; @@ -48,7 +57,9 @@ export function AddMembers({ const [searchResults, setSearchResults] = useState([]); const [isSearching, setIsSearching] = useState(false); const [allUsersData, setAllUsersData] = useState([]); - const [participantData, setParticipantData] = useState>({}); + const [participantData, setParticipantData] = useState< + Record + >({}); const [isAddingMembers, setIsAddingMembers] = useState(false); const otherParticipant = currentChat?.participants.find( @@ -117,7 +128,7 @@ export function AddMembers({ const fetchParticipantData = async (): Promise => { try { const newParticipantData: Record = {}; - + for (const participantId of currentChat.participants) { if (participantId === user?.id) { // Use current user data @@ -126,13 +137,16 @@ export function AddMembers({ } } else { // Fetch other participants' data - const userDoc = await getDoc(doc(db, 'users', participantId)); + const userDoc = await getDoc( + doc(db, 'users', participantId) + ); if (userDoc.exists()) { - newParticipantData[participantId] = userDoc.data() as User; + newParticipantData[participantId] = + userDoc.data() as User; } } } - + setParticipantData(newParticipantData); } catch (error) { console.error('Error fetching participants data:', error); @@ -154,22 +168,31 @@ export function AddMembers({ // Query Firestore for users - using a simpler approach const usersRef = collection(db, 'users'); const usersSnapshot = await getDocs(usersRef); - + // Filter users based on search query - const allUsers = usersSnapshot.docs.map(doc => { + const allUsers = usersSnapshot.docs.map((doc) => { const userData = doc.data() as User; return { ...userData, id: doc.id }; }); // Filter by search query and exclude current user, already selected users, and existing chat participants const availableUsers = allUsers - .filter(userData => - userData.id !== user?.id && // Exclude current user - !selectedUsers.some(selected => selected.id === userData.id) && // Exclude already selected - !currentChat?.participants.includes(userData.id) && // Exclude existing chat participants - (userData.name?.toLowerCase().includes(query.toLowerCase()) || - userData.username?.toLowerCase().includes(query.toLowerCase()) || - userData.bio?.toLowerCase().includes(query.toLowerCase())) + .filter( + (userData) => + userData.id !== user?.id && // Exclude current user + !selectedUsers.some( + (selected) => selected.id === userData.id + ) && // Exclude already selected + !currentChat?.participants.includes(userData.id) && // Exclude existing chat participants + (userData.name + ?.toLowerCase() + .includes(query.toLowerCase()) || + userData.username + ?.toLowerCase() + .includes(query.toLowerCase()) || + userData.bio + ?.toLowerCase() + .includes(query.toLowerCase())) ) .slice(0, 5); @@ -192,10 +215,7 @@ export function AddMembers({ }, [searchQuery, selectedUsers]); // Show selected users first, then search results - const displayUsers = [ - ...selectedUsers, - ...searchResults - ]; + const displayUsers = [...selectedUsers, ...searchResults]; const toggleUserSelection = (user: User) => { if (selectedUsers.some((u) => u.id === user.id)) { @@ -207,7 +227,7 @@ export function AddMembers({ const handleAddSelectedMembers = async () => { if (!currentChat && !newChat) return; - + if (newChat) { if (selectedUsers.length === 0) { return; // Just return without alert @@ -215,16 +235,19 @@ export function AddMembers({ try { setIsAddingMembers(true); - console.log('Creating chat with selected users:', selectedUsers); - + console.log( + 'Creating chat with selected users:', + selectedUsers + ); + // Determine if this should be a direct or group chat const isGroupChat = selectedUsers.length > 1; const chatType = isGroupChat ? 'group' : 'direct'; - + // Get all participant IDs (including current user) const participantIds = [ user.id, - ...selectedUsers.map(u => u.id) + ...selectedUsers.map((u) => u.id) ]; console.log('Participant IDs:', participantIds); @@ -233,9 +256,11 @@ export function AddMembers({ let chatName: string | undefined; if (isGroupChat) { // For group chats, use the custom name or create from selected users - chatName = groupName.trim() || selectedUsers - .map(u => u.name || u.username) - .join(', '); + chatName = + groupName.trim() || + selectedUsers + .map((u) => u.name || u.username) + .join(', '); } console.log('Chat name:', chatName); @@ -266,7 +291,7 @@ export function AddMembers({ setGroupName(''); setSearchQuery(''); setSearchResults([]); - + // Ensure modal closes with a small delay to allow UI updates setTimeout(() => { onClose(); @@ -274,7 +299,10 @@ export function AddMembers({ } catch (error) { console.error('Error creating chat:', error); console.error('Error details:', { - message: error instanceof Error ? error.message : 'Unknown error', + message: + error instanceof Error + ? error.message + : 'Unknown error', stack: error instanceof Error ? error.stack : undefined }); // Don't show alert, just log the error @@ -285,35 +313,35 @@ export function AddMembers({ // Add members to existing chat try { setIsAddingMembers(true); - + // Add all selected users for (const selectedUser of selectedUsers) { await addParticipant(selectedUser.id); } - + // Immediately update the current chat participants in the UI if (currentChat) { const updatedParticipants = [ ...currentChat.participants, - ...selectedUsers.map(u => u.id) + ...selectedUsers.map((u) => u.id) ]; - + // Update the current chat object immediately for UI feedback const updatedChat = { ...currentChat, participants: updatedParticipants }; - + // Force a re-render by updating the chat context setCurrentChat(updatedChat); } - + // Close modal and reset state setSelectedUsers([]); setGroupName(''); setSearchQuery(''); setSearchResults([]); - + // Ensure modal closes with a small delay to allow UI updates setTimeout(() => { onClose(); @@ -363,44 +391,64 @@ export function AddMembers({ {!newChat && currentChat && (

- Current Members ({currentChat.participants.length}) + Current Members ( + {currentChat.participants.length})

- {currentChat.participants.map((participantId) => { - const participant = participantData[participantId]; - return ( -
-
- {participant?.photoURL ? ( - {participant.name - ) : ( - - )} + {currentChat.participants.map( + (participantId) => { + const participant = + participantData[participantId]; + return ( +
+
+ {participant?.photoURL ? ( + { + ) : ( + + )} +
+
+ + {participant?.name || + participant?.username || + 'Unknown User'} + + + {participant?.username || + participantId} + +
-
- - {participant?.name || participant?.username || 'Unknown User'} - - - {participant?.username || participantId} - -
-
- ); - })} + ); + } + )}
)}
setSearchQuery(e.target.value)} className='w-full px-3 py-2 border border-gray-300 rounded-md dark:bg-gray-800 dark:text-white dark:border-gray-700' @@ -412,27 +460,42 @@ export function AddMembers({ Searching...

)} - {!isSearching && displayUsers.length === 0 && searchQuery && ( -

- No users found. -

- )} - {!isSearching && displayUsers.length === 0 && !searchQuery && ( -

- Start typing to search for users. -

- )} + {!isSearching && + displayUsers.length === 0 && + searchQuery && ( +

+ No users found. +

+ )} + {!isSearching && + displayUsers.length === 0 && + !searchQuery && ( +

+ Start typing to search for users. +

+ )} {displayUsers.map((userItem) => { - const isSelected = selectedUsers.some(u => u.id === userItem.id); - const isSelectedUser = selectedUsers.some(u => u.id === userItem.id); - const isExistingMember = currentChat?.participants.includes(userItem.id); - + const isSelected = selectedUsers.some( + (u) => u.id === userItem.id + ); + const isSelectedUser = selectedUsers.some( + (u) => u.id === userItem.id + ); + const isExistingMember = + currentChat?.participants.includes(userItem.id); + return (