diff --git a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte index 68df7499..5cc9a707 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte @@ -1,483 +1,527 @@ @@ -624,7 +668,7 @@ onDestroy(async () => { @@ -646,95 +690,119 @@ onDestroy(async () => { /> -

Sign Vote Request

+

+ {signingData?.pollId ? "Sign Vote Request" : "Sign Message Request"} +

- You're being asked to sign a vote for the following poll + {signingData?.pollId + ? "You're being asked to sign a vote for the following poll" + : "You're being asked to sign the following message"}

-
-

Poll ID

-

- {signingData?.pollId ?? "Unknown"} -

-
+ {#if signingData?.pollId && signingData?.voteData} + +
+

Poll ID

+

+ {signingData?.pollId ?? "Unknown"} +

+
-
-

Your Vote

-
- {#if signingData?.voteData?.optionId !== undefined} - -

- You selected: Option {parseInt(signingData.voteData.optionId) + - 1} -

-

- (This is the option number from the poll) -

- {:else if signingData?.voteData?.ranks} - -

Your ranking order:

-
- {#each Object.entries(signingData.voteData.ranks).sort(([a], [b]) => parseInt(a) - parseInt(b)) as [rank, optionIndex]} -
+

Your Vote

+
+ {#if signingData?.voteData?.optionId !== undefined} + +

+ You selected: Option {parseInt(signingData.voteData.optionId) + + 1} - +

+ (This is the option number from the poll) +

+ {:else if signingData?.voteData?.ranks} + +

Your ranking order:

+
+ {#each Object.entries(signingData.voteData.ranks).sort(([a], [b]) => parseInt(a) - parseInt(b)) as [rank, optionIndex]} +
- {rank === "1" - ? "1st" - : rank === "2" - ? "2nd" - : rank === "3" - ? "3rd" - : `${rank}th`} - - Option {parseInt(String(optionIndex)) + - 1} + {rank === "1" + ? "1st" + : rank === "2" + ? "2nd" + : rank === "3" + ? "3rd" + : `${rank}th`} + + Option {parseInt(String(optionIndex)) + + 1} +
+ {/each} +
+

+ (1st = most preferred, 2nd = second choice, etc.) +

+ {:else if signingData?.voteData?.points} + +

Your point distribution:

+
+ {#each Object.entries(signingData.voteData.points) + .filter(([_, points]) => (points as number) > 0) + .sort(([a], [b]) => parseInt(a) - parseInt(b)) as [optionIndex, points]} +
-
- {/each} -
-

- (1st = most preferred, 2nd = second choice, etc.) -

- {:else if signingData?.voteData?.points} - -

Your point distribution:

-
- {#each Object.entries(signingData.voteData.points) - .filter(([_, points]) => (points as number) > 0) - .sort(([a], [b]) => parseInt(a) - parseInt(b)) as [optionIndex, points]} -
- - {points} pts - - Option {parseInt(String(optionIndex)) + - 1} -
- {/each} -
-

- (Total: {Object.values(signingData.voteData.points).reduce( - (sum, points) => - (sum as number) + ((points as number) || 0), - 0, - )}/100 points) -

- {:else} -

Vote data not available

- {/if} + + {points} pts + + Option {parseInt(String(optionIndex)) + + 1} +
+ {/each} +
+

+ (Total: {Object.values( + signingData.voteData.points, + ).reduce( + (sum, points) => + (sum as number) + ((points as number) || 0), + 0, + )}/100 points) +

+ {:else} +

Vote data not available

+ {/if} +
+
+ {:else} + +
+

Message

+

+ {signingData?.message ?? "No message provided"} +

-
+ +
+

Session ID

+

+ {signingData?.sessionId?.slice(0, 8) ?? "Unknown"}... +

+
+ {/if}
{ Decline - {loading ? "Signing..." : "Sign Vote"} + {loading + ? "Signing..." + : signingData?.pollId + ? "Sign Vote" + : "Sign Message"}
@@ -775,18 +847,26 @@ onDestroy(async () => { />

- Vote Signed Successfully! + {signingData?.pollId + ? "Vote Signed Successfully!" + : "Message Signed Successfully!"}

-

You can return to the platform

+

+ {signingData?.pollId + ? "Your vote has been signed and submitted to the voting system." + : "Your message has been signed and submitted successfully."} +

- {#if signingData?.redirect_uri} + {#if redirect}
{ try { - window.location.href = signingData.redirect_uri; + if (redirect) { + window.location.href = redirect; + } } catch (error) { console.error("Manual redirect failed:", error); } diff --git a/infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte index d8a5cf0a..54406c8a 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte @@ -126,7 +126,7 @@ on:click={handleVersionTap} disabled={isRetrying} > - Version v0.1.8.1 + Version v0.2.0.1 {#if retryMessage} diff --git a/infrastructure/eid-wallet/src/routes/(app)/sign/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/sign/+page.svelte index df961eb9..f3fc761b 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/sign/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/sign/+page.svelte @@ -1,148 +1,171 @@ @@ -162,54 +185,80 @@ function handleRetry() {

- Sign Your Vote + {decodedData.pollId ? "Sign Your Vote" : "Sign Message"}

- You're about to sign a vote for the following poll: + {decodedData.pollId + ? "You're about to sign a vote for the following poll:" + : "You're about to sign the following message:"}

- -
-

- Poll Details -

-
-
- Poll ID: - {decodedData.pollId?.slice(0, 8)}... -
-
- Voting Mode: - {decodedData.voteData?.optionId - ? "Single Choice" - : "Ranked Choice"} -
- {#if decodedData.voteData?.optionId} + {#if decodedData.pollId && decodedData.voteData} + +
+

+ Poll Details +

+
- Selected Option: - Option {decodedData.voteData.optionId + 1} + Poll ID: + {decodedData.pollId?.slice(0, 8)}...
- {:else if decodedData.voteData?.ranks}
- Rankings: -
- {#each Object.entries(decodedData.voteData.ranks) as [rank, optionIndex]} -
- {rank === "1" - ? "1st" - : rank === "2" - ? "2nd" - : "3rd"}: Option {(optionIndex as number) + - 1} -
- {/each} + Voting Mode: + {decodedData.voteData?.optionId + ? "Single Choice" + : "Ranked Choice"} +
+ {#if decodedData.voteData?.optionId} +
+ Selected Option: + Option {decodedData.voteData.optionId + 1} +
+ {:else if decodedData.voteData?.ranks} +
+ Rankings: +
+ {#each Object.entries(decodedData.voteData.ranks) as [rank, optionIndex]} +
+ {rank === "1" + ? "1st" + : rank === "2" + ? "2nd" + : "3rd"}: Option {(optionIndex as number) + + 1} +
+ {/each} +
+ {/if} +
+
+ {:else} + +
+

+ Message Details +

+
+
+ Message: +

+ {decodedData.message} +

- {/if} +
+ Session ID: +

+ {decodedData.sessionId?.slice(0, 8)}... +

+
+
-
+ {/if}
@@ -218,8 +267,9 @@ function handleRetry() {

Security Notice

- By signing this message, you're confirming your - vote. This action cannot be undone. + {decodedData.pollId + ? "By signing this message, you're confirming your vote. This action cannot be undone." + : "By signing this message, you're confirming your agreement to the content. This action cannot be undone."}

@@ -239,7 +289,7 @@ function handleRetry() { callback={handleSign} class="flex-1" > - Sign Vote + {decodedData.pollId ? "Sign Vote" : "Sign Message"}
@@ -254,7 +304,9 @@ function handleRetry() {

- Signing Your Vote + {decodedData?.pollId + ? "Signing Your Vote" + : "Signing Your Message"}

Please wait while we process your signature... @@ -283,11 +335,14 @@ function handleRetry() {

- Vote Signed Successfully! + {decodedData?.pollId + ? "Vote Signed Successfully!" + : "Message Signed Successfully!"}

- Your vote has been signed and submitted to the voting - system. + {decodedData?.pollId + ? "Your vote has been signed and submitted to the voting system." + : "Your message has been signed and submitted successfully."}

diff --git a/infrastructure/web3-adapter/MAPPING_RULES.md b/infrastructure/web3-adapter/MAPPING_RULES.md new file mode 100644 index 00000000..64dd3913 --- /dev/null +++ b/infrastructure/web3-adapter/MAPPING_RULES.md @@ -0,0 +1,178 @@ +# Web3-Adapter Mapping Rules + +This document explains how to create mappings for the web3-adapter system, which enables data exchange between different platforms using a universal ontology. + +## Basic Structure + +A mapping file defines how local database fields map to global ontology fields. The structure is: + +```json +{ + "tableName": "local_table_name", + "schemaId": "global_schema_uuid", + "ownerEnamePath": "path_to_owner_ename", + "ownedJunctionTables": ["junction_table1", "junction_table2"], + "localToUniversalMap": { + "localField": "globalField", + "localRelation": "tableName(relationPath),globalAlias" + } +} +``` + +## Field Mapping + +### Direct Field Mapping + +```json +"localField": "globalField" +``` + +Maps a local field directly to a global field with the same name. + +### Relation Mapping + +```json +"localRelation": "tableName(relationPath),globalAlias" +``` + +Maps a local relation to a global field, where: + +- `tableName` is the referenced table name +- `relationPath` is the path to the relation data +- `globalAlias` is the target global field name + +### Array Relation Mapping + +```json +"participants": "users(participants[].id),participantIds" +``` + +Maps an array of relations: + +- `participants[].id` extracts the `id` field from each item in the `participants` array +- `users()` resolves each ID to a global user reference +- `participantIds` is the target global field name + +## Special Functions + +### Date Conversion (`__date`) + +Converts various timestamp formats to ISO string format. + +```json +"createdAt": "__date(createdAt)" +"timestamp": "__date(calc(timestamp * 1000))" +``` + +**Supported input formats:** + +- Unix timestamp (number) +- Firebase v8 timestamp (`{_seconds: number}`) +- Firebase v9+ timestamp (`{seconds: number}`) +- Firebase Timestamp objects +- Date objects +- UTC strings + +### Calculation (`__calc`) + +Performs mathematical calculations using field values. + +```json +"total": "__calc(quantity * price)" +"average": "__calc((score1 + score2 + score3) / 3)" +``` + +**Features:** + +- Supports basic arithmetic operations (+, -, \*, /, etc.) +- Can reference other fields in the same entity +- Automatically resolves field values before calculation + +## Owner Path + +The `ownerEnamePath` defines how to determine which eVault owns the data: + +```json +"ownerEnamePath": "ename" // Direct field +"ownerEnamePath": "users(createdBy.ename)" // Nested via relation +"ownerEnamePath": "users(participants[].ename)" // Array relation +``` + +## Junction Tables + +Junction tables (many-to-many relationships) can be marked as owned: + +```json +"ownedJunctionTables": [ + "user_followers", + "user_following" +] +``` + +When junction table data changes, it triggers updates to the parent entity. + +## Examples + +### User Mapping + +```json +{ + "tableName": "users", + "schemaId": "550e8400-e29b-41d4-a716-446655440000", + "ownerEnamePath": "ename", + "ownedJunctionTables": ["user_followers", "user_following"], + "localToUniversalMap": { + "handle": "username", + "name": "displayName", + "description": "bio", + "avatarUrl": "avatarUrl", + "ename": "ename", + "followers": "followers", + "following": "following" + } +} +``` + +### Group with Relations + +```json +{ + "tableName": "groups", + "schemaId": "550e8400-e29b-41d4-a716-446655440003", + "ownerEnamePath": "users(participants[].ename)", + "localToUniversalMap": { + "name": "name", + "description": "description", + "owner": "owner", + "admins": "users(admins),admins", + "participants": "users(participants[].id),participantIds", + "createdAt": "__date(createdAt)", + "updatedAt": "__date(updatedAt)" + } +} +``` + +## Best Practices + +1. **Use descriptive global field names** that match the ontology schema +2. **Handle timestamps consistently** using `__date()` function +3. **Map relations properly** using the `tableName(relationPath)` syntax +4. **Use aliases** when the global field name differs from the local field +5. **Test mappings** with sample data to ensure proper conversion +6. **Document complex mappings** with comments explaining the logic + +## Troubleshooting + +### Common Issues + +1. **Missing relations**: Ensure the referenced table has a mapping +2. **Invalid paths**: Check that the relation path matches your entity structure +3. **Type mismatches**: Use `__date()` for timestamps, `__calc()` for calculations +4. **Circular references**: Avoid mapping entities that reference each other infinitely + +### Debug Tips + +- Check the console for mapping errors +- Verify that all referenced tables have mappings +- Test with simple data first, then add complexity +- Use the `__calc()` function to debug field values diff --git a/platforms/blabsy/.env.production b/platforms/blabsy/.env.production deleted file mode 100644 index ca10eb4d..00000000 --- a/platforms/blabsy/.env.production +++ /dev/null @@ -1,2 +0,0 @@ -# Preview URL -NEXT_PUBLIC_URL=https://$NEXT_PUBLIC_VERCEL_URL diff --git a/platforms/blabsy/.firebaserc b/platforms/blabsy/.firebaserc index d4339af8..54912f1d 100644 --- a/platforms/blabsy/.firebaserc +++ b/platforms/blabsy/.firebaserc @@ -1,5 +1,5 @@ { "projects": { - "default": "twitter-clone-ccrsxx" + "default": "w3ds-staging" } } diff --git a/platforms/blabsy/firestore.rules b/platforms/blabsy/firestore.rules index 8dfe4997..23ca4eef 100644 --- a/platforms/blabsy/firestore.rules +++ b/platforms/blabsy/firestore.rules @@ -16,12 +16,6 @@ service cloud.firestore { function isValidImages(images) { return (images is list && images.size() <= 4) || images == null; } - - function isChatParticipant(chatId) { - return request.auth != null && - exists(/databases/$(database)/documents/chats/$(chatId)) && - request.auth.uid in get(/databases/$(database)/documents/chats/$(chatId)).data.participants; - } match /tweets/{tweetId} { allow read, update: if request.auth != null; @@ -31,30 +25,43 @@ service cloud.firestore { allow delete: if isAuthorized(resource.data.createdBy); } - match /users/{document=**} { + // Specific rule for user stats (fixes like permission issue) + match /users/{userId}/stats/{docId} { + allow read, create, update: if request.auth != null && + (request.auth.uid == userId || request.auth.uid == userId.replace('@', '')); + } + + // Specific rule for user bookmarks + match /users/{userId}/bookmarks/{docId} { + allow read, write, create: if request.auth != null && request.auth.uid == userId; + } + + // General users rule (for top-level user documents) + match /users/{userId} { allow read: if request.auth != null; allow create: if isAdmin(); - allow update: if request.auth != null && (request.auth.uid == resource.data.id || isAdmin()); + allow update: if request.auth != null && (request.auth.uid == userId || isAdmin()); allow delete: if isAdmin(); } match /chats/{chatId} { allow read: if request.auth != null; - allow create: if request.auth != null && request.auth.uid in request.resource.data.participants; - allow update: if request.auth != null && request.auth.uid in resource.data.participants; - allow delete: if request.auth != null && request.auth.uid in resource.data.participants; + allow create: if request.auth != null && + (request.auth.uid in request.resource.data.participants || + ('@' + request.auth.uid) in request.resource.data.participants); + allow update: if request.auth != null && + (request.auth.uid in resource.data.participants || + ('@' + request.auth.uid) in resource.data.participants); + allow delete: if request.auth != null && + (request.auth.uid in resource.data.participants || + ('@' + request.auth.uid) in resource.data.participants); } match /chats/{chatId}/messages/{messageId} { allow read: if request.auth != null; - allow create: if request.auth != null && - request.auth.uid in get(/databases/$(database)/documents/chats/$(chatId)).data.participants && - request.auth.uid == request.resource.data.senderId; - allow update: if request.auth != null && - request.auth.uid in get(/databases/$(database)/documents/chats/$(chatId)).data.participants; - allow delete: if request.auth != null && - request.auth.uid in get(/databases/$(database)/documents/chats/$(chatId)).data.participants && - request.auth.uid == resource.data.senderId; + allow create: if request.auth != null; + allow update: if request.auth != null; + allow delete: if request.auth != null; } } -} +} \ No newline at end of file diff --git a/platforms/blabsy/next.config.js b/platforms/blabsy/next.config.js index 6d9d3f05..66a87635 100644 --- a/platforms/blabsy/next.config.js +++ b/platforms/blabsy/next.config.js @@ -1,10 +1,13 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - reactStrictMode: true, - swcMinify: true, - images: { - unoptimized: true - } + reactStrictMode: true, + swcMinify: true, + images: { + unoptimized: true + }, + eslint: { + ignoreDuringBuilds: true, + } }; module.exports = nextConfig; diff --git a/platforms/blabsy/src/components/chat/add-members.tsx b/platforms/blabsy/src/components/chat/add-members.tsx index 7d952cd5..11216b0b 100644 --- a/platforms/blabsy/src/components/chat/add-members.tsx +++ b/platforms/blabsy/src/components/chat/add-members.tsx @@ -183,7 +183,7 @@ export function AddMembers({ !selectedUsers.some( (selected) => selected.id === userData.id ) && // Exclude already selected - !currentChat?.participants.includes(userData.id) && // Exclude existing chat participants + (newChat || !currentChat?.participants.includes(userData.id)) && // Only exclude existing participants if NOT creating new chat (userData.name ?.toLowerCase() .includes(query.toLowerCase()) || @@ -478,7 +478,7 @@ export function AddMembers({ (u) => u.id === userItem.id ); const isExistingMember = - currentChat?.participants.includes(userItem.id); + !newChat && currentChat?.participants.includes(userItem.id); return (