Skip to content

Merge MiniApps into Main Branch #18

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
48 changes: 48 additions & 0 deletions android/tinySSB/app/src/main/assets/web/eventemitter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// eventEmitter.js

/**
* Class representing an EventEmitter.
* This class allows different parts of an application to communicate with each other
* by emitting and listening to events.
*/
class EventEmitter {

/**
* Create an EventEmitter.
* Initializes an empty events object to store event listeners.
*/
constructor() {
this.events = {};
}

/**
* Register an event listener for a specific event.
* If the event does not exist, it is created.
*
* @param {string} event - The name of the event.
* @param {function} listener - The callback function to execute when the event is emitted.
*/
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
}

/**
* Emit a specific event, executing all registered listeners for that event.
* Additional arguments are passed to the listener functions.
*
* @param {string} event - The name of the event to emit.
* @param {...*} args - The arguments to pass to the listener functions.
* @returns {Array} - The results of the listener functions.
*/
emit(event, ...args) {
if (!this.events[event]) return [];

return this.events[event].map(listener => listener(...args));
}
}

// Create a global instance of EventEmitter and assign it to window object
window.eventEmitter = new EventEmitter();
39 changes: 39 additions & 0 deletions android/tinySSB/app/src/main/assets/web/img/miniapps.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions android/tinySSB/app/src/main/assets/web/miniapps/miniAppHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
function getContactsList() {
// Get the contacts list from the native code
backend("getContactsList");
}

function writeLogEntry(entry) {
// Write a log entry to the native code, use currentMiniAppID to identify the app
// entry is a JSONString
console.log("writeLogEntry " + currentMiniAppID + " " + entry);
backend("customApp:writeEntry " + currentMiniAppID + " " + entry);
}

function readLogEntries(numberOfEntries) {
// Read the log entries from the native code associated with the currentMiniAppID
backend("customApp:readEntries " + currentMiniAppID + " " + numberOfEntries);
}

function quitApp() {
// Quit the app
setScenario('miniapps');
}

function launchContactsMenu(heading, subheading) {
closeOverlay()
fill_members(true);
prev_scenario = 'customApp';
setScenario("members");

document.getElementById("div:textarea").style.display = 'none';
document.getElementById("div:confirm-members").style.display = 'flex';
document.getElementById("tremolaTitle").style.display = 'none';
var c = document.getElementById("conversationTitle");
c.style.display = null;
c.innerHTML = "<font size=+1><strong>" + heading + "</strong></font><br>" + subheading;
document.getElementById('plus').style.display = 'none';
}
179 changes: 179 additions & 0 deletions android/tinySSB/app/src/main/assets/web/miniapps/mini_apps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/**
* mini_apps.js
*
* This file handles the dynamic loading and initialization of mini applications and chat extensions
* within the main application. It retrieves the manifest files for each mini app, processes the
* manifest data, and creates corresponding UI elements such as buttons. These buttons allow users
* to launch the mini apps and chat extensions from the mini app menu.
*
*/

"use strict";

/**
* Handles the paths to manifest files.
*
* This function gets the paths to the manifest files of every app from the backend as input and
* forwards each path to the backend to retrieve the data of each manifest file.
*
* @param {string} manifestPathsJson - JSON string containing an array of paths to manifest files.
*/
function handleManifestPaths(manifestPathsJson) {

const manifestPaths = JSON.parse(manifestPathsJson);
const listElement = document.getElementById('lst:miniapps');

manifestPaths.forEach(path => {
backend("getManifestData " + path);
//fetchManifestFile(path);
});

}

/**
* Handles the content of a manifest file.
*
* This function gets the data of a manifest file, creates a button containing that data and appends
* that button to the list that contains all the buttons that initiate each mini app.
*
* @param {string} content - JSON string containing the manifest data.
*/
function handleManifestContent(content) {

setTimeout(() => {
const manifest = JSON.parse(content);
// Process the manifest data (e.g., create buttons)
const listElement = document.getElementById('lst:miniapps');
const miniAppButton = createMiniAppButton(manifest);
listElement.appendChild(miniAppButton);
console.log(`Added button after delay: ${miniAppButton.id}`);
}, 100);

}

