Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wallet Standard Remote Wallet #957

Merged
merged 7 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ public void onOpen(WebSocket conn, ClientHandshake handshake) {
final MessageReceiver mr = mScenario.createMessageReceiver();
ws.messageReceiver = mr;
mr.receiverConnected(ws);
conn.send(new byte[0]); // send APP_PING
}

@Override
Expand Down
1 change: 1 addition & 0 deletions examples/example-web-app/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ registerMwa({
},
authorizationResultCache: createDefaultAuthorizationResultCache(),
chain: 'solana:testnet',
remoteHostAuthority: '4.tcp.us-cal-1.ngrok.io:15762',
onWalletNotFound: createDefaultWalletNotFoundHandler(),
})

Expand Down
8 changes: 6 additions & 2 deletions js/packages/wallet-standard-mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,14 @@
"@solana-mobile/mobile-wallet-adapter-protocol-web3js": "^2.1.3",
"@solana/wallet-standard-chains": "^1.1.0",
"@solana/wallet-standard-features": "^1.2.0",
"@solana/web3.js": "^1.58.0",
"@solana/web3.js": "^1.95.3",
"@wallet-standard/base": "^1.0.1",
"@wallet-standard/features": "^1.0.3",
"@wallet-standard/wallet": "^1.0.1",
"bs58": "^5.0.0"
"bs58": "^5.0.0",
"qrcode": "^1.5.4"
},
"devDependencies": {
"@types/qrcode": "^1.5.5"
}
}
Copy link

Choose a reason for hiding this comment

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

Should we wait with merging this until we build WebBluetooth connections end-to-end?

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export const BluetoothHtml = `
<div class="mobile-wallet-adapter-embedded-modal-content">
<button id="mobile-wallet-adapter-embedded-modal-close" class="mobile-wallet-adapter-embedded-modal-close">
<svg width="14" height="14">
<path d="M14 12.461 8.3 6.772l5.234-5.233L12.006 0 6.772 5.234 1.54 0 0 1.539l5.234 5.233L0 12.006l1.539 1.528L6.772 8.3l5.69 5.7L14 12.461z" />
</svg>
</button>
<h1><b>Jupiter</b> wants to connect</h1>
<p class="mobile-wallet-adapter-embedded-modal-subtitle">Connect to your mobile wallet app through Bluetooth.</p>
<div class="mobile-wallet-adapter-embedded-modal-connection-status-container">
<div id="status-not-connected" class="connection-status">
<svg class="bluetooth-icon" width="24" height="24" viewBox="0 0 24 24">
<path fill="#a0a0a0" d="M14.24 12.01l2.32 2.32c.28-.72.44-1.51.44-2.33 0-.82-.16-1.59-.43-2.31l-2.33 2.32zm5.29-5.3l-1.26 1.26c.63 1.21.98 2.57.98 4.02s-.36 2.82-.98 4.02l1.2 1.2c.97-1.54 1.54-3.36 1.54-5.31-.01-1.89-.55-3.67-1.48-5.19zm-3.82 1L10 2H9v7.59L4.41 5 3 6.41 8.59 12 3 17.59 4.41 19 9 14.41V22h1l5.71-5.71-4.3-4.29 4.3-4.29zM11 5.83l1.88 1.88L11 9.59V5.83zm1.88 10.46L11 18.17v-3.76l1.88 1.88z"/>
</svg>
<p>Not connected</p>
</div>
<div id="status-connecting" class="connection-status" style="display:none;">
<div class="spinner"></div>
<p>Connecting...</p>
</div>
<div id="status-connected" class="connection-status" style="display:none;">
<svg class="checkmark-icon" width="24" height="24" viewBox="0 0 24 24">
<path fill="#4CAF50" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
</svg>
<p>Connected</p>
</div>
</div>
<div class="button-group">
<button id="cancel-btn" class="cancel-btn">Cancel</button>
<button id="connect-btn" class="connect-btn">Connect</button>
</div>
</div>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import EmbeddedModal from './modal';

export default class EmbeddedModalController {
private _modal: EmbeddedModal;
private _connectionStatus: 'not-connected' | 'connecting' | 'connected' = 'not-connected';

constructor(title: string) {
this._modal = new EmbeddedModal(title);
}

async init() {
await this._modal.init();
this.attachEventListeners();
}

private attachEventListeners() {
const connectBtn = document.querySelector('#connect-btn');
connectBtn?.addEventListener('click', () => this.connect());
}

open() {
this._modal.open();
this.setConnectionStatus('not-connected');
}

close() {
this._modal.close();
this.setConnectionStatus('not-connected');
}

private setConnectionStatus(status: 'not-connected' | 'connecting' | 'connected') {
this._connectionStatus = status;
this._modal.setConnectionStatus(status);
}

private async connect() {
console.log('Connecting...');
this.setConnectionStatus('connecting');

try {
// Simulating connection process
await new Promise((resolve) => setTimeout(resolve, 5000));
this.setConnectionStatus('connected');
console.log('Connected!');
} catch (error) {
console.error('Connection failed:', error);
this.setConnectionStatus('not-connected');
}
}
}
Copy link

Choose a reason for hiding this comment

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

Remove?

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

Empty file.
145 changes: 145 additions & 0 deletions js/packages/wallet-standard-mobile/src/embedded-modal/modal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import QRCode from 'qrcode';

import { BluetoothHtml } from './bluetooth-html.js';
import { QRCodeHtml } from './qrcode-html.js';
import { css } from './styles.js';

export default class EmbeddedModal {
private _title: string;
private _root: HTMLElement | null = null;

constructor(title: string) {
this._title = title;

// Bind methods to ensure `this` context is correct
this.init = this.init.bind(this);
this.injectQRCodeHTML = this.injectQRCodeHTML.bind(this);
this.injectBluetoothHTML = this.injectBluetoothHTML.bind(this);
this.open = this.open.bind(this);
this.close = this.close.bind(this);
this.connect = this.connect.bind(this);

this._root = document.getElementById('mobile-wallet-adapter-embedded-root-ui');
}

async init(qrCode: string) {
console.log('Injecting modal');
this.injectStyles();
this.injectQRCodeHTML(qrCode);
}

setConnectionStatus(status: 'not-connected' | 'connecting' | 'connected') {
if (!this._root) return;

const statuses = ['not-connected', 'connecting', 'connected'];
statuses.forEach((s) => {
const el = this._root!.querySelector(`#status-${s}`);
if (el instanceof HTMLElement) {
el.style.display = s === status ? 'flex' : 'none';
}
});
}

