diff --git a/source/base/functions.ts b/source/base/functions.ts new file mode 100644 index 0000000..274127d --- /dev/null +++ b/source/base/functions.ts @@ -0,0 +1,63 @@ +namespace _dali +{ + + + /** + */ + export function view_mode_encode( + mode : _dali.type.enum_view_mode + ) : string + { + switch (mode) + { + case _dali.type.enum_view_mode.week: {return "week"; break;} + case _dali.type.enum_view_mode.list: {return "list"; break;} + default: {throw (new Error("invalid mode"));} + } + } + + + /** + */ + export function view_mode_decode( + view_mode_encoded : string + ) : _dali.type.enum_view_mode + { + const map : Record = { + "week": _dali.type.enum_view_mode.week, + "list": _dali.type.enum_view_mode.list, + }; + if (! (view_mode_encoded in map)) + { + throw (new Error("invalid mode: " + view_mode_encoded)); + } + else + { + return map[view_mode_encoded]; + } + } + + + /** + */ + export function view_mode_determine( + mode_descriptor : string + ) : _dali.type.enum_view_mode + { + if (mode_descriptor === "auto") + { + return ( + (window.innerWidth >= 800) + ? + _dali.type.enum_view_mode.week + : + _dali.type.enum_view_mode.list + ); + } + else + { + return view_mode_decode(mode_descriptor); + } + } + +} diff --git a/source/base/types.ts b/source/base/types.ts index 674f72f..565a258 100644 --- a/source/base/types.ts +++ b/source/base/types.ts @@ -111,4 +111,13 @@ namespace _dali.type resource : resource_object; }; + + /** + */ + export enum enum_view_mode + { + week, + list, + } + } diff --git a/source/base/widget.ts b/source/base/widget.ts index 9e7542b..f971ca8 100644 --- a/source/base/widget.ts +++ b/source/base/widget.ts @@ -1,4 +1,4 @@ -namespace _zeitbild +namespace _dali { /** diff --git a/source/main.ts b/source/main.ts index cb10981..1713e10 100644 --- a/source/main.ts +++ b/source/main.ts @@ -4,6 +4,37 @@ namespace _dali { + /** + */ + function nav_groups( + logged_in : boolean + ) : Array + { + return ( + logged_in + ? + ["logged_in"] + : + ["logged_out"] + ); + } + + + /** + * @todo reload page when switching to "logged_out" + */ + async function update( + ) : Promise + { + lib_plankton.log.debug( + "dali.update" + ); + const logged_in : boolean = await _dali.backend.is_logged_in(); + lib_plankton.zoo_page.nav_set_groups(nav_groups(logged_in)); + // lib_plankton.zoo_page.reload(); + } + + /** */ export async function main( @@ -39,34 +70,50 @@ namespace _dali "fallback": { "name": "overview", "parameters": {} - } + }, + "nav_entries": [ + { + "location": {"name": "login", "parameters": {}}, + "label": lib_plankton.translate.get("page.login.title"), + "groups": ["logged_out"], + }, + { + "location": {"name": "overview", "parameters": {}}, + "label": lib_plankton.translate.get("page.overview.title"), + "groups": ["logged_out", "logged_in"], + }, + { + "location": {"name": "calendar_add", "parameters": {}}, + "label": lib_plankton.translate.get("page.calendar_add.title"), + "groups": ["logged_in"], + }, + { + "location": {"name": "caldav", "parameters": {}}, + "label": lib_plankton.translate.get("page.caldav.title"), + "groups": ["logged_in"], + }, + /* + { + "location": {"name": "event_add", "parameters": {}}, + "label": lib_plankton.translate.get("page.event_add.title"), + "groups": ["logged_in"], + } + */ + { + "location": {"name": "logout", "parameters": {}}, + "label": lib_plankton.translate.get("page.logout.title"), + "groups": ["logged_in"], + }, + ], + "nav_initial_groups": [], } ); - lib_plankton.zoo_page.add_nav_entry( - {"name": "login", "parameters": {}}, - {"label": lib_plankton.translate.get("page.login.title")} - ); - lib_plankton.zoo_page.add_nav_entry( - {"name": "overview", "parameters": {}}, - {"label": lib_plankton.translate.get("page.overview.title")} - ); - lib_plankton.zoo_page.add_nav_entry( - {"name": "calendar_add", "parameters": {}}, - {"label": lib_plankton.translate.get("page.calendar_add.title")} - ); - lib_plankton.zoo_page.add_nav_entry( - {"name": "caldav", "parameters": {}}, - {"label": lib_plankton.translate.get("page.caldav.title")} - ); - /* - lib_plankton.zoo_page.add_nav_entry( - {"name": "event_add", "parameters": {}}, - {"label": lib_plankton.translate.get("page.event_add.title")} - ); - */ - lib_plankton.zoo_page.add_nav_entry( - {"name": "logout", "parameters": {}}, - {"label": lib_plankton.translate.get("page.logout.title")} + await update(); + lib_plankton.call.loop( + () => { + update(); + }, + _dali.conf.get().misc.update_interval ); // exec diff --git a/source/pages/login/logic.ts b/source/pages/login/logic.ts index c5ff23d..3c98c3c 100644 --- a/source/pages/login/logic.ts +++ b/source/pages/login/logic.ts @@ -12,8 +12,10 @@ namespace _dali.pages "oidc_redirect_uri_template": _dali.conf.get()["misc"]["oidc_redirect_uri_template"], } ); - switch (preparation.kind) { - case "internal": { + switch (preparation.kind) + { + case "internal": + { target_element.innerHTML = await _dali.helpers.template_coin( "login", "default", @@ -51,11 +53,13 @@ namespace _dali.pages "target": "submit", "procedure": async (get_value, get_representation) => { const value : any = await get_value(); - try { + try + { await _dali.backend.session_begin( value.name, value.password ); + lib_plankton.zoo_page.nav_set_groups(["logged_in"]); lib_plankton.zoo_page.set( { "name": "overview", @@ -63,7 +67,8 @@ namespace _dali.pages } ); } - catch (error) { + catch (error) + { lib_plankton.zoo_page.set( { "name": "login", @@ -86,7 +91,8 @@ namespace _dali.pages ); break; } - case "oidc": { + case "oidc": + { let element_a : HTMLElement = document.createElement("a");; element_a.textContent = lib_plankton.string.coin( lib_plankton.translate.get("page.login.oidc.via"), @@ -99,7 +105,8 @@ namespace _dali.pages target_element.appendChild(element_a); break; } - default: { + default: + { break; } } diff --git a/source/pages/logout/logic.ts b/source/pages/logout/logic.ts index 78ad65e..43388d1 100644 --- a/source/pages/logout/logic.ts +++ b/source/pages/logout/logic.ts @@ -7,8 +7,21 @@ namespace _dali.pages "logout", async (parameters, target_element) => { target_element.innerHTML = ""; - await _dali.backend.session_end( - ); + try + { + await _dali.backend.session_end( + ); + } + catch (error) + { + lib_plankton.log.notice( + "dali.logout_failed", + { + "details": String(error), + } + ); + } + lib_plankton.zoo_page.nav_set_groups(["logged_out"]); lib_plankton.zoo_page.set( { "name": "overview", diff --git a/source/pages/overview/logic.ts b/source/pages/overview/logic.ts index 784e585..b8898e4 100644 --- a/source/pages/overview/logic.ts +++ b/source/pages/overview/logic.ts @@ -7,21 +7,15 @@ namespace _dali.pages.overview "overview", async (parameters, target_element) => { // params - const compact : boolean = ( - ( - parameters["compact"] - ?? - ( - (window.innerWidth >= 800) - ? - "no" - : - "yes" - ) - ) - === - "yes" - ); + const view_mode : _dali.type.enum_view_mode = view_mode_determine(parameters["mode"] ?? "auto"); + + /** + * @todo ordentlich machen (nicht nur week und list) + */ + const set_view_mode = (view_mode) => { + const compact : boolean = (view_mode !== _dali.type.enum_view_mode.week); + target_element.querySelector("#overview").classList.toggle("overview-compact", compact); + }; // exec target_element.innerHTML = await _dali.helpers.template_coin( @@ -30,15 +24,49 @@ namespace _dali.pages.overview { } ); - target_element.querySelector("#overview").classList.toggle("overview-compact", compact); + + // mode switcher + { + const widget_mode_switcher : _dali.class_widget = new _dali.widgets.mode_switcher.class_widget_mode_switcher( + [ + { + "mode": _dali.type.enum_view_mode.week, + "label": lib_plankton.translate.get("page.overview.mode.week"), + }, + { + "mode": _dali.type.enum_view_mode.list, + "label": lib_plankton.translate.get("page.overview.mode.list"), + }, + ], + { + "initial_selection": view_mode, + "action_change": (view_mode) => { + lib_plankton.zoo_page.set( + { + "name": "overview", + "parameters": { + "mode": _dali.view_mode_encode(view_mode), + } + } + ); + set_view_mode(view_mode); + } + } + ); + set_view_mode(view_mode); + await widget_mode_switcher.load(target_element.querySelector("#overview-mode")); + } + let widget_weekview : _dali.widgets.weekview.class_widget_weekview; let widget_listview : _dali.widgets.listview.class_widget_listview; // hint { - if (! await _dali.backend.is_logged_in()) { - target_element.querySelector("#overview-head").textContent = lib_plankton.translate.get("page.overview.login_hint"); + if (! await _dali.backend.is_logged_in()) + { + target_element.querySelector("#overview-hint").textContent = lib_plankton.translate.get("page.overview.login_hint"); } - else { + else + { // do nothing } } @@ -56,13 +84,16 @@ namespace _dali.pages.overview data, { "action_open": (entry) => { - switch (entry.access_level) { - case _dali.type.enum_access_level.none: { + switch (entry.access_level) + { + case _dali.type.enum_access_level.none: + { throw (new Error("this event should not be visible")); break; } case _dali.type.enum_access_level.edit: - case _dali.type.enum_access_level.view: { + case _dali.type.enum_access_level.view: + { lib_plankton.zoo_page.set( { "name": "calendar_edit", @@ -74,7 +105,8 @@ namespace _dali.pages.overview ); break; } - case _dali.type.enum_access_level.admin: { + case _dali.type.enum_access_level.admin: + { lib_plankton.zoo_page.set( { "name": "calendar_edit", @@ -90,6 +122,7 @@ namespace _dali.pages.overview }, "action_toggle_visibility": (entry) => { widget_weekview.toggle_visibility(entry.id); + widget_listview.toggle_visibility(entry.id); }, } ); @@ -105,12 +138,15 @@ namespace _dali.pages.overview } ); const action_select_event = (calendar_id, access_level, event_id) => { - switch (access_level) { - case _dali.type.enum_access_level.none: { + switch (access_level) + { + case _dali.type.enum_access_level.none: + { throw (new Error("this event should not be visible")); break; } - case _dali.type.enum_access_level.view: { + case _dali.type.enum_access_level.view: + { lib_plankton.zoo_page.set( { "name": "event_edit", @@ -124,7 +160,8 @@ namespace _dali.pages.overview break; } case _dali.type.enum_access_level.edit: - case _dali.type.enum_access_level.admin: { + case _dali.type.enum_access_level.admin: + { lib_plankton.zoo_page.set( { "name": "event_edit", diff --git a/source/pages/overview/templates/default.html.tpl b/source/pages/overview/templates/default.html.tpl index 237c7c9..aeb85a8 100644 --- a/source/pages/overview/templates/default.html.tpl +++ b/source/pages/overview/templates/default.html.tpl @@ -1,5 +1,9 @@
+
+
+
+
diff --git a/source/resources/backend.ts b/source/resources/backend.ts index 34ebf1c..f15652a 100644 --- a/source/resources/backend.ts +++ b/source/resources/backend.ts @@ -61,24 +61,17 @@ namespace _dali.backend async function get_session_key( ) : Promise<(null | string)> { - try { + try + { return (await _data_chest.read("session_key")); } - catch (error) { + catch (error) + { return null; } } - /** - */ - export async function is_logged_in( - ) : Promise - { - return ((await get_session_key()) !== null); - } - - /** */ async function call( @@ -166,6 +159,23 @@ namespace _dali.backend return Promise.resolve(output); } } + + + /** + */ + export async function is_logged_in( + ) : Promise + { + // return ((await get_session_key()) !== null); + const result : { + logged_in : boolean; + } = await call( + lib_plankton.http.enum_method.get, + "/session/status", + null + ); + return result.logged_in; + } /** diff --git a/source/resources/conf.ts b/source/resources/conf.ts index 58737c3..396022d 100644 --- a/source/resources/conf.ts +++ b/source/resources/conf.ts @@ -57,6 +57,11 @@ namespace _dali.conf "type": "boolean", "default": false }, + "update_interval": { + "nullable": false, + "type": "integer", + "default": 60 + }, }, "required": [ ], @@ -90,7 +95,8 @@ namespace _dali.conf export function get( ) : any { - if (_data === null) { + if (_data === null) + { throw (new Error("conf not loaded yet")); } else { diff --git a/source/style/main.css b/source/style/main.css index 1cf4e9b..e1d417e 100644 --- a/source/style/main.css +++ b/source/style/main.css @@ -34,6 +34,11 @@ nav > ul > li padding: 8px; } +nav > ul > li:not(.active) +{ + display: none; +} + nav a { padding: 8px; diff --git a/source/style/page-overview.css b/source/style/page-overview.css index 5c5ffe0..fa2c45c 100644 --- a/source/style/page-overview.css +++ b/source/style/page-overview.css @@ -1,15 +1,21 @@ #overview-head +{ + padding-bottom: 12px; + margin-bottom: 12px; + border-bottom: 2px solid; +} + +#overview-hint { text-align: center; font-weight: bold; - margin-bottom: 12px; } #overview-body { display: flex; flex-direction: row; - flex-wrap: wrap; + flex-wrap: nowrap; } #overview-body #overview-pane-left @@ -24,40 +30,12 @@ flex-shrink: 1; } -@media all and (max-width: 950px) -{ - #overview #overview-pane-left - { - flex-basis: 0%; - } +#overview.overview-compact #overview-pane-left {flex-basis: 25%;} +#overview.overview-compact #overview-pane-right {flex-basis: 75%;} +#overview.overview-compact #overview-pane-right-listview {} +#overview.overview-compact #overview-pane-right-weekview {display: none;} - #overview #overview-pane-left > * - { - display: none; - } - - #overview #overview-pane-right - { - flex-basis: 100%; - } - - #overview #overview-pane-right-listview {} - #overview #overview-pane-right-weekview {display: none;} -} - -@media not all and (max-width: 950px) -{ - #overview #overview-pane-left - { - flex-basis: 12.5%; - } - - #overview #overview-pane-right - { - flex-basis: 87.5%; - } - - #overview #overview-pane-right-listview {display: none;} - #overview #overview-pane-right-weekview {} - -} +#overview:not(.overview-compact) #overview-pane-left {flex-basis: 12.5%;} +#overview:not(.overview-compact) #overview-pane-right {flex-basis: 87.5%;} +#overview:not(.overview-compact) #overview-pane-right-listview {display: none;} +#overview:not(.overview-compact) #overview-pane-right-weekview {} diff --git a/source/style/widget-listview.css b/source/style/widget-listview.css index cce2eb5..f955d12 100644 --- a/source/style/widget-listview.css +++ b/source/style/widget-listview.css @@ -26,6 +26,11 @@ cursor: pointer; } +.listview-entry-hidden +{ + display: none; +} + .listview-entry-name { font-weight: bold; diff --git a/source/widgets/listview/logic.ts b/source/widgets/listview/logic.ts index 4842011..2de12ef 100644 --- a/source/widgets/listview/logic.ts +++ b/source/widgets/listview/logic.ts @@ -27,7 +27,7 @@ namespace _dali.widgets.listview /** */ - export class class_widget_listview extends _zeitbild.class_widget + export class class_widget_listview extends _dali.class_widget { /** @@ -35,6 +35,11 @@ namespace _dali.widgets.listview private get_entries : type_get_entries; + /** + */ + private container : (null | Element); + + /** */ private action_select_event : ( @@ -90,11 +95,34 @@ namespace _dali.widgets.listview ); super(); this.get_entries = get_entries; + this.container = null; this.action_select_event = options.action_select_event; this.action_add = options.action_add; } + /** + */ + public toggle_visibility( + calendar_id : _dali.type.calendar_id + ) : void + { + this.container.querySelectorAll(".listview-entry").forEach( + (element) => { + const rel : string = element.getAttribute("rel"); + const parts : Array = rel.split("/"); + const calendar_id_ : _dali.type.calendar_id = parseInt(parts[0]); + if (! (calendar_id === calendar_id_)) { + // do nothing + } + else { + element.classList.toggle("listview-entry-hidden"); + } + } + ); + } + + /** * [implementation] */ @@ -289,6 +317,8 @@ namespace _dali.widgets.listview ); } + this.container = target_element.querySelector(".listview"); + return Promise.resolve(undefined); } diff --git a/source/widgets/mode_switcher/logic.ts b/source/widgets/mode_switcher/logic.ts new file mode 100644 index 0000000..50a8e53 --- /dev/null +++ b/source/widgets/mode_switcher/logic.ts @@ -0,0 +1,119 @@ +namespace _dali.widgets.mode_switcher +{ + + /** + */ + type type_option = { + mode : _dali.type.enum_view_mode; + label : string, + }; + + + /** + */ + export class class_widget_mode_switcher extends _dali.class_widget + { + + /** + */ + private options : Array; + + + /** + */ + private initial_selection : (null | _dali.type.enum_view_mode); + + + /** + */ + private action_change : (null | ((mode : _dali.type.enum_view_mode) => void)); + + + /** + */ + public constructor( + options : Array, + { + "initial_selection": initial_selection = null, + "action_change": action_change = null, + } + : + { + initial_selection ?: (null | _dali.type.enum_view_mode), + action_change ?: (null | ((mode : _dali.type.enum_view_mode) => void)) + } + = + { + } + ) + { + super(); + this.options = options; + this.initial_selection = initial_selection; + this.action_change = action_change; + } + + + /** + * [implementation] + */ + public async load( + target_element : Element + ) : Promise + { + target_element.innerHTML = await _dali.helpers.template_coin( + "widget-mode_switcher", + "main", + { + "options": (await lib_plankton.call.promise_condense( + this.options.map( + option => () => _dali.helpers.template_coin( + "widget-mode_switcher", + "option", + { + "rel": _dali.view_mode_encode(option.mode), + "value": _dali.view_mode_encode(option.mode), + "label": option.label, + } + ) + ) + )).join(""), + } + ); + // initial selection + { + if (this.initial_selection !== null) + { + const selector : string = lib_plankton.string.coin( + ".widget-mode_switcher-option[rel=\"{{rel}}\"] > input", + { + "rel": _dali.view_mode_encode(this.initial_selection) + } + ); + (target_element.querySelector(selector) as HTMLInputElement).checked = true; + } + } + // set listeners + { + if (this.action_change !== null) + { + target_element.querySelectorAll(".widget-mode_switcher-option").forEach( + element => { + const view_mode_encoded : string = element.getAttribute("rel"); + const mode : _dali.type.enum_view_mode = _dali.view_mode_decode(view_mode_encoded); + element.querySelector("input").addEventListener( + "change", + (event) => { + this.action_change(mode); + } + ); + } + ); + } + } + return Promise.resolve(undefined); + } + + } + +} diff --git a/source/widgets/mode_switcher/templates/main.html.tpl b/source/widgets/mode_switcher/templates/main.html.tpl new file mode 100644 index 0000000..3919b0b --- /dev/null +++ b/source/widgets/mode_switcher/templates/main.html.tpl @@ -0,0 +1,3 @@ +
+{{options}} +
diff --git a/source/widgets/mode_switcher/templates/option.html.tpl b/source/widgets/mode_switcher/templates/option.html.tpl new file mode 100644 index 0000000..ae61485 --- /dev/null +++ b/source/widgets/mode_switcher/templates/option.html.tpl @@ -0,0 +1,4 @@ + diff --git a/source/widgets/sources/logic.ts b/source/widgets/sources/logic.ts index ad0a03a..270387d 100644 --- a/source/widgets/sources/logic.ts +++ b/source/widgets/sources/logic.ts @@ -12,7 +12,7 @@ namespace _dali.widgets.sources /** */ - export class class_widget_sources extends _zeitbild.class_widget + export class class_widget_sources extends _dali.class_widget { /** diff --git a/source/widgets/weekview/logic.ts b/source/widgets/weekview/logic.ts index 49322a1..9ca8644 100644 --- a/source/widgets/weekview/logic.ts +++ b/source/widgets/weekview/logic.ts @@ -27,7 +27,7 @@ namespace _dali.widgets.weekview /** */ - export class class_widget_weekview extends _zeitbild.class_widget + export class class_widget_weekview extends _dali.class_widget { /** @@ -832,7 +832,7 @@ namespace _dali.widgets.weekview /** */ public toggle_visibility( - calendar_id: _dali.type.calendar_id + calendar_id : _dali.type.calendar_id ) : void { this.container.querySelectorAll(".weekview-event_entry").forEach( diff --git a/tools/makefile b/tools/makefile index 38ff62a..3195a2b 100644 --- a/tools/makefile +++ b/tools/makefile @@ -35,6 +35,7 @@ templates: \ templates-widgets-sources \ templates-widgets-listview \ templates-widgets-weekview \ + templates-widgets-mode_switcher \ templates-pages-caldav \ templates-pages-calendar_add \ templates-pages-calendar_edit \ @@ -64,6 +65,13 @@ templates-widgets-weekview: \ @ ${cmd_mkdir} ${dir_build}/templates/widget-weekview @ ${cmd_cp} -r -u -v ${dir_source}/widgets/weekview/templates/* ${dir_build}/templates/widget-weekview/ +.PHONY: templates-widgets-mode_switcher +templates-widgets-mode_switcher: \ + $(wildcard ${dir_source}/widgets/mode_switcher/templates/*) + @ ${cmd_log} "templates:widgets:mode_switcher …" + @ ${cmd_mkdir} ${dir_build}/templates/widget-mode_switcher + @ ${cmd_cp} -r -u -v ${dir_source}/widgets/mode_switcher/templates/* ${dir_build}/templates/widget-mode_switcher/ + .PHONY: templates-pages-caldav templates-pages-caldav: \ $(wildcard ${dir_source}/pages/caldav/templates/*) @@ -128,11 +136,13 @@ ${dir_temp}/logic-unlinked.js: \ ${dir_source}/base/helpers.ts \ ${dir_source}/base/widget.ts \ ${dir_source}/base/types.ts \ + ${dir_source}/base/functions.ts \ ${dir_source}/resources/conf.ts \ ${dir_source}/resources/backend.ts \ ${dir_source}/widgets/sources/logic.ts \ ${dir_source}/widgets/listview/logic.ts \ ${dir_source}/widgets/weekview/logic.ts \ + ${dir_source}/widgets/mode_switcher/logic.ts \ ${dir_source}/pages/login/logic.ts \ ${dir_source}/pages/logout/logic.ts \ ${dir_source}/pages/caldav/logic.ts \