/**
* Creates a button to initiate a Mini App.
*
* This function gets the manifest data as input and uses it to create the button that initiates
* the mini app in the mini app menu. If the mini app is also a chat extension, a button will
* be added in the attach-menu.
*
* @param {Object} manifest - The manifest data of the mini app.
* @param {string} manifest.id - The ID of the mini app.
* @param {string} manifest.icon - The path to the icon of the mini app.
* @param {string} manifest.name - The name of the mini app.
* @param {string} manifest.description - The description of the mini app.
* @param {string} manifest.init - The initialization function as a string.
* @param {string} [manifest.extension] - Indicates if the app is also a chat extension.
* @returns {HTMLElement} The created button element.
*/
function createMiniAppButton(manifest) {
const item = document.createElement('div');
item.className = 'miniapp_item_div';

const button = document.createElement('button');
button.id = 'btn:' + manifest.id;
button.className = 'miniapp_item_button w100';
button.style.display = 'flex';
button.style.alignItems = 'center';
button.style.padding = '10px';
button.style.border = '1px solid #ccc';
button.style.borderRadius = '5px';
button.style.backgroundColor = '#f9f9f9';

console.log("Init function: " + manifest.init);

//button.onclick = manifest.init(); // This didn't work as intended
button.addEventListener('click', () => {
try {
// Dynamically evaluate the init function
console.log("Init function: " + manifest.init);
//Set currentMiniAppID to the manifest.id
currentMiniAppID = manifest.id
setScenario("customApp:" + manifest.id);
console.log(curr_scenario);
eval(manifest.init);
} catch (error) {
console.error(`Error executing init function: ${manifest.init}`, error + " " + error.stack);
}
});

const icon = document.createElement('img');
console.log("App Icon: " + manifest.icon);
let iconSrc = manifest.icon;
// Remove the incorrect asset prefix if present
if (iconSrc.startsWith("file:///android_asset/")) {
iconSrc = iconSrc.replace("file:///android_asset/", "file://");
}
icon.src = iconSrc;
console.log("Icon source: " + icon.src);
icon.alt = `${manifest.name} icon`;
icon.className = 'miniapp_icon';
icon.style.width = '50px';
icon.style.height = '50px';
icon.style.marginRight = '10px';


const textContainer = document.createElement('div');
textContainer.className = 'miniapp_text_container';

const nameElement = document.createElement('div');
nameElement.className = 'miniapp_name';
nameElement.textContent = manifest.name;

const descriptionElement = document.createElement('div');
descriptionElement.className = 'miniapp_description';
descriptionElement.textContent = manifest.description;

textContainer.appendChild(nameElement);
textContainer.appendChild(descriptionElement);

button.appendChild(icon);
button.appendChild(textContainer);
item.appendChild(button);

if (manifest.extension === "True") {
createExtensionButton(manifest);
}

return item;
}

/**
* Creates a button for a chat extension.
*
* This function gets the manifest data and uses it to create a button for the chat extension,
* which is then appended to the attach-menu.
*
* @param {Object} manifest - The manifest data of the chat extension.
* @param {string} manifest.extensionText - The text to display on the extension button.
* @param {string} manifest.extensionInit - The initialization function for the extension as a string.
*/
function createExtensionButton(manifest) {
//console.log("Extension entered")
const attachMenu = document.getElementById('attach-menu');

// Create a new button element
const newButton = document.createElement('button');

// Set the button's class
newButton.className = 'attach-menu-item-button';

// Set the button's text content
newButton.textContent = manifest.extensionText;

// Set the button's onclick event
newButton.addEventListener('click', () => {
try {
// Dynamically evaluate the init function
eval(manifest.extensionInit);
} catch (error) {
console.error(`Error executing extensionInit function: ${manifest.extensionInit}`, error);
}
});

// Append the new button to the target div
attachMenu.appendChild(newButton);
}


50 changes: 47 additions & 3 deletions android/tinySSB/app/src/main/assets/web/tremola.css
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,13 @@
width: 100%;
}
.buttontext {
height: 45pt;
padding: 4px 10px 10px 10px;
}
font-size: small;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
height: 50px;
padding: 3px;
}
.item {
border: none;
text-align: left;
Expand Down Expand Up @@ -646,6 +650,46 @@ input:checked + .slider:before {
background-color: red;
}

/* --------------------------------------------------------------------------- */
/* Mini App Menu */
/* --------------------------------------------------------------------------- */

.miniapp_item_div {
padding: 0px 5px 10px 5px;
margin: 3px 3px 6px 3px;
}

.miniapp_item_button {
overflow: hidden;
position: relative;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #f9f9f9;
padding: 10px;
width: 100%;
text-align: left;
display: flex;
align-items: center;
}

.miniapp_icon {
width: 50px;
height: 50px;
margin-right: 10px;
}

.miniapp_text_container {
display: flex;
flex-direction: column;
}

.miniapp_name {
font-weight: bold;
}

.miniapp_description {
font-size: small;
}


/* eof */
Loading