private injectStyles() {
// Check if the styles have already been injected
if (document.getElementById('mobile-wallet-adapter-styles')) {
return;
}

const styleElement = document.createElement('style');
styleElement.id = 'mobile-wallet-adapter-styles';
styleElement.textContent = css;
document.head.appendChild(styleElement);
}

private async populateQRCode(qrUrl: string) {
const qrcodeContainer = document.getElementById('mobile-wallet-adapter-embedded-modal-qr-code-container');
if (qrcodeContainer) {
const qrCodeElement = await QRCode.toCanvas(qrUrl, { width: 400 });
if (qrcodeContainer.firstElementChild !== null) {
qrcodeContainer.replaceChild(qrCodeElement, qrcodeContainer.firstElementChild);
} else qrcodeContainer.appendChild(qrCodeElement);
} else {
console.error('QRCode Container not found');
}
}

private injectQRCodeHTML(qrCode: string) {
// Check if the HTML has already been injected
if (document.getElementById('mobile-wallet-adapter-embedded-root-ui')) {
if (!this._root) this._root = document.getElementById('mobile-wallet-adapter-embedded-root-ui');
this.populateQRCode(qrCode);
return;
}

// Create a container for the modal
this._root = document.createElement('div');
this._root.id = 'mobile-wallet-adapter-embedded-root-ui';
this._root.className = 'mobile-wallet-adapter-embedded-modal';
this._root.innerHTML = QRCodeHtml;
this._root.style.display = 'none';

// Append the modal to the body
document.body.appendChild(this._root);

// Render the QRCode
this.populateQRCode(qrCode);

this.attachEventListeners();
}

private injectBluetoothHTML() {
// Check if the HTML has already been injected
if (document.getElementById('mobile-wallet-adapter-embedded-root-ui')) {
return;
}

this._root = document.createElement('div');
this._root.id = 'mobile-wallet-adapter-embedded-root-ui';
this._root.className = 'mobile-wallet-adapter-embedded-modal';
this._root.innerHTML = BluetoothHtml;
document.body.appendChild(this._root);

this.attachEventListeners();
}

private attachEventListeners() {
if (!this._root) return;

const closeBtn = this._root.querySelector('#mobile-wallet-adapter-embedded-modal-close');
const cancelBtn = this._root.querySelector('#cancel-btn');
const connectBtn = this._root.querySelector('#connect-btn');

closeBtn?.addEventListener('click', () => this.close());
cancelBtn?.addEventListener('click', () => this.close());
connectBtn?.addEventListener('click', () => this.connect());
}

open() {
console.debug('Modal open');
if (this._root) {
this._root.style.display = 'flex';
this.setConnectionStatus('not-connected'); // Reset status when opening
}
}

close() {
console.debug('Modal close');
if (this._root) {
this._root.style.display = 'none';
this.setConnectionStatus('not-connected'); // Reset status when closing
}
}

private connect() {
console.log('Connecting...');
// Mock connection
this.setConnectionStatus('connecting');

// Simulate connection process
setTimeout(() => {
this.setConnectionStatus('connected');
console.log('Connected!');
}, 5000); // 5 seconds delay
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const QRCodeHtml = `
<div class="mobile-wallet-adapter-embedded-modal-content">
<button id="mobile-wallet-adapter-embedded-modal-close" class="mobile-wallet-adapter-embedded-modal-close">
<svg width="14" height="14">
<path d="M14 12.461 8.3 6.772l5.234-5.233L12.006 0 6.772 5.234 1.54 0 0 1.539l5.234 5.233L0 12.006l1.539 1.528L6.772 8.3l5.69 5.7L14 12.461z" />
</svg>
</button>
<h1>Scan to connect</h1>
<p class="mobile-wallet-adapter-embedded-modal-subtitle">Use your wallet app to scan the QR Code and connect.</p>
<div id="mobile-wallet-adapter-embedded-modal-qr-code-container" />
</div>
`;
Loading
Loading