693 lines
14 KiB
TypeScript
693 lines
14 KiB
TypeScript
/*
|
|
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/>.
|
|
*/
|
|
|
|
|
|
namespace _dali.model
|
|
{
|
|
|
|
/**
|
|
*/
|
|
type type_state = {
|
|
groups : Array<
|
|
{
|
|
id : _dali.type_group_id;
|
|
object : _dali.type_group_object;
|
|
}
|
|
>;
|
|
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,
|
|
]
|
|
)
|
|
)
|
|
),
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*/
|
|
export async function group_list(
|
|
)
|
|
: Promise<
|
|
Array<
|
|
{
|
|
id : _dali.type_group_id;
|
|
object : _dali.type_group_object;
|
|
}
|
|
>
|
|
>
|
|
{
|
|
return Promise.resolve(_state.groups);
|
|
}
|
|
|
|
|
|
/**
|
|
*/
|
|
export async function user_list(
|
|
)
|
|
: Promise<
|
|
Array<
|
|
{
|
|
id : _dali.type_user_id;
|
|
name : string;
|
|
}
|
|
>
|
|
>
|
|
{
|
|
return Promise.resolve(_state.users);
|
|
}
|
|
|
|
|
|
/**
|
|
*/
|
|
async function sync_calendars(
|
|
)
|
|
: 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),
|
|
"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(
|
|
lib_plankton.map.hashmap.make(
|
|
x => x.toFixed(0),
|
|
{
|
|
"pairs": (
|
|
data.access.attributed_user
|
|
.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),
|
|
"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)
|
|
.map(
|
|
(pair) => (
|
|
{
|
|
"user_id": pair.key,
|
|
"level": _dali.access_level_encode(pair.value),
|
|
}
|
|
)
|
|
)
|
|
),
|
|
},
|
|
/**
|
|
* @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),
|
|
"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)
|
|
.map(
|
|
(pair) => ({
|
|
"user_id": pair.key,
|
|
"level": _dali.access_level_encode(pair.value),
|
|
})
|
|
)
|
|
),
|
|
},
|
|
}
|
|
);
|
|
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 = {
|
|
"groups": [],
|
|
"users": [],
|
|
"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 () => {
|
|
_state.groups = (
|
|
(await _dali.backend.group_list())
|
|
.map(
|
|
entry => (
|
|
{
|
|
"id": entry.id,
|
|
"object": {
|
|
"name": entry.name,
|
|
"label": entry.label,
|
|
}
|
|
}
|
|
)
|
|
)
|
|
);
|
|
_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 () => {
|
|
_state.groups = [];
|
|
_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();
|
|
}
|
|
|
|
}
|