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

WebLn Implementaion to Quick-pay #19

Open
wants to merge 29 commits into
base: version6
Choose a base branch
from

Conversation

SanjaySinghRajpoot
Copy link

@SanjaySinghRajpoot SanjaySinghRajpoot commented Jun 3, 2022

Description

WebLn library added to the Quick-Pay browser extension. With the help of this, all the WebLn features will be implemented inside the content.js file and a new webln script will be created. It provides a programmatic, permissioned interface for letting applications ask users to send payments, generates invoices to receive payments, and much more. This documentation covers both how to use WebLN in your Lightning-driven applications, but also how to implement a provider.

Changes

  • WebLN installation
  • Invoice Error
  • JS scripts for permissions
  • WebLn provider Added
  • Privacy Docs Added

Thank You

Summary by CodeRabbit

  • New Features

    • Introduced interaction with Lightning Network nodes, including invoice creation, payment, and message signing.
    • Added a connector class for service interactions, enabling node information retrieval and payment processing.
    • Implemented a new WebLNProvider for initiating web Lightning Network interactions.
    • Enhanced payment module with better error handling for invalid invoice IDs.
    • Added a function to inject scripts into web pages for browser extension interaction.
  • Bug Fixes

    • Refined error message display in the error page.
    • Standardized quotation marks in JavaScript for consistency.
  • Style

    • Updated coding style for event handlers and message passing in scripts.
  • Documentation

    • Defined interfaces for web Lightning Network node interactions.
  • Chores

    • Added global variable for REQUEST_GETINFO.
    • Created utility functions for API calls, data conversion, and number formatting.

@ShahanaFarooqui ShahanaFarooqui changed the base branch from master to version6 June 10, 2022 15:42
@SanjaySinghRajpoot SanjaySinghRajpoot changed the title WebLn Installation using NPM package manager and script function WebLn Implementaion to Quick-pay Jun 16, 2022
@saubyk
Copy link
Collaborator

saubyk commented Jan 18, 2024

@coderabbitai review

Copy link

coderabbitai bot commented Jan 18, 2024

Walkthrough

The recent updates involve integrating Lightning Network capabilities into a web service, enhancing interactivity with a node through new connectors, interfaces, and provider classes. Error handling and message formatting have been refined across various modules, and scripts for enabling inpage interaction with a browser extension have been added. The changes aim to streamline payment processes, node interactions, and improve the user experience with more consistent and robust error messaging.

Changes

File Pattern Change Summary
src/actions/webln_action.js Introduced Lightning Network interaction functions.
src/connectors/... Added Cln class and connectors.interface.js for Lightning Network service interaction.
src/pages/error.js Standardized quotation marks and improved event handling syntax.
src/pages/payment.js Initialized WebLNProvider and updated error handling.
src/scripts/background.js Added script injection, webln call handling, and messaging logic.
src/scripts/content.js, src/scripts/injectScript.js Adjusted quote consistency and added inpage script injection.
src/shared/constants.js, src/shared/utils.js Introduced new constants and utility functions.
src/webln/webln.js Created WebLNProvider class for enabling and interacting with a provider.

Poem

