dev: updates and URL collector

This commit is contained in:
KernelDeimos 2024-10-21 22:57:25 -04:00
parent 12e0cecf02
commit 368c20cf57
9 changed files with 134 additions and 10 deletions

View File

@ -51,6 +51,12 @@ module.exports = class APIError {
status: 400,
message: () => 'Invalid token'
},
'unrecognized_offering': {
status: 400,
message: ({ name }) => {
return `offering ${quot(name)} was not recognized.`;
},
},
// Things
'disallowed_thing': {
status: 400,
@ -65,7 +71,7 @@ module.exports = class APIError {
: ''
) + '.'
},
// Unorganized
'item_with_same_name_exists': {
status: 409,
@ -590,11 +596,11 @@ module.exports = class APIError {
status: this.status,
};
}
querystringize (extra) {
return new URLSearchParams(this.querystringize_(extra));
}
querystringize_ (extra) {
const fields = {};
for ( const k in this.fields ) {

View File

@ -0,0 +1,19 @@
const Component = use('util.Component');
export default def(class JustID extends Component {
static ID = 'ui.component.JustID';
static RENDER_MODE = Component.NO_SHADOW;
static PROPERTIES = {
id: { value: undefined },
}
create_template ({ template }) {
const size = 24;
$(template).html(/*html*/`
<div
style="height: 358px"
id="${this.get('id')}"></div>
`);
}
})

View File

@ -71,6 +71,21 @@ export default def(class StepView extends Component {
// now that we're ready, show the wrapper
$(this.dom_).find('#wrapper').show();
}
add_child (child) {
const children = this.get('children');
let pos = children.length;
child.setAttribute('slot', 'inside');
$(child).hide();
child.attach(this);
return pos;
}
display (child) {
const pos = this.add_child(child);
this.goto(pos);
}
back () {
if ( this.get('position') === 0 ) return;
@ -84,4 +99,8 @@ export default def(class StepView extends Component {
}
this.set('position', this.get('position') + 1);
}
goto (pos) {
this.set('position', pos);
}
});

View File

@ -322,7 +322,7 @@ const en = {
zipping: "Zipping %strong%",
// === 2FA Setup ===
setup2fa_1_step_heading: 'Open your authenticator app',
setup2fa_1_step_heading: 'Open your authenticator app',
setup2fa_1_instructions: `
You can use any authenticator app that supports the Time-based One-Time Password (TOTP) protocol.
There are many to choose from, but if you're unsure
@ -349,6 +349,12 @@ const en = {
login2fa_use_recovery_code: 'Use a recovery code',
login2fa_recovery_back: 'Back',
login2fa_recovery_placeholder: 'XXXXXXXX',
// Subscriptions
'offering.free': 'Use Puter',
'offering.pay-puter': 'Pay Puter',
'offering.pay-puter-more': 'Pay Puter More',
'offering.pay-puter-even-more': 'Pay Puter Even More',
}
};

View File

@ -0,0 +1,2 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32"><g stroke-width="2" transform="translate(0, 0)"><line data-color="color-2" x1="16" y1="7" x2="16" y2="25" fill="none" stroke="currentColor" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2" stroke-linejoin="miter"></line><path data-color="color-2" d="M20.294,10.889C18.349,9.88,12.1,9.116,12.1,12.593c0,4.163,7.778,2.691,7.778,6.367s-5.055,3.4-8.555,2.2" fill="none" stroke="currentColor" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2" stroke-linejoin="miter"></path><path data-cap="butt" d="M16,31.013a15.007,15.007,0,0,1-6.783-28.4" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="2" stroke-linecap="butt" stroke-linejoin="miter"></path><polyline points="8.419 9.029 9.22 2.617 2 3.52" fill="none" stroke="currentColor" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2" stroke-linejoin="miter"></polyline><path data-cap="butt" d="M16,.987a15.007,15.007,0,0,1,6.783,28.4" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="2" stroke-linecap="butt" stroke-linejoin="miter"></path><polyline points="23.581 22.971 22.78 29.383 30 28.48" fill="none" stroke="currentColor" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2" stroke-linejoin="miter"></polyline></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -21,12 +21,14 @@ logger.info('start -> async initialization');
import './util/TeePromise.js';
import './util/Component.js';
import './util/Collector.js';
import './UI/Components/Frame.js';
import './UI/Components/Glyph.js';
import './UI/Components/Spinner.js';
import './UI/Components/ActionCard.js';
import './UI/Components/NotifCard.js';
import './UI/Components/TestView.js';
import './UI/Components/JustID.js';
logger.info('end -> async initialization');
globalThis.init_promise.resolve();

View File

@ -0,0 +1,66 @@
const CollectorHandle = (key, collector) => ({
async get (route) {
if ( collector.stored[key] ) return collector.stored[key];
return await collector.fetch({ key, method: 'get', route });
},
async post (route, body) {
if ( collector.stored[key] ) return collector.stored[key];
return await collector.fetch({ key, method: 'post', route, body });
}
})
// TODO: link this with kv.js for expiration handling
export default def(class Collector {
constructor ({ origin, authToken }) {
this.origin = origin;
this.authToken = authToken;
this.stored = {};
}
to (name) {
return CollectorHandle(name, this);
}
whats (key) {
return this.stored[key];
}
async get (route) {
return await this.fetch({ method: 'get', route });
}
async post (route, body) {
return await this.fetch({ method: 'post', route, body });
}
discard (key) {
if ( ! key ) this.stored = {};
delete this.stored[key];
}
async fetch (options) {
const fetchOptions = {
method: options.method,
headers: {
Authorization: `Bearer ${this.authToken}`,
'Content-Type': 'application/json',
},
};
if ( options.method === 'post' ) {
fetchOptions.body = JSON.stringify(
options.body ?? {});
}
const maybe_slash = options.route.startsWith('/')
? '' : '/';
const resp = await fetch(
this.origin +maybe_slash+ options.route,
fetchOptions,
);
const asJSON = await resp.json();
if ( options.key ) this.stored[options.key] = asJSON;
return asJSON;
}
}, 'util.Collector');

View File

@ -183,7 +183,10 @@ export const Component = def(class Component extends HTMLElement {
this.dom_.appendChild(style);
}
if ( this.create_template ) {
this.create_template({ template });
this.create_template({
template,
content: template.content,
});
}
const el = template.content.cloneNode(true);
return el;
@ -202,7 +205,8 @@ export const Component = def(class Component extends HTMLElement {
}
this.values_[name].sub(callback);
callback(this.values_[name].get(), {});
}
},
dom: this.dom_,
};
}
});

View File

@ -27,13 +27,13 @@
/**
* Placeholder creates a simple element with a unique ID
* as an HTML string.
*
*
* This can be useful where string concatenation is used
* to build element trees.
*
*
* The `replaceWith` method can be used to replace the
* placeholder with a real element.
*
*
* @returns {PlaceholderReturn}
*/
const Placeholder = def(() => {
@ -49,7 +49,7 @@ const Placeholder = def(() => {
};
}, 'util.Placeholder');
const anti_collision = `94d2cb6b85a1`; // Arbitrary random string
const anti_collision = `a4d2cb6b85a1`; // Arbitrary random string
Placeholder.next_id_ = 0;
Placeholder.get_next_id_ = () => `${anti_collision}_${Placeholder.next_id_++}`;