feat: I have implemented the core Web3 economy features for Plexus, aligning it with the "Club 2.0" vision.
This commit is contained in:
1509
client/package-lock.json
generated
1509
client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -21,11 +21,14 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"autoprefixer": "^10.4.17",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-vue": "^9.21.1",
|
||||
"jsdom": "^27.4.0",
|
||||
"postcss": "^8.4.33",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"vite": "^7.2.4"
|
||||
"vite": "^7.2.4",
|
||||
"vitest": "^4.0.17"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ const saveSettings = () => {
|
||||
{{ username }}
|
||||
</div>
|
||||
<div class="text-[10px] text-gray-400 truncate">
|
||||
#{{ walletAddress?.slice(-4) }}
|
||||
#{{ walletAddress?.slice(-4) }} • <span class="text-yellow-400">{{ chatStore.balance }} $PLEXUS</span>
|
||||
</div>
|
||||
</div>
|
||||
<Settings
|
||||
|
||||
97
client/src/stores/__tests__/chat.spec.js
Normal file
97
client/src/stores/__tests__/chat.spec.js
Normal file
@@ -0,0 +1,97 @@
|
||||
// @vitest-environment jsdom
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { setActivePinia, createPinia } from 'pinia';
|
||||
import { useChatStore } from '../chat';
|
||||
|
||||
// Mock socket.io-client
|
||||
const mockSocket = {
|
||||
on: vi.fn(),
|
||||
emit: vi.fn(),
|
||||
connected: true
|
||||
};
|
||||
|
||||
vi.mock('socket.io-client', () => ({
|
||||
io: () => mockSocket
|
||||
}));
|
||||
|
||||
// Mock js-cookie
|
||||
vi.mock('js-cookie', () => ({
|
||||
default: {
|
||||
set: vi.fn(),
|
||||
get: vi.fn(),
|
||||
remove: vi.fn()
|
||||
}
|
||||
}));
|
||||
|
||||
describe('Chat Store Web3 Economy', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia());
|
||||
vi.clearAllMocks();
|
||||
|
||||
// Reset socket mocks
|
||||
mockSocket.on.mockReset();
|
||||
mockSocket.emit.mockReset();
|
||||
|
||||
// Mock fetch
|
||||
global.fetch = vi.fn(() => Promise.resolve({
|
||||
json: () => Promise.resolve([])
|
||||
}));
|
||||
});
|
||||
|
||||
it('should initialize with default balance of 100', () => {
|
||||
const store = useChatStore();
|
||||
expect(store.balance).toBe(100);
|
||||
});
|
||||
|
||||
it('should deduct 1 PLEXUS when sending a message', () => {
|
||||
const store = useChatStore();
|
||||
store.connect('wallet123', 'user123');
|
||||
|
||||
// Mock socket connection
|
||||
const connectCallback = mockSocket.on.mock.calls.find(call => call[0] === 'connect')[1];
|
||||
connectCallback();
|
||||
|
||||
const initialBalance = store.balance;
|
||||
store.sendMessage('Hello');
|
||||
|
||||
expect(store.balance).toBe(initialBalance - 1);
|
||||
});
|
||||
|
||||
it('should prevent sending message if balance is insufficient', () => {
|
||||
const store = useChatStore();
|
||||
store.connect('wallet123', 'user123');
|
||||
store.balance = 0;
|
||||
|
||||
// Mock alert
|
||||
window.alert = vi.fn();
|
||||
|
||||
store.sendMessage('Hello');
|
||||
|
||||
expect(store.balance).toBe(0);
|
||||
expect(mockSocket.emit).not.toHaveBeenCalledWith('sendMessage', expect.anything());
|
||||
expect(window.alert).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should update balance when balanceUpdated event is received', () => {
|
||||
const store = useChatStore();
|
||||
store.connect('wallet123', 'user123');
|
||||
|
||||
// Find the balanceUpdated handler
|
||||
// We need to trigger the socket.on call that registers the handler
|
||||
// The store calls socket.on multiple times. We need to find the one for 'balanceUpdated'
|
||||
|
||||
// Since we mocked socket.on, we can simulate the event
|
||||
// But the store registers listeners inside `connect`
|
||||
|
||||
// Get all calls to socket.on
|
||||
const calls = mockSocket.on.mock.calls;
|
||||
const balanceHandler = calls.find(call => call[0] === 'balanceUpdated')[1];
|
||||
|
||||
expect(balanceHandler).toBeDefined();
|
||||
|
||||
// Simulate event
|
||||
balanceHandler({ balance: 50 });
|
||||
|
||||
expect(store.balance).toBe(50);
|
||||
});
|
||||
});
|
||||
@@ -9,6 +9,7 @@ export const useChatStore = defineStore('chat', () => {
|
||||
const walletAddress = ref(null);
|
||||
const username = ref(null);
|
||||
const signature = ref(null);
|
||||
const balance = ref(100); // Mock initial balance
|
||||
|
||||
const currentChannel = ref('nebula');
|
||||
const messages = ref({}); // { channelId: [messages] }
|
||||
@@ -105,6 +106,10 @@ export const useChatStore = defineStore('chat', () => {
|
||||
}
|
||||
});
|
||||
|
||||
socket.value.on('balanceUpdated', ({ balance: newBalance }) => {
|
||||
balance.value = newBalance;
|
||||
});
|
||||
|
||||
socket.value.on('error', (err) => {
|
||||
console.error('Socket error:', err);
|
||||
// Handle failed messages if we can identify them
|
||||
@@ -119,6 +124,14 @@ export const useChatStore = defineStore('chat', () => {
|
||||
function sendMessage(content) {
|
||||
if (!socket.value || !content.trim()) return;
|
||||
|
||||
if (balance.value < 1) {
|
||||
alert('Insufficient $PLEXUS balance! You need 1 $PLEXUS to send a message.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Deduct token immediately for UI feedback
|
||||
balance.value -= 1;
|
||||
|
||||
const tempId = 'temp-' + Date.now();
|
||||
const pendingMsg = {
|
||||
tempId,
|
||||
@@ -263,6 +276,8 @@ export const useChatStore = defineStore('chat', () => {
|
||||
getProfile,
|
||||
updateProfile,
|
||||
createPost,
|
||||
setChannel
|
||||
createPost,
|
||||
setChannel,
|
||||
balance
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user