From 2c9bc8f9e0192c1233e530912ebf682c78daafac Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Fri, 4 Apr 2025 18:43:05 -0400 Subject: [PATCH] dev: instant update for usage in settings --- .../modules/selfhosted/DevCreditService.js | 14 ++++++++ src/gui/src/UI/Settings/UITabUsage.js | 35 +++++++++++++++++-- src/gui/src/i18n/i18n.js | 17 ++++++--- src/gui/src/i18n/translations/en.js | 1 + 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/backend/src/modules/selfhosted/DevCreditService.js b/src/backend/src/modules/selfhosted/DevCreditService.js index 8e2978946..07b286542 100644 --- a/src/backend/src/modules/selfhosted/DevCreditService.js +++ b/src/backend/src/modules/selfhosted/DevCreditService.js @@ -44,12 +44,25 @@ class PermissiveCreditService extends BaseService { const username = event.actor.type.user.username; event.available = this.consume_user_credit_( username, event.cost); + if ( ! this.config.simulated_credit ) return; + + // Update usage settings tab in UI + svc_event.emit('outer.gui.usage.update', { + user_id_list: [event.actor.type.user.id], + response: { + id: 'dev-credit', + used: this.config.simulated_credit - + this.get_user_credit_(username), + available: this.config.simulated_credit, + }, + }); }); svc_event.on('usages.query', (_, event) => { const username = event.actor.type.user.username; if ( ! this.config.simulated_credit ) { event.usages.push({ + id: 'dev-credit', name: `Unlimited Credit`, used: 0, available: 1, @@ -57,6 +70,7 @@ class PermissiveCreditService extends BaseService { return; } event.usages.push({ + id: 'dev-credit', name: `Simulated Credit (${this.config.simulated_credit})`, used: this.config.simulated_credit - this.get_user_credit_(username), diff --git a/src/gui/src/UI/Settings/UITabUsage.js b/src/gui/src/UI/Settings/UITabUsage.js index 31effde0d..68f26d649 100644 --- a/src/gui/src/UI/Settings/UITabUsage.js +++ b/src/gui/src/UI/Settings/UITabUsage.js @@ -41,6 +41,7 @@ export default { `; }, init: ($el_window) => { + const sanitize_id = id => (''+id).replace(/[^A-Za-z0-9-]/g, ''); $.ajax({ url: window.api_origin + "/drivers/usage", type: 'GET', @@ -79,18 +80,46 @@ export default { name = `File Conversions`; h += ` -
+

${html_encode(name)}:

- ${Number(entry.used)} used of ${Number(entry.available)} + ${i18n('used_of', entry)}
${Number(entry.usage_percentage)}%
`; }); + + const divContent = $('.settings-content[data-settings="usage"]'); // Append driver usage bars to the container - $('.settings-content[data-settings="usage"]').append(`
${h}
`); + divContent.append(`
${h}
`); + + const update_usage = event => { + if ( ! event.usage_percentage ) { + event.usage_percentage = (event.used / event.available * 100).toFixed(0); + } + const el_divContent = divContent[0]; + el_divContent + .querySelector(`[data-id=${sanitize_id(event.id)}] .usage-progbar`) + .style.width = '' + Number(event.usage_percentage) + '%'; + el_divContent + .querySelector(`[data-id=${sanitize_id(event.id)}] .usage-progbar span`) + .innerText = '' + Number(event.usage_percentage) + '%'; + const used_of_str = i18n('used_of', event); + el_divContent + .querySelector(`[data-id=${sanitize_id(event.id)}] > span`) + .innerText = used_of_str; + }; + + divContent.on('remove', () => { + socket.off('usage.update', update_usage); + }); + socket.on('usage.update', update_usage); } }); diff --git a/src/gui/src/i18n/i18n.js b/src/gui/src/i18n/i18n.js index d1aee9b15..9dd8e095e 100644 --- a/src/gui/src/i18n/i18n.js +++ b/src/gui/src/i18n/i18n.js @@ -27,15 +27,22 @@ const variables = { privacy: "https://puter.com/privacy" }; -function ReplacePlaceholders(str) { - str = str.replace(/{{link=(.*?)}}(.*?){{\/link}}/g, (_, key, text) => `${text}`); - str = str.replace(/{{(.*?)}}/g, (_, key) => variables[key]); +function ReplacePlaceholders(str, arg_variables = {}) { + const all_variables = { ...variables, ...arg_variables }; + str = str.replace(/{{link=(.*?)}}(.*?){{\/link}}/g, (_, key, text) => `${text}`); + str = str.replace(/{{(.*?)}}/g, (_, key) => all_variables[key]); return str; } window.i18n = function (key, replacements = [], encode_html = true) { + let arg_variables = {}; if(Array.isArray(replacements) === false){ - replacements = [replacements]; + if ( typeof replacements === 'object' ) { + arg_variables = replacements; + replacements = []; + } else { + replacements = [replacements]; + } } let language = translations[window.locale] ?? translations['en']; @@ -44,7 +51,7 @@ window.i18n = function (key, replacements = [], encode_html = true) { if (!str) { str = key; } - str = ReplacePlaceholders(str); + str = ReplacePlaceholders(str, arg_variables); if ( encode_html ) { str = html_encode(str); // html_encode doesn't render line breaks diff --git a/src/gui/src/i18n/translations/en.js b/src/gui/src/i18n/translations/en.js index 924fd89e5..88f33b709 100644 --- a/src/gui/src/i18n/translations/en.js +++ b/src/gui/src/i18n/translations/en.js @@ -309,6 +309,7 @@ const en = { unzipping: "Unzipping %strong%", upload: 'Upload', upload_here: 'Upload here', + used_of: '{{used}} used of {{available}}', usage: 'Usage', username: "Username", username_changed: 'Username updated successfully.',