solid-ui
Version:
UI library for Solid applications
217 lines • 10.8 kB
JavaScript
/**
* Contains the [[AddAgentButtons]] class
* @packageDocumentation
*/
import * as debug from '../debug';
import { icons } from '../iconBase';
import { ensureLoadedProfile } from '../login/login';
import ns from '../ns';
import * as utils from '../utils';
import * as widgets from '../widgets';
import { style } from '../style';
/**
* Renders the Sharing pane's "+" button and the menus behind it,
* see https://github.com/solidos/userguide/blob/main/views/sharing/userguide.md#add
*/
export class AddAgentButtons {
constructor(groupList) {
this.groupList = groupList;
this.isExpanded = false;
this.rootElement = groupList.controller.dom.createElement('div');
this.barElement = groupList.controller.dom.createElement('div');
}
render() {
this.rootElement.innerHTML = '';
this.rootElement.appendChild(this.renderAddButton());
this.rootElement.appendChild(this.barElement);
return this.rootElement;
}
renderAddButton() {
return widgets.button(this.groupList.controller.dom, `${icons.iconBase}noun_34653_green.svg`, 'Add ...', () => {
this.toggleBar();
this.renderBar();
});
}
renderBar() {
this.barElement.innerHTML = '';
if (!this.isExpanded) {
return;
}
this.barElement.appendChild(this.renderPersonButton());
this.barElement.appendChild(this.renderGroupButton());
this.barElement.appendChild(this.renderPublicButton());
this.barElement.appendChild(this.renderAuthenticatedAgentButton());
this.barElement.appendChild(this.renderBotButton());
this.barElement.appendChild(this.renderAppsButton());
}
renderSimplifiedBar(button) {
Array.from(this.barElement.children)
.filter(element => element !== button)
.forEach(element => this.barElement.removeChild(element));
}
renderPersonButton() {
return widgets.button(this.groupList.controller.dom, icons.iconBase + widgets.iconForClass['vcard:Individual'], 'Add Person', event => {
this.renderSimplifiedBar(event.target);
this.renderNameForm(ns.vcard('Individual'), 'person')
.then(name => this.addPerson(name))
.then(() => this.renderCleanup())
.catch(error => this.groupList.controller.renderStatus(error));
});
}
renderGroupButton() {
return widgets.button(this.groupList.controller.dom, icons.iconBase + widgets.iconForClass['vcard:Group'], 'Add Group', event => {
this.renderSimplifiedBar(event.target);
this.renderNameForm(ns.vcard('Group'), 'group')
.then(name => this.addGroup(name))
.then(() => this.renderCleanup())
.catch(error => this.groupList.controller.renderStatus(error));
});
}
renderNameForm(type, noun) {
return widgets.askName(this.groupList.controller.dom, this.groupList.store, this.barElement, ns.vcard('URI'), type, noun);
}
renderPublicButton() {
return widgets.button(this.groupList.controller.dom, icons.iconBase + widgets.iconForClass['foaf:Agent'], 'Add Everyone', () => this.addAgent(ns.foaf('Agent').uri)
.then(() => this.groupList.controller.renderTemporaryStatus('Adding the general public to those who can read. Drag the globe to a different level to give them more access.'))
.then(() => this.renderCleanup()));
}
renderAuthenticatedAgentButton() {
return widgets.button(this.groupList.controller.dom, `${icons.iconBase}noun_99101.svg`, 'Anyone logged In', () => this.addAgent(ns.acl('AuthenticatedAgent').uri)
.then(() => this.groupList.controller.renderTemporaryStatus('Adding anyone logged in to those who can read. Drag the ID icon to a different level to give them more access.'))
.then(() => this.renderCleanup()));
}
renderBotButton() {
return widgets.button(this.groupList.controller.dom, icons.iconBase + 'noun_Robot_849764.svg', 'A Software Agent (bot)', event => {
this.renderSimplifiedBar(event.target);
this.renderNameForm(ns.schema('Application'), 'bot')
.then(name => this.addBot(name))
.then(() => this.renderCleanup());
});
}
renderAppsButton() {
return widgets.button(this.groupList.controller.dom, `${icons.iconBase}noun_15177.svg`, 'A Web App (origin)', event => {
this.renderSimplifiedBar(event.target);
const eventContext = {
div: this.barElement,
dom: this.groupList.controller.dom
};
const existingApps = this.renderAppsTable(eventContext)
.catch(error => this.groupList.controller.renderStatus(error));
this.renderAppsView();
const newApp = this.renderNameForm(ns.schema('WebApplication'), 'webapp domain')
.then(name => this.getOriginFromName(name));
Promise.race([
existingApps,
newApp
])
.then(origin => {
if (origin) {
this.groupList.addNewURI(origin);
}
})
.then(() => this.renderCleanup());
});
}
renderAppsView() {
const trustedApplications = this.groupList.controller.context.session.paneRegistry.byName('trustedApplications');
if (trustedApplications) {
const trustedApplicationsElement = trustedApplications.render(null, this.groupList.controller.context);
trustedApplicationsElement.setAttribute('style', style.trustedAppController);
const cancelButton = widgets.cancelButton(this.groupList.controller.dom, () => this.renderCleanup());
cancelButton.setAttribute('style', style.trustedAppCancelButton);
trustedApplicationsElement.insertBefore(cancelButton, trustedApplicationsElement.firstChild);
this.barElement.appendChild(trustedApplicationsElement);
}
}
async renderAppsTable(eventContext) {
await ensureLoadedProfile(eventContext);
const trustedApps = this.groupList.store.each(eventContext.me, ns.acl('trustedApp')); // @@ TODO fix as
const trustedOrigins = trustedApps.flatMap(app => this.groupList.store.each(app, ns.acl('origin'))); // @@ TODO fix as
this.barElement.appendChild(this.groupList.controller.dom.createElement('p')).textContent = `You have ${trustedOrigins.length} selected web apps.`;
return new Promise((resolve, reject) => {
const appsTable = this.barElement.appendChild(this.groupList.controller.dom.createElement('table'));
appsTable.setAttribute('style', style.trustedAppAddApplicationsTable);
trustedApps.forEach(app => {
const origin = this.groupList.store.any(app, ns.acl('origin')); // @@ TODO fix as
if (!origin) {
reject(new Error(`Unable to pick app: ${app.value}`));
}
const thingTR = widgets.personTR(this.groupList.controller.dom, ns.acl('origin'), origin, {});
const innerTable = this.groupList.controller.dom.createElement('table');
const innerRow = innerTable.appendChild(this.groupList.controller.dom.createElement('tr'));
const innerLeftColumn = innerRow.appendChild(this.groupList.controller.dom.createElement('td'));
innerLeftColumn.appendChild(thingTR);
const innerMiddleColumn = innerRow.appendChild(this.groupList.controller.dom.createElement('td'));
innerMiddleColumn.textContent = `Give access to ${this.groupList.controller.noun} ${utils.label(this.groupList.controller.subject)}?`;
const innerRightColumn = innerRow.appendChild(this.groupList.controller.dom.createElement('td'));
innerRightColumn.appendChild(widgets.continueButton(this.groupList.controller.dom, () => resolve(origin.value)));
appsTable.appendChild(innerTable);
});
});
}
renderCleanup() {
this.renderBar();
this.groupList.render();
}
async addPerson(name) {
if (!name)
return this.toggleBar(); // user cancelled
const domainNameRegexp = /^https?:/i;
if (!name.match(domainNameRegexp)) {
// @@ enforce in user input live like a form element
return Promise.reject(new Error('Not a http URI'));
}
// @@ check it actually is a person and has an owner who agrees they own it
debug.log(`Adding to ACL person: ${name}`);
await this.groupList.addNewURI(name);
this.toggleBar();
}
async addGroup(name) {
if (!name)
return this.toggleBar(); // user cancelled
const domainNameRegexp = /^https?:/i;
if (!name.match(domainNameRegexp)) {
// @@ enforce in user input live like a form element
return Promise.reject(new Error('Not a http URI'));
}
// @@ check it actually is a group and has an owner who agrees they own it
debug.log('Adding to ACL group: ' + name);
await this.groupList.addNewURI(name);
this.toggleBar();
}
async addAgent(agentUri) {
await this.groupList.addNewURI(agentUri);
this.toggleBar();
}
async addBot(name) {
if (!name)
return this.toggleBar(); // user cancelled
const domainNameRegexp = /^https?:/i;
if (!name.match(domainNameRegexp)) {
// @@ enforce in user input live like a form element
return Promise.reject(new Error('Not a http URI'));
}
// @@ check it actually is a bot and has an owner who agrees they own it
debug.log('Adding to ACL bot: ' + name);
await this.groupList.addNewURI(name);
this.toggleBar();
}
async getOriginFromName(name) {
if (!name)
return Promise.resolve(); // user cancelled
const domainNameRegexp = /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i;
// https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch08s15.html
if (!name.match(domainNameRegexp)) {
// @@ enforce in user input live like a form element
return Promise.reject(new Error('Not a domain name'));
}
const origin = 'https://' + name;
debug.log('Adding to ACL origin: ' + origin);
this.toggleBar();
return origin;
}
toggleBar() {
this.isExpanded = !this.isExpanded;
}
}
//# sourceMappingURL=add-agent-buttons.js.map