Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions frontend/src/components/puzzles/PuzzleGame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ import { GameHeader } from "./GameHeader";
import { PuzzleSelectionView } from "./PuzzleSelectionView";
import { GamePlayingView } from "./GamePlayingView";
import { useState } from "react";
import { useLineraNotifications } from "@/lib/linera/hooks/useLineraNotifications";

export function PuzzleGame() {
const game = usePuzzleGame();
const { isPuzzleCompleted, isLoadingFromBlockchain: areCompletedPuzzlesLoading} = useCompletedPuzzles();
const [showTutorial, setShowTutorial] = useState(true);

// Set up notification handling for React Query invalidation
useLineraNotifications();


const handleCellClick = (x: number, y: number) => {
game.toggleCell(x, y);
};
Expand Down
28 changes: 28 additions & 0 deletions frontend/src/lib/linera/hooks/useLineraNotifications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useEffect } from "react";
import { useQueryClient } from "@tanstack/react-query";
import { LineraService } from "../services/LineraService";
import { useLineraInitialization } from "./useLineraQueries";

export function useLineraNotifications() {
const queryClient = useQueryClient();
const { data: initialized } = useLineraInitialization();

useEffect(() => {
if (!initialized) return;

const lineraService = LineraService.getInstance();

// Set up notification handler
lineraService.onNotification((notification) => {
console.log("[GOL] Received notification:", notification);

// When a new block is created, it might contain puzzle completions
// Invalidate the completed puzzles query to refetch
queryClient.invalidateQueries({ queryKey: ["completedPuzzles"] });
});

return () => {
// Cleanup is handled by LineraService disconnect
};
}, [initialized, queryClient]);
}
29 changes: 29 additions & 0 deletions frontend/src/lib/linera/lib/linera-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class LineraAdapter {
private wasmInitPromise: Promise<unknown> | null = null;
private connectPromise: Promise<LineraProvider> | null = null;
private onConnectionChange?: () => void;
private notificationCallbacks: Set<(notification: any) => void> = new Set();

private constructor() {}

Expand Down Expand Up @@ -61,6 +62,20 @@ export class LineraAdapter {
const client = await new Client(wallet, signer);
console.log("✅ Linera wallet created successfully!");

client.onNotification(notification => {
let newBlock = notification.reason.NewBlock;
if (!newBlock) return;

// Notify all registered callbacks
this.notificationCallbacks.forEach(callback => {
try {
callback(notification);
} catch (error) {
console.error("Error in notification callback:", error);
}
});
});

this.provider = {
client,
wallet,
Expand Down Expand Up @@ -145,12 +160,26 @@ export class LineraAdapter {
this.onConnectionChange = undefined;
}

getAddress(): string {
if (!this.provider) throw new Error("Provider not set");
return this.provider.address;
}

reset(): void {
this.application = null;
this.provider = null;
this.connectPromise = null;
this.onConnectionChange?.();
}

onNewBlockNotification(callback: (notification: any) => void): () => void {
this.notificationCallbacks.add(callback);

// Return unsubscribe function
return () => {
this.notificationCallbacks.delete(callback);
};
}
}

// Export singleton instance
Expand Down
16 changes: 12 additions & 4 deletions frontend/src/lib/linera/services/LineraService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class LineraService {
private initialized = false;
private isInitializing = false;
private walletInfo: WalletInfo | null = null;
private notificationUnsubscribe: (() => void) | null = null;

private constructor() {}

Expand Down Expand Up @@ -187,6 +188,7 @@ export class LineraService {
};

const result = await lineraAdapter.queryApplication<any>(query);
console.log("[GOL] Query sent using address: ", lineraAdapter.getAddress());
console.log("[GOL] Got response from getPuzzle", result);

if (result.errors) {
Expand Down Expand Up @@ -280,7 +282,8 @@ export class LineraService {
};

const result = await lineraAdapter.queryApplication<any>(query);
console.log("[GOL] Got completed puzzle IDs", result);
// console.log("[GOL] Got completed puzzle IDs", result);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deuszx @TudorEsan Should we remove this line?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can remove in another PR. The same thing was logged twice, in the same scenario, and was a bit confusing.

console.log("[GOL] Query sent using address: ", lineraAdapter.getAddress());

if (result.errors) {
console.error("GraphQL errors:", result.errors);
Expand All @@ -295,12 +298,17 @@ export class LineraService {
}
}

onNotification(_callback: (notification: any) => void): void {
// Notifications not supported with Dynamic wallet yet
console.warn("Notifications not yet supported with Dynamic wallet");
onNotification(callback: (notification: any) => void): void {
this.notificationUnsubscribe = lineraAdapter.onNewBlockNotification(callback);
}

async disconnect(): Promise<void> {
// Unsubscribe from notifications
if (this.notificationUnsubscribe) {
this.notificationUnsubscribe();
this.notificationUnsubscribe = null;
}

lineraAdapter.reset();
this.initialized = false;
this.isInitializing = false;
Expand Down