frontend-dali/source/model.ts

693 lines
14 KiB
TypeScript
Raw Normal View History

2025-10-23 23:16:11 +02:00
/*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« 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.
»dali« 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 »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
2025-10-17 00:10:28 +02:00
namespace _dali.model
{
/**
*/
type type_state = {
2025-10-23 11:35:57 +02:00
groups : Array<
{
id : _dali.type_group_id;
object : _dali.type_group_object;
}
>;
2025-10-17 00:10:28 +02:00
users : Array<
{
id : _dali.type_user_id;
name : string;
}
>;
calendars : (
null
|
lib_plankton.map.type_map<
_dali.type_calendar_id,
{
reduced : _dali.type_calendar_object_reduced;
complete : (null | _dali.type_calendar_object);
}
>
);
events : lib_plankton.map.type_map<
_dali.type_event_key,
_dali.type_event_object_extended
>;
covered_dates : lib_plankton.set.type_set<
lib_plankton.pit.type_date
>;
};
/**
*/
let _state : (null | type_state) = null;
/**
*/
let _listeners_reset : Array<((priviliged ?: boolean) => Promise<void>)> = [];
/**
*/
function make_date_set(
{
"elements": elements = [],
}
:
{
elements ?: Array<lib_plankton.pit.type_date>;
}
=
{
}
)
: lib_plankton.set.type_set<lib_plankton.pit.type_date>
{
return lib_plankton.set.hashset.implementation_set(
lib_plankton.set.hashset.make(
x => lib_plankton.pit.date_format(x),
{
/**
* @todo häääh!?
*/
"elements": elements.filter(x => (x !== null)),
}
)
);
}
/**
*/
function timeframe_to_date_set(
timeframe : {
from : lib_plankton.pit.type_pit;
to : lib_plankton.pit.type_pit;
}
)
: lib_plankton.set.type_set<lib_plankton.pit.type_date>
{
if (! lib_plankton.pit.is_before(timeframe.from, timeframe.to))
{
throw (new Error("invalid timeframe"));
}
else
{
const heuristic : int = Math.ceil((timeframe.to - timeframe.from) / (60 * 60 * 24));
let pit_current : lib_plankton.pit.type_pit = timeframe.from;
return make_date_set(
{
"elements": (
lib_plankton.list.sequence(heuristic)
.map(
(offset) => lib_plankton.call.convey(
timeframe.from,
[
x => lib_plankton.pit.shift_day(x, offset),
x => lib_plankton.pit.to_datetime(x),
x => x.date,
]
)
)
),
}
);
}
}
2025-10-23 11:35:57 +02:00
/**
*/
export async function group_list(
)
: Promise<
Array<
{
id : _dali.type_group_id;
object : _dali.type_group_object;
}
>
>
{
return Promise.resolve(_state.groups);
}
2025-10-17 00:10:28 +02:00
/**
*/
export async function user_list(
)
: Promise<
Array<
{
id : _dali.type_user_id;
name : string;
}
>
>
{
return Promise.resolve(_state.users);
}
/**
*/
async function sync_calendars(
2025-10-17 00:10:28 +02:00
)
: Promise<void>
{
const data = await _dali.backend.calendar_list();
lib_plankton.map.clear(_state.calendars);
data.forEach(
entry => {
_state.calendars.set(
entry.id,
{
"reduced": {
"name": entry.name,
"hue": entry.hue,
"access_level": _dali.access_level_decode(entry.access_level),
},
"complete": null,
}
);
}
);
}
/**
*/
export function calendar_list(
)
: Promise<
Array<
_dali.type_calendar_object_reduced_with_id
>
>
{
return Promise.resolve(
lib_plankton.map.dump(_state.calendars)
.map(
pair => (
{
"id": pair.key,
"name": pair.value.reduced.name,
"hue": pair.value.reduced.hue,
"access_level": pair.value.reduced.access_level,
}
)
)
);
}
/**
*/
export async function calendar_get(
calendar_id : _dali.type_calendar_id
)
: Promise<_dali.type_calendar_object>
{
const value = _state.calendars.get(calendar_id);
if (value.complete === null)
{
const data = await _dali.backend.calendar_get(calendar_id);
const calendar_object : _dali.type_calendar_object = {
"name": data.name,
"hue": data.hue,
"access": {
"public": data.access.public,
"default_level": _dali.access_level_decode(data.access.default_level),
2025-10-23 11:35:57 +02:00
"attributed_group": lib_plankton.map.hashmap.implementation_map(
lib_plankton.map.hashmap.make(
x => x.toFixed(0),
{
"pairs": (
data.access.attributed_group
.map(
(entry) => (
{
"key": entry.group_id,
"value": _dali.access_level_decode(entry.level),
}
)
)
),
}
)
),
"attributed_user": lib_plankton.map.hashmap.implementation_map(
2025-10-17 00:10:28 +02:00
lib_plankton.map.hashmap.make(
x => x.toFixed(0),
{
"pairs": (
2025-10-23 11:35:57 +02:00
data.access.attributed_user
2025-10-17 00:10:28 +02:00
.map(
(entry) => (
{
"key": entry.user_id,
"value": _dali.access_level_decode(entry.level),
}
)
)
),
}
)
),
},
"resource_id": data.resource_id,
};
value.complete = calendar_object;
/**
* @todo set in map?
*/
return calendar_object;
}
else
{
return value.complete;
}
}
/**
*/
export async function calendar_add(
calendar_object : _dali.type_calendar_object
)
: Promise<void>
{
const calendar_id : _dali.type_calendar_id = await _dali.backend.calendar_add(
{
"name": calendar_object.name,
"hue": calendar_object.hue,
"access": {
"public": calendar_object.access.public,
"default_level": _dali.access_level_encode(calendar_object.access.default_level),
2025-10-23 11:35:57 +02:00
"attributed_group": (
lib_plankton.map.dump(calendar_object.access.attributed_group)
.map(
(pair) => (
{
"group_id": pair.key,
"level": _dali.access_level_encode(pair.value),
}
)
)
),
"attributed_user": (
lib_plankton.map.dump(calendar_object.access.attributed_user)
2025-10-17 00:10:28 +02:00
.map(
(pair) => (
{
"user_id": pair.key,
"level": _dali.access_level_encode(pair.value),
}
)
)
2025-10-23 11:35:57 +02:00
),
2025-10-17 00:10:28 +02:00
},
/**
* @todo
*/
"resource": {
"kind": "local",
"data": {
"events": [],
}
},
}
);
const calendar_object_reduced : _dali.type_calendar_object_reduced = {
"name": calendar_object.name,
"hue": calendar_object.hue,
"access_level": _dali.enum_access_level.admin,
};
_state.calendars.set(
calendar_id,
{
"reduced": calendar_object_reduced,
"complete": calendar_object,
}
);
}
/**
*/
export async function calendar_change(
calendar_id : _dali.type_calendar_id,
calendar_object : _dali.type_calendar_object
)
: Promise<void>
{
/*await */ _dali.backend.calendar_change(
calendar_id,
{
"name": calendar_object.name,
"hue": calendar_object.hue,
"access": {
"public": calendar_object.access.public,
"default_level": _dali.access_level_encode(calendar_object.access.default_level),
2025-10-23 11:35:57 +02:00
"attributed_group": (
lib_plankton.map.dump(calendar_object.access.attributed_group)
.map(
(pair) => ({
"group_id": pair.key,
"level": _dali.access_level_encode(pair.value),
})
)
),
"attributed_user": (
lib_plankton.map.dump(calendar_object.access.attributed_user)
2025-10-17 00:10:28 +02:00
.map(
(pair) => ({
"user_id": pair.key,
"level": _dali.access_level_encode(pair.value),
})
)
2025-10-23 11:35:57 +02:00
),
2025-10-17 00:10:28 +02:00
},
}
);
const calendar_object_reduced : _dali.type_calendar_object_reduced = {
"name": calendar_object.name,
"hue": calendar_object.hue,
/**
* @todo
*/
"access_level": _dali.enum_access_level.admin,
};
_state.calendars.set(
calendar_id,
{
"reduced": calendar_object_reduced,
"complete": calendar_object,
}
);
{
lib_plankton.map.clear(_state.events);
lib_plankton.set.clear(_state.covered_dates);
notify_reset();
}
}
/**
*/
export async function calendar_remove(
calendar_id : _dali.type_calendar_id
)
: Promise<void>
{
/*await */ _dali.backend.calendar_remove(calendar_id);
_state.calendars.delete(calendar_id);
{
lib_plankton.map.clear(_state.events);
lib_plankton.set.clear(_state.covered_dates);
notify_reset();
}
}
/**
* @todo do NOT export?
* @todo clear?
* @todo heed calendar_ids
* @todo mutex
* @todo only update outside timeframe
*/
export async function sync_events(
timeframe : {
from : lib_plankton.pit.type_pit;
to : lib_plankton.pit.type_pit;
},
{
"calendar_ids": calendar_ids = null,
}
:
{
calendar_ids ?: (null | Array<_dali.type_calendar_id>);
}
=
{
}
)
: Promise<void>
{
const queried_dates : lib_plankton.set.type_set<lib_plankton.pit.type_date> = timeframe_to_date_set(
timeframe
);
const new_dates : lib_plankton.set.type_set<lib_plankton.pit.type_date> = lib_plankton.set.difference(
() => make_date_set(),
queried_dates,
_state.covered_dates
);
if (lib_plankton.set.empty(new_dates))
{
// do nothing
}
else
{
const result = await _dali.backend.events(
timeframe.from,
timeframe.to,
{
"calendar_ids": calendar_ids,
}
);
result.forEach(
entry => {
_state.events.set(
entry.hash,
{
"key": entry.hash,
"calendar_id": entry.calendar_id,
"calendar_name": entry.calendar_name,
"hue": entry.hue,
"access_level": _dali.access_level_decode(entry.access_level),
"event_id": entry.event_id,
"event_object": entry.event_object
}
);
}
);
_state.covered_dates = lib_plankton.set.union(
() => make_date_set(),
_state.covered_dates,
new_dates
);
}
}
/**
*/
export function event_list(
)
: Promise<Array<_dali.type_event_object_extended>>
{
return Promise.resolve(lib_plankton.map.values(_state.events));
}
/**
*/
export async function event_get(
event_key : _dali.type_event_key
)
: Promise<_dali.type_event_object_extended>
{
return Promise.resolve(_state.events.get(event_key));
}
/**
*/
export async function event_add(
calendar_id : _dali.type_calendar_id,
event_object : _dali.type_event_object
)
: Promise<void>
{
/**
* @todo do NOT wait?
*/
const result = await _dali.backend.calendar_event_add(
calendar_id,
event_object
);
const event_id : _dali.type_local_resource_event_id = result.local_resource_event_id;
const event_key : _dali.type_event_key = result.hash;
const value = _state.calendars.get(calendar_id);
const calendar_object_reduced : _dali.type_calendar_object_reduced = value.reduced;
_state.events.set(
event_key,
{
"key": event_key,
"calendar_id": calendar_id,
"calendar_name": calendar_object_reduced.name,
"hue": calendar_object_reduced.hue,
"access_level": calendar_object_reduced.access_level,
"event_id": event_id,
"event_object": event_object,
}
);
}
/**
*/
export async function event_change(
event_key : _dali.type_event_key,
event_object : _dali.type_event_object
)
: Promise<void>
{
const event_object_extended_old : _dali.type_event_object_extended = _state.events.get(event_key);
const event_object_extended_new : _dali.type_event_object_extended = {
"key": event_object_extended_old.key,
"calendar_id": event_object_extended_old.calendar_id,
"calendar_name": event_object_extended_old.calendar_name,
"hue": event_object_extended_old.hue,
"access_level": event_object_extended_old.access_level,
"event_id": event_object_extended_old.event_id,
"event_object": event_object,
};
_state.events.set(
event_key,
event_object_extended_new
);
/*await */_dali.backend.calendar_event_change(
event_object_extended_old.calendar_id,
event_object_extended_old.event_id,
event_object
);
}
/**
*/
export async function event_remove(
event_key : _dali.type_event_key
)
: Promise<void>
{
const event_object_extended : _dali.type_event_object_extended = _state.events.get(event_key);
_state.events.delete(event_key);
/*await */_dali.backend.calendar_event_remove(
event_object_extended.calendar_id,
event_object_extended.event_id
);
}
/**
*/
export function listen_reset(
action : ((priviliged ?: boolean) => Promise<void>)
)
: void
{
_listeners_reset.push(action);
}
/**
*/
async function notify_reset(
priviliged ?: boolean
)
: Promise<void>
{
for (const action of _listeners_reset)
{
await action(priviliged);
}
}
/**
*/
export async function initialize(
)
: Promise<void>
{
_state = {
2025-10-23 11:35:57 +02:00
"groups": [],
"users": [],
2025-10-17 00:10:28 +02:00
"calendars": lib_plankton.map.hashmap.implementation_map(
lib_plankton.map.hashmap.make(
calendar_id => calendar_id.toFixed(0)
)
),
"events": lib_plankton.map.hashmap.implementation_map(
lib_plankton.map.hashmap.make(
event_key => event_key
)
),
"covered_dates": make_date_set(
),
};
_dali.listen_login(
async () => {
2025-10-23 11:35:57 +02:00
_state.groups = (
(await _dali.backend.group_list())
.map(
entry => (
{
"id": entry.id,
"object": {
"name": entry.name,
"label": entry.label,
}
}
)
)
);
2025-10-17 00:10:28 +02:00
_state.users = await _dali.backend.user_list();
await sync_calendars();
lib_plankton.map.clear(_state.events);
lib_plankton.set.clear(_state.covered_dates);
notify_reset(true);
}
);
_dali.listen_logout(
async () => {
2025-10-23 11:35:57 +02:00
_state.groups = [];
2025-10-17 00:10:28 +02:00
_state.users = [];
await sync_calendars();
lib_plankton.map.clear(_state.events);
lib_plankton.set.clear(_state.covered_dates);
notify_reset(false);
}
);
await sync_calendars();
// await sync_events();
}
}