in web interfaces, put crumbs path in document title, for more useful browser history

This commit is contained in:
Mechiel Lukkien 2024-03-16 19:13:44 +01:00
parent dfe587fdeb
commit fdee24f3bd
No known key found for this signature in database
4 changed files with 138 additions and 94 deletions

View file

@ -762,29 +762,39 @@ const localStorageRemove = (k) => {
}; };
const client = new api.Client().withOptions({ csrfHeader: 'x-mox-csrf', login: login }).withAuthToken(localStorageGet('webaccountcsrftoken') || ''); const client = new api.Client().withOptions({ csrfHeader: 'x-mox-csrf', login: login }).withAuthToken(localStorageGet('webaccountcsrftoken') || '');
const link = (href, anchorOpt) => dom.a(attr.href(href), attr.rel('noopener noreferrer'), anchorOpt || href); const link = (href, anchorOpt) => dom.a(attr.href(href), attr.rel('noopener noreferrer'), anchorOpt || href);
const crumblink = (text, link) => dom.a(text, attr.href(link)); const crumblink = (text, path) => {
const crumbs = (...l) => [ return {
dom.div(style({ float: 'right' }), localStorageGet('webaccountaddress') || '(unknown)', ' ', dom.clickbutton('Logout', attr.title('Logout, invalidating this session.'), async function click(e) { text: text,
const b = e.target; path: path
try { };
b.disabled = true; };
await client.Logout(); const crumbs = (...l) => {
} const crumbtext = (e) => typeof e === 'string' ? e : e.text;
catch (err) { document.title = l.map(e => crumbtext(e)).join(' - ');
console.log('logout', err); const crumblink = (e) => typeof e === 'string' ? e : dom.a(e.text, attr.href(e.path));
window.alert('Error: ' + errmsg(err)); return [
} dom.div(style({ float: 'right' }), localStorageGet('webaccountaddress') || '(unknown)', ' ', dom.clickbutton('Logout', attr.title('Logout, invalidating this session.'), async function click(e) {
finally { const b = e.target;
b.disabled = false; try {
} b.disabled = true;
localStorageRemove('webaccountaddress'); await client.Logout();
localStorageRemove('webaccountcsrftoken'); }
// Reload so all state is cleared from memory. catch (err) {
window.location.reload(); console.log('logout', err);
})), window.alert('Error: ' + errmsg(err));
dom.h1(l.map((e, index) => index === 0 ? e : [' / ', e])), }
dom.br() finally {
]; b.disabled = false;
}
localStorageRemove('webaccountaddress');
localStorageRemove('webaccountcsrftoken');
// Reload so all state is cleared from memory.
window.location.reload();
})),
dom.h1(l.map((e, index) => index === 0 ? crumblink(e) : [' / ', crumblink(e)])),
dom.br()
];
};
const errmsg = (err) => '' + (err.message || '(no error message)'); const errmsg = (err) => '' + (err.message || '(no error message)');
const footer = dom.div(style({ marginTop: '6ex', opacity: 0.75 }), link('https://www.xmox.nl', 'mox'), ' ', moxversion); const footer = dom.div(style({ marginTop: '6ex', opacity: 0.75 }), link('https://www.xmox.nl', 'mox'), ' ', moxversion);
const domainName = (d) => { const domainName = (d) => {

View file

@ -103,33 +103,45 @@ const client = new api.Client().withOptions({csrfHeader: 'x-mox-csrf', login: lo
const link = (href: string, anchorOpt: string) => dom.a(attr.href(href), attr.rel('noopener noreferrer'), anchorOpt || href) const link = (href: string, anchorOpt: string) => dom.a(attr.href(href), attr.rel('noopener noreferrer'), anchorOpt || href)
const crumblink = (text: string, link: string) => dom.a(text, attr.href(link)) const crumblink = (text: string, path: string) => {
const crumbs = (...l: ElemArg[]) => [ return {
dom.div( text: text,
style({float: 'right'}), path: path
localStorageGet('webaccountaddress') || '(unknown)', }
' ', }
dom.clickbutton('Logout', attr.title('Logout, invalidating this session.'), async function click(e: MouseEvent) { const crumbs = (...l: ({text: string, path: string} | string)[]) => {
const b = e.target! as HTMLButtonElement const crumbtext = (e: {text: string, path: string} | string) => typeof e === 'string' ? e : e.text
try { document.title = l.map(e => crumbtext(e)).join(' - ')
b.disabled = true
await client.Logout()
} catch (err) {
console.log('logout', err)
window.alert('Error: ' + errmsg(err))
} finally {
b.disabled = false
}
localStorageRemove('webaccountaddress') const crumblink = (e: {text: string, path: string} | string) =>
localStorageRemove('webaccountcsrftoken') typeof e === 'string' ? e : dom.a(e.text, attr.href(e.path))
// Reload so all state is cleared from memory. return [
window.location.reload() dom.div(
}), style({float: 'right'}),
), localStorageGet('webaccountaddress') || '(unknown)',
dom.h1(l.map((e, index) => index === 0 ? e : [' / ', e])), ' ',
dom.br() dom.clickbutton('Logout', attr.title('Logout, invalidating this session.'), async function click(e: MouseEvent) {
] const b = e.target! as HTMLButtonElement
try {
b.disabled = true
await client.Logout()
} catch (err) {
console.log('logout', err)
window.alert('Error: ' + errmsg(err))
} finally {
b.disabled = false
}
localStorageRemove('webaccountaddress')
localStorageRemove('webaccountcsrftoken')
// Reload so all state is cleared from memory.
window.location.reload()
}),
),
dom.h1(l.map((e, index) => index === 0 ? crumblink(e) : [' / ', crumblink(e)])),
dom.br()
]
}
const errmsg = (err: unknown) => ''+((err as any).message || '(no error message)') const errmsg = (err: unknown) => ''+((err as any).message || '(no error message)')

View file

@ -1456,28 +1456,38 @@ const yellow = '#ffe400';
const red = '#ff7443'; const red = '#ff7443';
const blue = '#8bc8ff'; const blue = '#8bc8ff';
const link = (href, anchorOpt) => dom.a(attr.href(href), attr.rel('noopener noreferrer'), anchorOpt || href); const link = (href, anchorOpt) => dom.a(attr.href(href), attr.rel('noopener noreferrer'), anchorOpt || href);
const crumblink = (text, link) => dom.a(text, attr.href(link)); const crumblink = (text, path) => {
const crumbs = (...l) => [ return {
dom.div(style({ float: 'right' }), dom.clickbutton('Logout', attr.title('Logout, invalidating this session.'), async function click(e) { text: text,
const b = e.target; path: path
try { };
b.disabled = true; };
await client.Logout(); const crumbs = (...l) => {
} const crumbtext = (e) => typeof e === 'string' ? e : e.text;
catch (err) { document.title = l.map(e => crumbtext(e)).join(' - ');
console.log('logout', err); const crumblink = (e) => typeof e === 'string' ? e : dom.a(e.text, attr.href(e.path));
window.alert('Error: ' + errmsg(err)); return [
} dom.div(style({ float: 'right' }), dom.clickbutton('Logout', attr.title('Logout, invalidating this session.'), async function click(e) {
finally { const b = e.target;
b.disabled = false; try {
} b.disabled = true;
localStorageRemove('webadmincsrftoken'); await client.Logout();
// Reload so all state is cleared from memory. }
window.location.reload(); catch (err) {
})), console.log('logout', err);
dom.h1(l.map((e, index) => index === 0 ? e : [' / ', e])), window.alert('Error: ' + errmsg(err));
dom.br() }
]; finally {
b.disabled = false;
}
localStorageRemove('webadmincsrftoken');
// Reload so all state is cleared from memory.
window.location.reload();
})),
dom.h1(l.map((e, index) => index === 0 ? crumblink(e) : [' / ', crumblink(e)])),
dom.br()
];
};
const errmsg = (err) => '' + (err.message || '(no error message)'); const errmsg = (err) => '' + (err.message || '(no error message)');
const footer = dom.div(style({ marginTop: '6ex', opacity: 0.75 }), link('https://www.xmox.nl', 'mox'), ' ', moxversion, ' ', moxgoversion, ' ', moxgoos, '/', moxgoarch); const footer = dom.div(style({ marginTop: '6ex', opacity: 0.75 }), link('https://www.xmox.nl', 'mox'), ' ', moxversion, ' ', moxgoversion, ' ', moxgoos, '/', moxgoarch);
const age = (date, future, nowSecs) => { const age = (date, future, nowSecs) => {

View file

@ -92,30 +92,42 @@ const blue = '#8bc8ff'
const link = (href: string, anchorOpt?: string) => dom.a(attr.href(href), attr.rel('noopener noreferrer'), anchorOpt || href) const link = (href: string, anchorOpt?: string) => dom.a(attr.href(href), attr.rel('noopener noreferrer'), anchorOpt || href)
const crumblink = (text: string, link: string) => dom.a(text, attr.href(link)) const crumblink = (text: string, path: string) => {
const crumbs = (...l: ElemArg[]) => [ return {
dom.div( text: text,
style({float: 'right'}), path: path
dom.clickbutton('Logout', attr.title('Logout, invalidating this session.'), async function click(e: MouseEvent) { }
const b = e.target! as HTMLButtonElement }
try { const crumbs = (...l: ({text: string, path: string} | string)[]) => {
b.disabled = true const crumbtext = (e: {text: string, path: string} | string) => typeof e === 'string' ? e : e.text
await client.Logout() document.title = l.map(e => crumbtext(e)).join(' - ')
} catch (err) {
console.log('logout', err)
window.alert('Error: ' + errmsg(err))
} finally {
b.disabled = false
}
localStorageRemove('webadmincsrftoken') const crumblink = (e: {text: string, path: string} | string) =>
// Reload so all state is cleared from memory. typeof e === 'string' ? e : dom.a(e.text, attr.href(e.path))
window.location.reload() return [
}), dom.div(
), style({float: 'right'}),
dom.h1(l.map((e, index) => index === 0 ? e : [' / ', e])), dom.clickbutton('Logout', attr.title('Logout, invalidating this session.'), async function click(e: MouseEvent) {
dom.br() const b = e.target! as HTMLButtonElement
] try {
b.disabled = true
await client.Logout()
} catch (err) {
console.log('logout', err)
window.alert('Error: ' + errmsg(err))
} finally {
b.disabled = false
}
localStorageRemove('webadmincsrftoken')
// Reload so all state is cleared from memory.
window.location.reload()
}),
),
dom.h1(l.map((e, index) => index === 0 ? crumblink(e) : [' / ', crumblink(e)])),
dom.br()
]
}
const errmsg = (err: unknown) => ''+((err as any).message || '(no error message)') const errmsg = (err: unknown) => ''+((err as any).message || '(no error message)')