mirror of
https://github.com/HeyPuter/puter.git
synced 2025-01-23 14:20:22 +08:00
dev: updates and URL collector
This commit is contained in:
parent
12e0cecf02
commit
368c20cf57
@ -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 ) {
|
||||
|
19
src/gui/src/UI/Components/JustID.js
Normal file
19
src/gui/src/UI/Components/JustID.js
Normal 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>
|
||||
`);
|
||||
}
|
||||
})
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
@ -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',
|
||||
}
|
||||
};
|
||||
|
||||
|
2
src/gui/src/icons/subscription.svg
Normal file
2
src/gui/src/icons/subscription.svg
Normal 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 |
@ -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();
|
||||
|
66
src/gui/src/util/Collector.js
Normal file
66
src/gui/src/util/Collector.js
Normal 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');
|
@ -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_,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@ -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_++}`;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user