diff --git a/lib/plankton/plankton.d.ts b/lib/plankton/plankton.d.ts index 1598f00..b105772 100644 --- a/lib/plankton/plankton.d.ts +++ b/lib/plankton/plankton.d.ts @@ -1,11 +1,11 @@ /** * @author fenris */ -type int = number; +declare type int = number; /** * @author fenris */ -type float = number; +declare type float = number; declare class Buffer { constructor(x: string, modifier?: string); toString(modifier?: string): string; @@ -19,7 +19,7 @@ declare namespace lib_plankton.base { /** * @author fenris */ -type type_pseudopointer = { +declare type type_pseudopointer = { value: type_value; }; /** @@ -647,6 +647,9 @@ declare namespace lib_plankton.call { value: (null | type_value); error: (null | any); }>; + /** + */ + export function sleep(seconds: float): Promise; export {}; } declare namespace lib_plankton.file { @@ -1528,6 +1531,18 @@ declare namespace lib_plankton.translate { /** * @desc retrieves a string by going through the order and trying to fetch it for the current entry * @author fenris + * @todo rename to "get" + */ + function get_new(path: string, { "args": args, "preferred_language": preferred_language, "fallback": fallback, }?: { + args?: Record; + preferred_language?: (null | string); + fallback?: string; + }): string; + /** + * @desc retrieves a string by going through the order and trying to fetch it for the current entry + * @author fenris + * @deprecated use "get_new" + * @todo remove */ function get(path: string, args?: { [id: string]: string; @@ -1894,6 +1909,51 @@ declare namespace lib_plankton.storage.localstorage { }[]>; } } +declare namespace lib_plankton.cache { + /** + */ + type type_result = { + retrieved: boolean; + value: type_value; + }; + /** + */ + type type_entry = { + value: type_value; + expiry: (null | float); + }; + /** + */ + type type_subject = lib_plankton.storage.type_chest, void, any, any>; +} +declare namespace lib_plankton.cache { + /** + */ + function make({ "chest": chest, }?: { + chest?: lib_plankton.storage.type_chest, void, any, any>; + }): type_subject; + /** + */ + function init(subject: type_subject): Promise; + /** + */ + function clear(subject: type_subject): Promise; + /** + */ + function invalidate(subject: type_subject, key: string): Promise; + /** + */ + function query(subject: type_subject, key: string, lifetime: (null | float), retrieve: (() => Promise)): Promise>; + /** + * syntactic sugar: if the information, whether the value was retrieved, is irrelevant + */ + function get(subject: type_subject, key: string, lifetime: (null | float), retrieve: (() => Promise)): Promise; + /** + */ + function get_complex(cache: type_subject, group: string, input: type_input, lifetime: (null | float), retrieve: ((input: type_input) => Promise), { "encode_input": encode_input, }?: { + encode_input?: ((input: type_input) => string); + }): Promise; +} declare namespace lib_plankton.map { /** */ @@ -2105,18 +2165,45 @@ declare namespace lib_plankton.pit { /** */ function is_before(pit: type_pit, reference: type_pit): boolean; + /** + */ + function is_after(pit: type_pit, reference: type_pit): boolean; + /** + */ + function is_equal_or_after(pit: type_pit, reference: type_pit): boolean; /** */ function is_between(pit: type_pit, reference_left: type_pit, reference_right: type_pit): boolean; + /** + */ + function shift_hour(pit: type_pit, increment: int): type_pit; /** */ function shift_day(pit: type_pit, increment: int): type_pit; /** */ function shift_week(pit: type_pit, increment: int): type_pit; + /** + */ + function shift_year(pit: type_pit, increment: int): type_pit; + /** + */ + function trunc_minute(pit: type_pit): type_pit; + /** + */ + function trunc_hour(pit: type_pit): type_pit; + /** + */ + function trunc_day(pit: type_pit): type_pit; /** */ function trunc_week(pit: type_pit): type_pit; + /** + */ + function trunc_month(pit: type_pit): type_pit; + /** + */ + function trunc_year(pit: type_pit): type_pit; /** */ function now(): type_pit; @@ -2746,7 +2833,7 @@ declare namespace lib_plankton.zoo_input { private core; /** */ - constructor(options?: { + constructor({ "label_timezone_shift": label_timezone_shift, "label_date": label_date, "label_time": label_time, }?: { label_timezone_shift?: string; label_date?: string; label_time?: string; diff --git a/lib/plankton/plankton.js b/lib/plankton/plankton.js index 0217965..90f16cf 100644 --- a/lib/plankton/plankton.js +++ b/lib/plankton/plankton.js @@ -1467,6 +1467,16 @@ var lib_plankton; }))); } call.try_catch_wrap_async = try_catch_wrap_async; + /** + */ + function sleep(seconds) { + return (new Promise((resolve, reject) => { + setTimeout(() => { + resolve(undefined); + }, Math.floor(seconds * 1000)); + })); + } + call.sleep = sleep; })(call = lib_plankton.call || (lib_plankton.call = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -2222,7 +2232,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { + while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { @@ -4623,8 +4633,9 @@ var lib_plankton; /** * @desc retrieves a string by going through the order and trying to fetch it for the current entry * @author fenris + * @todo rename to "get" */ - function get(path, args = {}, fallback = null) { + function get_new(path, { "args": args = {}, "preferred_language": preferred_language = null, "fallback": fallback = null, } = {}) { if (fallback == null) { fallback = `{${path}}`; } @@ -4632,11 +4643,16 @@ var lib_plankton; console.info(`${_logprefix} getting translation for string '${path}' with arguments ${JSON.stringify(args)} …`); } let result = lib_plankton.pod.make_empty(); - let found = _order.some(identifier => { + const order = ((preferred_language === null) + ? + _order + : + ([preferred_language].concat(_order.filter(x => (x !== preferred_language))))); + const found = order.some(identifier => { if (translate._verbosity >= 2) { console.info(`${_logprefix} trying package '${identifier}' …`); } - let result_ = fetch(identifier, path, args); + const result_ = fetch(identifier, path, args); if (lib_plankton.pod.is_filled(result_)) { result = result_; return true; @@ -4646,20 +4662,33 @@ var lib_plankton; } }); if (found) { - let str = lib_plankton.pod.cull(result); + const str = lib_plankton.pod.cull(result); if (translate._verbosity >= 3) { console.info(`${_logprefix} found translation: '${str}'`); } return str; } else { - let str = fallback; + const str = fallback; if (translate._verbosity >= 1) { console.warn(`${_logprefix} no package provides a translation for string '${path}'; will use the fallback translation '${str}'`); } return str; } } + translate.get_new = get_new; + /** + * @desc retrieves a string by going through the order and trying to fetch it for the current entry + * @author fenris + * @deprecated use "get_new" + * @todo remove + */ + function get(path, args = {}, fallback = null) { + return get_new(path, { + "args": args, + "fallback": fallback, + }); + } translate.get = get; /** * @author fenris @@ -5184,6 +5213,136 @@ var lib_plankton; })(storage = lib_plankton.storage || (lib_plankton.storage = {})); })(lib_plankton || (lib_plankton = {})); /* +This file is part of »bacterio-plankton:cache«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:cache« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:cache« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:cache«. If not, see . + */ +/* +This file is part of »bacterio-plankton:cache«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:cache« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:cache« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:cache«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var cache; + (function (cache_1) { + /** + */ + function make({ "chest": chest = lib_plankton.storage.memory.implementation_chest({}), } = {}) { + return chest; + } + cache_1.make = make; + /** + */ + function init(subject) { + return subject.setup(undefined); + } + cache_1.init = init; + /** + */ + function clear(subject) { + return subject.clear(); + } + cache_1.clear = clear; + /** + */ + function invalidate(subject, key) { + return subject.delete(key); + } + cache_1.invalidate = invalidate; + /** + */ + async function query(subject, key, lifetime, retrieve) { + const now = lib_plankton.base.get_current_timestamp(); + return ((subject.read(key) + .then((entry) => Promise.resolve({ + "found": true, + "entry": entry, + })) + .catch(() => Promise.resolve({ + "found": false, + "entry": null, + }))) + .then((item) => { + if ((!item.found) + || + ((item.entry.expiry !== null) + && + (item.entry.expiry <= now))) { + lib_plankton.log.info("plankton.cache.unknown_or_expired", { + "key": key, + }); + return (retrieve() + .then((value) => (subject.write(key, { + "value": value, + "expiry": ((lifetime === null) + ? + null + : + (now + lifetime)) + }) + .then(() => Promise.resolve({ + "retrieved": true, + "value": value + }))))); + } + else { + lib_plankton.log.info("plankton.cache.known_and_valid", { + "key": key, + }); + return Promise.resolve({ + "retrieved": false, + "value": item.entry.value + }); + } + })); + } + cache_1.query = query; + /** + * syntactic sugar: if the information, whether the value was retrieved, is irrelevant + */ + function get(subject, key, lifetime, retrieve) { + return (query(subject, key, lifetime, retrieve) + .then(result => Promise.resolve(result.value))); + } + cache_1.get = get; + /** + */ + function get_complex(cache, group, input, lifetime, retrieve, { "encode_input": encode_input = (input => JSON.stringify(input)), } = {}) { + return get(cache, (group + "." + encode_input(input)), lifetime, () => retrieve(input)); + } + cache_1.get_complex = get_complex; + })(cache = lib_plankton.cache || (lib_plankton.cache = {})); +})(lib_plankton || (lib_plankton = {})); +/* This file is part of »bacterio-plankton:map«. Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' @@ -5821,10 +5980,17 @@ var lib_plankton; function is_after(pit, reference) { return (pit > reference); } + pit_1.is_after = is_after; + /** + */ + function is_equal_or_after(pit, reference) { + return (pit >= reference); + } + pit_1.is_equal_or_after = is_equal_or_after; /** */ function is_between(pit, reference_left, reference_right) { - return (is_after(pit, reference_left) + return (is_equal_or_after(pit, reference_left) && is_before(pit, reference_right)); } @@ -5834,6 +6000,7 @@ var lib_plankton; function shift_hour(pit, increment) { return (pit + (60 * 60 * increment)); } + pit_1.shift_hour = shift_hour; /** */ function shift_day(pit, increment) { @@ -5851,6 +6018,7 @@ var lib_plankton; function shift_year(pit, increment) { return (pit + (60 * 60 * 24 * 365 * increment)); } + pit_1.shift_year = shift_year; /** */ function trunc_minute(pit) { @@ -5878,6 +6046,7 @@ var lib_plankton; }; return from_datetime(datetime_output); } + pit_1.trunc_minute = trunc_minute; /** */ function trunc_hour(pit) { @@ -5901,6 +6070,7 @@ var lib_plankton; }; return from_datetime(datetime_output); } + pit_1.trunc_hour = trunc_hour; /** */ function trunc_day(pit) { @@ -5920,6 +6090,7 @@ var lib_plankton; }; return from_datetime(datetime_output); } + pit_1.trunc_day = trunc_day; /** */ function trunc_week(pit) { @@ -5951,6 +6122,7 @@ var lib_plankton; }; return from_datetime(datetime_output); } + pit_1.trunc_month = trunc_month; /** */ function trunc_year(pit) { @@ -5970,6 +6142,7 @@ var lib_plankton; }; return from_datetime(datetime_output); } + pit_1.trunc_year = trunc_year; /** */ function now() { @@ -7672,27 +7845,32 @@ var lib_plankton; class class_input_datetime { /** */ - constructor(options = {}) { - options = Object.assign({ - "label_timezone_shift": "", - "label_date": "", - "label_time": "", - }, options); + constructor({ "label_timezone_shift": label_timezone_shift = null, "label_date": label_date = null, "label_time": label_time = null, } = {}) { + /* + options = Object.assign( + { + "label_timezone_shift": "", + "label_date": "", + "label_time": "", + }, + options + ); + */ this.core = new zoo_input.class_input_group([ { "name": "timezone_shift", "input": new zoo_input.class_input_number(), - "label": options.label_timezone_shift, + "label": label_timezone_shift, }, { "name": "date", "input": new zoo_input.class_input_date(), - "label": options.label_date, + "label": label_date, }, { "name": "time", "input": new zoo_input.class_input_soft(new zoo_input.class_input_time()), - "label": options.label_time, + "label": label_time, }, ]); } diff --git a/source/logic/logic.ts b/source/logic/logic.ts new file mode 100644 index 0000000..48cc08a --- /dev/null +++ b/source/logic/logic.ts @@ -0,0 +1,103 @@ +/* +Espe | Ein schlichtes Werkzeug zur Mitglieder-Verwaltung | Frontend +Copyright (C) 2024 Christian Fraß + +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public +License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program. If not, see +. + */ + + +namespace _espe.logic +{ + + /** + */ + let _cache : lib_plankton.cache.type_subject; + + + /** + */ + export async function init( + ) : Promise + { + _cache = lib_plankton.cache.make( + { + // "chest": lib_plankton.storage.memory.implementation_chest({}), + } + ); + await lib_plankton.cache.init( + _cache + ); + } + + + /** + */ + export function group_data( + ) : Promise< + { + pool : Map< + int, + { + name : string; + label : string; + } + >; + order : Array; + } + > + { + return lib_plankton.cache.get< + { + pool : Map< + int, + { + name : string; + label : string; + } + >; + order : Array; + } + >( + _cache, + "group_data", + 120, + async () => { + const array : Array< + { + id : int; + preview : { + name : string; + label : string; + }; + } + > = await _espe.backend.group_list(); + const pool : Map = new Map(); + const order : Array = new Array(); + for (const entry of array) + { + pool.set( + entry.id, + { + "name": entry.preview.name, + "label": entry.preview.label, + } + ); + order.push(entry.id); + } + return { + "pool": pool, + "order": order, + }; + } + ); + } + +} diff --git a/source/logic/main.ts b/source/logic/main.ts index fbdcf9c..486fd41 100644 --- a/source/logic/main.ts +++ b/source/logic/main.ts @@ -120,6 +120,8 @@ async function main( } ); + await _espe.logic.init(); + await lib_plankton.zoo_page.init( document.querySelector("main"), { diff --git a/source/pages/invitation_create/logic.ts b/source/pages/invitation_create/logic.ts index 2c66b3d..2882d05 100644 --- a/source/pages/invitation_create/logic.ts +++ b/source/pages/invitation_create/logic.ts @@ -22,41 +22,7 @@ lib_plankton.zoo_page.register( const indent = str => (/*"... " + */str); - /** - * @todo cache - */ - const groups_as_array : Array< - { - id : int; - preview : { - name : string; - label : string; - }; - } - > = await _espe.backend.group_list(); - const groups_as_map : Map< - int, - { - name : string; - label : string; - } - > = new Map< - int, - { - name : string; - label : string; - } - >(); - for (const group_thingy of groups_as_array) - { - groups_as_map.set( - group_thingy.id, - { - "name": group_thingy.preview.name, - "label": group_thingy.preview.label, - } - ); - } + const group_data = await _espe.logic.group_data(); /** * @todo unify with form of "invitation_view" @@ -212,10 +178,10 @@ lib_plankton.zoo_page.register( */ "input": new lib_plankton.zoo_input.class_input_wrapped, Array>( new _espe.class_input_set( - groups_as_array.map( - group_thingy => group_thingy.id.toFixed(0) + group_data.order.map( + group_id => group_id.toFixed(0) ), - group_id_encoded => groups_as_map.get(parseInt(group_id_encoded)).label + group_id_encoded => group_data.pool.get(parseInt(group_id_encoded)).label ), (value_inner) => { const array : Array = []; diff --git a/source/pages/invitation_handle/logic.ts b/source/pages/invitation_handle/logic.ts index 3a9f2a8..77bae6d 100644 --- a/source/pages/invitation_handle/logic.ts +++ b/source/pages/invitation_handle/logic.ts @@ -81,41 +81,7 @@ lib_plankton.zoo_page.register( } else { - /** - * @todo cache - */ - const groups_as_array : Array< - { - id : int; - preview : { - name : string; - label : string; - }; - } - > = await _espe.backend.group_list(); - const groups_as_map : Map< - int, - { - name : string; - label : string; - } - > = new Map< - int, - { - name : string; - label : string; - } - >(); - for (const group_thingy of groups_as_array) - { - groups_as_map.set( - group_thingy.id, - { - "name": group_thingy.preview.name, - "label": group_thingy.preview.label, - } - ); - } + const group_data = await _espe.logic.group_data(); const form = new lib_plankton.zoo_form.class_form< { @@ -196,13 +162,10 @@ lib_plankton.zoo_page.register( */ "input": new lib_plankton.zoo_input.class_input_wrapped, Array>( new _espe.class_input_set( - groups_as_array.map( - group_thingy => group_thingy.id.toFixed(0) + group_data.order.map( + group_id => group_id.toFixed(0) ), - group_id_encoded => groups_as_map.get(parseInt(group_id_encoded)).name, - { - "read_only": (! data.groups_changeable), - } + group_id_encoded => group_data.pool.get(parseInt(group_id_encoded)).label ), (value_inner) => { const array : Array = []; diff --git a/source/pages/member_view/logic.ts b/source/pages/member_view/logic.ts index 3997c56..6f43dc0 100644 --- a/source/pages/member_view/logic.ts +++ b/source/pages/member_view/logic.ts @@ -22,41 +22,7 @@ lib_plankton.zoo_page.register( dom_fragment.querySelector(".member_view-title").textContent = lib_plankton.translate.get("page.member_view.title"); - /** - * @todo cache - */ - const groups_as_array : Array< - { - id : int; - preview : { - name : string; - label : string; - }; - } - > = await _espe.backend.group_list(); - const groups_as_map : Map< - int, - { - name : string; - label : string; - } - > = new Map< - int, - { - name : string; - label : string; - } - >(); - for (const group_thingy of groups_as_array) - { - groups_as_map.set( - group_thingy.id, - { - "name": group_thingy.preview.name, - "label": group_thingy.preview.label, - } - ); - } + const group_data = await _espe.logic.group_data(); const member_data = await _espe.backend.member_get(id); @@ -130,10 +96,10 @@ lib_plankton.zoo_page.register( */ "input": new lib_plankton.zoo_input.class_input_wrapped, Array>( new _espe.class_input_set( - groups_as_array.map( - group_thingy => group_thingy.id.toFixed(0) + group_data.order.map( + group_id => group_id.toFixed(0) ), - group_id_encoded => groups_as_map.get(parseInt(group_id_encoded)).label + group_id_encoded => group_data.pool.get(parseInt(group_id_encoded)).label ), (value_inner) => { const array : Array = []; diff --git a/tools/makefile b/tools/makefile index cce13e5..e1ffd30 100644 --- a/tools/makefile +++ b/tools/makefile @@ -39,6 +39,7 @@ ${dir_temp}/logic-unlinked.js: \ ${dir_source}/logic/input_set.ts \ ${dir_source}/resources/backend.ts \ ${dir_source}/resources/conf.ts \ + ${dir_source}/logic/logic.ts \ ${dir_source}/pages/index/logic.ts \ ${dir_source}/pages/login/logic.ts \ ${dir_source}/pages/logout/logic.ts \ diff --git a/tools/update-plankton b/tools/update-plankton index ec989b2..fbc3aa6 100755 --- a/tools/update-plankton +++ b/tools/update-plankton @@ -12,6 +12,7 @@ modules="${modules} base64" modules="${modules} string" modules="${modules} translate" modules="${modules} storage" +modules="${modules} cache" modules="${modules} zoo-input" modules="${modules} zoo-form" modules="${modules} zoo-search"