In the realm of code, where the data hops,
A rabbit tweaked the web, with Lightning Network ops.
🌩️ With a flash and a spark, the payments now flow,
🐇 CodeRabbit hopped on, making the experience glow.

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share

Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit-tests for this file.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit tests for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository from git and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 5

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between a26bc50 and aedf74a.
Files ignored due to path filters (15)
  • dist/assets/themes/day/indigo.css is excluded by: !dist/**
  • dist/assets/themes/day/pink.css is excluded by: !dist/**
  • dist/assets/themes/day/purple.css is excluded by: !dist/**
  • dist/assets/themes/day/teal.css is excluded by: !dist/**
  • dist/assets/themes/day/yellow.css is excluded by: !dist/**
  • dist/assets/themes/night/indigo.css is excluded by: !dist/**
  • dist/assets/themes/night/pink.css is excluded by: !dist/**
  • dist/assets/themes/night/purple.css is excluded by: !dist/**
  • dist/assets/themes/night/teal.css is excluded by: !dist/**
  • dist/assets/themes/night/yellow.css is excluded by: !dist/**
  • dist/manifest.json is excluded by: !dist/**, !**/*.json
  • dist/pages/payment.js is excluded by: !dist/**
  • dist/scripts/content.js is excluded by: !dist/**
  • packages/RTL-Quickpay-v0.0.6.zip is excluded by: !**/*.zip
  • src/manifest.json is excluded by: !**/*.json
Files selected for processing (11)
  • src/actions/webln_action.js (1 hunks)
  • src/connectors/cln.js (1 hunks)
  • src/connectors/connectors.interface.js (1 hunks)
  • src/pages/error.js (1 hunks)
  • src/pages/payment.js (3 hunks)
  • src/scripts/background.js (2 hunks)
  • src/scripts/content.js (1 hunks)
  • src/scripts/injectScript.js (1 hunks)
  • src/shared/constants.js (1 hunks)
  • src/shared/utils.js (2 hunks)
  • src/webln/webln.js (1 hunks)
Additional comments: 12
src/connectors/connectors.interface.js (1)
  • 1-9: The interface definitions for WebLNNode and GetInfoResponse are clear and concise. They establish a contract for the shape of data related to Lightning Network nodes, which is essential for type safety and code maintainability.
src/pages/error.js (2)
  • 25-32: The error handling logic correctly checks the type of self.errorData.error and converts it to a string if necessary. It then updates the UI elements with the error message and title. This is a good use of defensive programming to ensure the error data is in the expected format before displaying it to the user.
  • 37-44: The click event handler for the back button is set up correctly to load the previous module when the button is clicked. This provides a clear navigation path for the user to return to the previous state after encountering an error.
src/scripts/content.js (1)
  • 1-68: The content script sets up event listeners for user interactions and sends messages to the background script as expected. The use of window.webln to provide WebLN functionality to the page is a good practice, as it encapsulates the WebLN provider within the window object, making it accessible to the page scripts.
src/actions/webln_action.js (3)
  • 4-16: The getInfo function correctly retrieves connector information and formats the response to match the expected WebLNNode interface. This ensures consistency in the data structure used throughout the application.
  • 19-44: The makeInvoiceWithPrompt function provides a good user experience by allowing the invoice amount and memo to be editable based on the presence of the respective arguments. The error handling is also appropriate, returning an error object if an exception occurs.
  • 60-67: The signMessageOrPrompt function checks if the message to sign is a string and returns an error if not. This is a good practice to validate input before processing.
src/shared/constants.js (1)
  • 70-70: The addition of the REQUEST_GETINFO global variable is consistent with the rest of the constants defined in the file. It's good practice to keep such constants centralized for easier management and updates.
src/shared/utils.js (1)
  • 21-73: The getOriginData function and its helper functions getDomain, getName, and getIcon are well-implemented to extract relevant data from the current web page. They use appropriate DOM methods and provide fallbacks where necessary.
src/connectors/cln.js (1)
  • 4-129: The Cln class provides methods for interacting with the Core Lightning node. The request method is well-implemented with proper error handling and response parsing. The use of fetch with appropriate headers and body formatting is correct. Ensure that sensitive data like macaroons are handled securely.
src/pages/payment.js (2)
  • 18-20: The Payment module correctly initializes a WebLNProvider instance. This is a crucial step for integrating WebLN functionality into the payment process.
  • 222-225: The error handling for an invalid invoice ID is updated to display a specific error message. This improves user feedback and helps in diagnosing input errors.

Comment on lines +1 to +22
var browser = require('webextension-polyfill');

// load the inpage script
// only an inpage script gets access to the document
// and the document can interact with the extension through the inpage script
export default function injectScript() {
try {
if (!document) throw new Error("No document");
const container = document.head || document.documentElement;
if (!container) throw new Error("No container element");
const scriptEl = document.createElement("script");
scriptEl.setAttribute("async", "false");
scriptEl.setAttribute("type", "text/javascript");
scriptEl.setAttribute(
"src",
browser.extension.getURL("js/inpageScript.bundle.js")
);
container.appendChild(scriptEl);
} catch (err) {
console.error("WebLN injection failed", err);
}
}
Copy link

Choose a reason for hiding this comment

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

The injectScript function correctly checks for the presence of document and the container element before creating and appending the script element. It also handles errors appropriately by logging them to the console. However, consider removing the async attribute setting as it is not necessary when setting the attribute to "false" and could be misleading.

- scriptEl.setAttribute("async", "false");

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
var browser = require('webextension-polyfill');
// load the inpage script
// only an inpage script gets access to the document
// and the document can interact with the extension through the inpage script
export default function injectScript() {
try {
if (!document) throw new Error("No document");
const container = document.head || document.documentElement;
if (!container) throw new Error("No container element");
const scriptEl = document.createElement("script");
scriptEl.setAttribute("async", "false");
scriptEl.setAttribute("type", "text/javascript");
scriptEl.setAttribute(
"src",
browser.extension.getURL("js/inpageScript.bundle.js")
);
container.appendChild(scriptEl);
} catch (err) {
console.error("WebLN injection failed", err);
}
}
var browser = require('webextension-polyfill');
// load the inpage script
// only an inpage script gets access to the document
// and the document can interact with the extension through the inpage script
export default function injectScript() {
try {
if (!document) throw new Error("No document");
const container = document.head || document.documentElement;
if (!container) throw new Error("No container element");
const scriptEl = document.createElement("script");
scriptEl.setAttribute("type", "text/javascript");
scriptEl.setAttribute(
"src",
browser.extension.getURL("js/inpageScript.bundle.js")
);
container.appendChild(scriptEl);
} catch (err) {
console.error("WebLN injection failed", err);
}
}

Comment on lines +47 to +57
async function payWithPrompt(message) {
try {
const response = await utils.openPrompt(Object.assign(Object.assign({}, message), { action: "confirmPayment" }));
return response;
}
catch (e) {
console.error("Payment cancelled", e);
if (e instanceof Error) {
return { error: e.message };
}
}
Copy link

Choose a reason for hiding this comment

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

The payWithPrompt function is designed to handle user confirmation for payments. The error handling could be improved by providing a more detailed error message to the user, rather than just logging it to the console.

- console.error("Payment cancelled", e);
+ return { error: "Payment cancelled: " + (e instanceof Error ? e.message : e) };

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
async function payWithPrompt(message) {
try {
const response = await utils.openPrompt(Object.assign(Object.assign({}, message), { action: "confirmPayment" }));
return response;
}
catch (e) {
console.error("Payment cancelled", e);
if (e instanceof Error) {
return { error: e.message };
}
}
async function payWithPrompt(message) {
try {
const response = await utils.openPrompt(Object.assign(Object.assign({}, message), { action: "confirmPayment" }));
return response;
}
catch (e) {
return { error: "Payment cancelled: " + (e instanceof Error ? e.message : e) };
if (e instanceof Error) {
return { error: e.message };
}
}
}

Comment on lines +1 to +60
export default class WebLNProvider {
constructor() {
this.enabled = false;
this.isEnabled = false;
this.executing = false;
}
enable() {
if (this.enabled) {
return Promise.resolve({ enabled: true });
}
return this.execute("enable").then((result) => {
if (typeof result.enabled === "boolean") {
this.enabled = result.enabled;
this.isEnabled = result.enabled;
}
return result;
});
}
getInfo() {
if (!this.enabled) {
throw new Error("Provider must be enabled before calling getInfo");
}
return this.execute("getInfo");
}

// NOTE: new call `action`s must be specified also in the content script
execute(action, args) {
return new Promise((resolve, reject) => {
// post the request to the content script. from there it gets passed to the background script and back
// in page script can not directly connect to the background script
window.postMessage({
application: "RTL",
prompt: true,
action: `${action}`,
args,
}, "*" // TODO use origin
);
function handleWindowMessage(messageEvent) {
// check if it is a relevant message
// there are some other events happening
if (!messageEvent.data ||
!messageEvent.data.response ||
messageEvent.data.application !== "RTL") {
return;
}
if (messageEvent.data.data.error) {
reject(new Error(messageEvent.data.data.error));
}
else {
// 1. data: the message data
// 2. data: the data passed as data to the message
// 3. data: the actual response data
resolve(messageEvent.data.data.data);
}
// For some reason must happen only at the end of this function
window.removeEventListener("message", handleWindowMessage);
}
window.addEventListener("message", handleWindowMessage);
});
}
Copy link

Choose a reason for hiding this comment

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

The WebLNProvider class is well-structured, with methods to enable the provider, retrieve information, and execute actions. The execute method's use of window messaging is appropriate for communication between the content script and the background script. However, consider validating the origin when posting and receiving messages for added security.

- }, "*" // TODO use origin
+ }, window.location.origin)

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
export default class WebLNProvider {
constructor() {
this.enabled = false;
this.isEnabled = false;
this.executing = false;
}
enable() {
if (this.enabled) {
return Promise.resolve({ enabled: true });
}
return this.execute("enable").then((result) => {
if (typeof result.enabled === "boolean") {
this.enabled = result.enabled;
this.isEnabled = result.enabled;
}
return result;
});
}
getInfo() {
if (!this.enabled) {
throw new Error("Provider must be enabled before calling getInfo");
}
return this.execute("getInfo");
}
// NOTE: new call `action`s must be specified also in the content script
execute(action, args) {
return new Promise((resolve, reject) => {
// post the request to the content script. from there it gets passed to the background script and back
// in page script can not directly connect to the background script
window.postMessage({
application: "RTL",
prompt: true,
action: `${action}`,
args,
}, "*" // TODO use origin
);
function handleWindowMessage(messageEvent) {
// check if it is a relevant message
// there are some other events happening
if (!messageEvent.data ||
!messageEvent.data.response ||
messageEvent.data.application !== "RTL") {
return;
}
if (messageEvent.data.data.error) {
reject(new Error(messageEvent.data.data.error));
}
else {
// 1. data: the message data
// 2. data: the data passed as data to the message
// 3. data: the actual response data
resolve(messageEvent.data.data.data);
}
// For some reason must happen only at the end of this function
window.removeEventListener("message", handleWindowMessage);
}
window.addEventListener("message", handleWindowMessage);
});
}
export default class WebLNProvider {
constructor() {
this.enabled = false;
this.isEnabled = false;
this.executing = false;
}
enable() {
if (this.enabled) {
return Promise.resolve({ enabled: true });
}
return this.execute("enable").then((result) => {
if (typeof result.enabled === "boolean") {
this.enabled = result.enabled;
this.isEnabled = result.enabled;
}
return result;
});
}
getInfo() {
if (!this.enabled) {
throw new Error("Provider must be enabled before calling getInfo");
}
return this.execute("getInfo");
}
// NOTE: new call `action`s must be specified also in the content script
execute(action, args) {
return new Promise((resolve, reject) => {
// post the request to the content script. from there it gets passed to the background script and back
// in page script can not directly connect to the background script
window.postMessage({
application: "RTL",
prompt: true,
action: `${action}`,
args,
}, window.location.origin)
function handleWindowMessage(messageEvent) {
// check if it is a relevant message
// there are some other events happening
if (!messageEvent.data ||
!messageEvent.data.response ||
messageEvent.data.application !== "RTL") {
return;
}
if (messageEvent.data.data.error) {
reject(new Error(messageEvent.data.data.error));
}
else {
// 1. data: the message data
// 2. data: the data passed as data to the message
// 3. data: the actual response data
resolve(messageEvent.data.data.data);
}
// For some reason must happen only at the end of this function
window.removeEventListener("message", handleWindowMessage);
}
window.addEventListener("message", handleWindowMessage);
});
}

Comment on lines +106 to +127
const utils = {
call: (action, args, overwrites) => {
return browser.runtime
.sendMessage(Object.assign({ application: "RTL", prompt: true, action: action, args: args, origin: { internal: true } }, overwrites))
.then((response) => {
if (response.error) {
toast.error(response.error);
throw new Error(response.error);
}
return response.data;
});
},
base64ToHex: (str) => {
const hex = [];
for (let i = 0, bin = atob(str.replace(/[ \r\n]+$/, "")); i < bin.length; ++i) {
let tmp = bin.charCodeAt(i).toString(16);
if (tmp.length === 1)
tmp = "0" + tmp;
hex[hex.length] = tmp;
}
return hex.join("");
},
Copy link

Choose a reason for hiding this comment

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

The utils object contains methods for making API calls and converting base64 to hex. The call method's error handling could be improved by providing more context in the error message.

- throw new Error(response.error);
+ throw new Error("API call failed: " + response.error);

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
const utils = {
call: (action, args, overwrites) => {
return browser.runtime
.sendMessage(Object.assign({ application: "RTL", prompt: true, action: action, args: args, origin: { internal: true } }, overwrites))
.then((response) => {
if (response.error) {
toast.error(response.error);
throw new Error(response.error);
}
return response.data;
});
},
base64ToHex: (str) => {
const hex = [];
for (let i = 0, bin = atob(str.replace(/[ \r\n]+$/, "")); i < bin.length; ++i) {
let tmp = bin.charCodeAt(i).toString(16);
if (tmp.length === 1)
tmp = "0" + tmp;
hex[hex.length] = tmp;
}
return hex.join("");
},
const utils = {
call: (action, args, overwrites) => {
return browser.runtime
.sendMessage(Object.assign({ application: "RTL", prompt: true, action: action, args: args, origin: { internal: true } }, overwrites))
.then((response) => {
if (response.error) {
toast.error(response.error);
throw new Error("API call failed: " + response.error);
}
return response.data;
});
},
base64ToHex: (str) => {
const hex = [];
for (let i = 0, bin = atob(str.replace(/[ \r\n]+$/, "")); i < bin.length; ++i) {
let tmp = bin.charCodeAt(i).toString(16);
if (tmp.length === 1)
tmp = "0" + tmp;
hex[hex.length] = tmp;
}
return hex.join("");
},

Comment on lines +41 to +106
const weblnCalls = ["getInfo"];

// calls that can be executed when webln is not enabled for the current content page
const disabledCalls = ["enable"];

let isEnabled = false; // store if webln is enabled for this content page
let callActive = false; // store if a webln is currently active. Used to prevent multiple calls in parallel

async function init() {

injectScript(); // injects the webln object

// message listener to listen to inpage webln calls
// those calls get passed on to the background script
// (the inpage script can not do that directly, but only the inpage script can make webln available to the page)
window.addEventListener("message", (ev) => {
// Only accept messages from the current window
if (ev.source !== window) {
return;
}
if (ev.data && ev.data.application === "RTL" && !ev.data.response) {
// if a call is active we ignore the request
if (callActive) {
console.error("WebLN call already executing");
return;
}
// limit the calls that can be made from webln
// only listed calls can be executed
// if not enabled only enable can be called.
const availableCalls = isEnabled ? weblnCalls : disabledCalls;
if (!availableCalls.includes(ev.data.action)) {
console.error("Function not available. Is the provider enabled?");
return;
}

const messageWithOrigin = {
action: `webln/${ev.data.action}`, // every webln call must be scoped under `webln/` we do this to indicate that those actions are callable from the websites
args: ev.data.args,
application: "RTL",
public: true, // indicate that this is a public call from the content script
prompt: true,
origin: getOriginData(),
};
const replyFunction = (response) => {
callActive = false; // reset call is active
// if it is the enable call we store if webln is enabled for this content script
if (ev.data.action === "enable") {
isEnabled = response.data?.enabled;
}
window.postMessage(
{
application: "RTL",
response: true,
data: response,
},
"*" // TODO use origin
);
};
callActive = true;
return browser.runtime
.sendMessage(messageWithOrigin)
.then(replyFunction)
.catch(replyFunction);
}
});
}
Copy link

Choose a reason for hiding this comment

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

The background.js file sets up the necessary infrastructure for handling WebLN calls. The init function and the message listener are correctly implemented to handle and process these calls. The use of isEnabled and callActive flags to manage the state is a good practice. Ensure that the TODO to use the origin in window.postMessage is addressed for security reasons.

- }, "*" // TODO use origin
+ }, window.location.origin)

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
const weblnCalls = ["getInfo"];
// calls that can be executed when webln is not enabled for the current content page
const disabledCalls = ["enable"];
let isEnabled = false; // store if webln is enabled for this content page
let callActive = false; // store if a webln is currently active. Used to prevent multiple calls in parallel
async function init() {
injectScript(); // injects the webln object
// message listener to listen to inpage webln calls
// those calls get passed on to the background script
// (the inpage script can not do that directly, but only the inpage script can make webln available to the page)
window.addEventListener("message", (ev) => {
// Only accept messages from the current window
if (ev.source !== window) {
return;
}
if (ev.data && ev.data.application === "RTL" && !ev.data.response) {
// if a call is active we ignore the request
if (callActive) {
console.error("WebLN call already executing");
return;
}
// limit the calls that can be made from webln
// only listed calls can be executed
// if not enabled only enable can be called.
const availableCalls = isEnabled ? weblnCalls : disabledCalls;
if (!availableCalls.includes(ev.data.action)) {
console.error("Function not available. Is the provider enabled?");
return;
}
const messageWithOrigin = {
action: `webln/${ev.data.action}`, // every webln call must be scoped under `webln/` we do this to indicate that those actions are callable from the websites
args: ev.data.args,
application: "RTL",
public: true, // indicate that this is a public call from the content script
prompt: true,
origin: getOriginData(),
};
const replyFunction = (response) => {
callActive = false; // reset call is active
// if it is the enable call we store if webln is enabled for this content script
if (ev.data.action === "enable") {
isEnabled = response.data?.enabled;
}
window.postMessage(
{
application: "RTL",
response: true,
data: response,
},
"*" // TODO use origin
);
};
callActive = true;
return browser.runtime
.sendMessage(messageWithOrigin)
.then(replyFunction)
.catch(replyFunction);
}
});
}
const weblnCalls = ["getInfo"];
// calls that can be executed when webln is not enabled for the current content page
const disabledCalls = ["enable"];
let isEnabled = false; // store if webln is enabled for this content page
let callActive = false; // store if a webln is currently active. Used to prevent multiple calls in parallel
async function init() {
injectScript(); // injects the webln object
// message listener to listen to inpage webln calls
// those calls get passed on to the background script
// (the inpage script can not do that directly, but only the inpage script can make webln available to the page)
window.addEventListener("message", (ev) => {
// Only accept messages from the current window
if (ev.source !== window) {
return;
}
if (ev.data && ev.data.application === "RTL" && !ev.data.response) {
// if a call is active we ignore the request
if (callActive) {
console.error("WebLN call already executing");
return;
}
// limit the calls that can be made from webln
// only listed calls can be executed
// if not enabled only enable can be called.
const availableCalls = isEnabled ? weblnCalls : disabledCalls;
if (!availableCalls.includes(ev.data.action)) {
console.error("Function not available. Is the provider enabled?");
return;
}
const messageWithOrigin = {
action: `webln/${ev.data.action}`, // every webln call must be scoped under `webln/` we do this to indicate that those actions are callable from the websites
args: ev.data.args,
application: "RTL",
public: true, // indicate that this is a public call from the content script
prompt: true,
origin: getOriginData(),
};
const replyFunction = (response) => {
callActive = false; // reset call is active
// if it is the enable call we store if webln is enabled for this content script
if (ev.data.action === "enable") {
isEnabled = response.data?.enabled;
}
window.postMessage(
{
application: "RTL",
response: true,
data: response,
},
window.location.origin)
};
callActive = true;
return browser.runtime
.sendMessage(messageWithOrigin)
.then(replyFunction)
.catch(replyFunction);
}
});
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants