openvibers/openvibe.network Commits
openvibe.my: tiles open the menu at their place, sections scroll into view, history times carry their zone (#867)
Changes
@@@ -170,6 +170,16 @@ function showSection(id, btn) {if (id === 'apps' && typeof loadConnectApps === 'function') loadConnectApps();}++// A later #section (a link on this page, the menu's links) opens that section and scrolls to it.++if (typeof window !== 'undefined' && window.addEventListener) {++ window.addEventListener('hashchange', () => {++ const id = window.location.hash.replace(/^#/, '');++ if (!id || !document.getElementById('sec-' + id)) return;++ showSection(id);++ document.getElementById('sec-' + id)?.scrollIntoView?.({ behavior: 'smooth', block: 'start' });++ });++}++function openInitialSection() {const pathSection = { '/themes': 'themes', '/notifications': 'notifications', '/verify-email': 'notifications', '/linked': 'linked', '/security': 'security', '/machines': 'machines', '/profile': 'profile', '/history': 'history' }[window.location.pathname];// #delete is the re-authentication return target: the delete step lives in the accounts section.@@@ -193,7 +203,9 @@ function openInitialSection() {showSection(routeSection);// On openvibe.network the section sits below the account home, so the page scrolls to it; openvibe.link's hub// (body.link-hub) is the section alone and stays at the top.-- if (routeSection === 'machines' && !document.body?.classList?.contains('link-hub')) document.getElementById('sec-machines')?.scrollIntoView?.({ block: 'start' });++ // A section named in the address (openvibe.my/history, /my#history, the menu's Full history) is where the reader++ // lands: it sits below the account home, so the page scrolls to it (it opened the section and stayed at the top).++ if (routeSection !== 'profile' && !document.body?.classList?.contains('link-hub')) document.getElementById('sec-' + routeSection)?.scrollIntoView?.({ block: 'start' });if (routeSection === 'machines' && hash === 'pair') setTimeout(() => mcPair(), 0);}}@@@ -97,7 +97,8 @@ function renderActions(unread) {if (hero && color && hero.style && hero.style.setProperty) hero.style.setProperty('--me', color);const streamer = !!(currentUser && currentUser.role && currentUser.role !== 'user');const count = unread && Number(unread.count) > 0 ? Number(unread.count) : 0;-- const dock = (tab) => () => { try { const nb = globalThis.OpenVibeNavbar; if (nb && nb.openDock) { nb.openDock(tab); return true; } } catch (e) { /* the link works */ } return false; };++ // The menu at a place (openvibe-shared 4.223.0 OpenVibeNavbar.openMenu); before it, the Dock's opener; else the link.++ const dock = (tab) => () => { try { const nb = globalThis.OpenVibeNavbar; if (nb && nb.openMenu) { nb.openMenu(tab); return true; } if (nb && nb.openDock) { nb.openDock(tab); return true; } } catch (e) { /* the link works */ } return false; };const acts = [streamer ? ['Go live', 'fa-tower-broadcast', 'https://openvibe.live/broadcast', 'live'] : ['Watch live', 'fa-circle-play', 'https://openvibe.live/', 'live'],['Messages', 'fa-comment-dots', 'https://openvibe.chat/messages', 'chat', dock('chats')],@@@ -105,7 +106,7 @@ function renderActions(unread) {['Your files', 'fa-folder', 'https://openvibe.download/files', 'files'],['Codes', 'fa-code', 'https://openvibe.codes/', 'codes'],['Wallet', 'fa-wallet', '/wallet', 'wallet'],-- ['Notifications', 'fa-bell', '#notifications', 'bell', () => { const bell = document.querySelector('[data-ov-bell], .openvibe-navbar-bell, #openvibe-notif-btn'); if (bell) { bell.click(); return true; } showSection('notifications'); return true; }, count],++ ['Notifications', 'fa-bell', '#notifications', 'bell', () => { try { const nb = globalThis.OpenVibeNavbar; if (nb && nb.openMenu) { nb.openMenu('notifications'); return true; } } catch (e) { /* the section below */ } showSection('notifications'); document.getElementById('sec-notifications')?.scrollIntoView?.({ behavior: 'smooth', block: 'start' }); return true; }, count],['Settings', 'fa-gear', '#settings', 'gear'],];nav.replaceChildren(...acts.map(([label, icon, href, tone, open, n]) => {@@@ -44,13 +44,17 @@ function serviceFromUrl(url) {} catch { return { service: null, sub: null }; }}++// Times leave with their zone (the column holds UTC as "YYYY-MM-DD HH:MM:SS", which a browser reads as its local time:++// west of UTC every visit looked hours ahead and said "just now", 2026-11-12).++const isoUtc = (t) => (typeof t === 'string' && /^\d{4}-\d\d-\d\d[ T]\d\d:\d\d(:\d\d(\.\d+)?)?$/.test(t) ? `${t.replace(' ', 'T')}Z` : t);++function rowOut(r) {let meta = null;try { meta = r.meta ? JSON.parse(r.meta) : null; } catch { meta = null; }return {id: r.id, type: r.type, title: r.title, url: r.url, icon: r.icon, image_url: r.image_url || null, service: r.service, sub: r.sub,service_label: r.sub ? `${r.sub}.${SERVICE_LABELS[r.service] || r.service || ''}` : (SERVICE_LABELS[r.service] || r.service || null),-- meta, hits: r.hits, created_at: r.created_at, updated_at: r.updated_at,++ meta, hits: r.hits, created_at: isoUtc(r.created_at), updated_at: isoUtc(r.updated_at),};}
@@ -170,6 +170,16 @@ function showSection(id, btn) {
@@ -170,6 +170,16 @@ function showSection(id, btn) {
if (id === 'apps' && typeof loadConnectApps === 'function') loadConnectApps();
if (id === 'apps' && typeof loadConnectApps === 'function') loadConnectApps();
}
}
+// A later #section (a link on this page, the menu's links) opens that section and scrolls to it.
+if (typeof window !== 'undefined' && window.addEventListener) {
+ window.addEventListener('hashchange', () => {
+ const id = window.location.hash.replace(/^#/, '');
+ if (!id || !document.getElementById('sec-' + id)) return;
+ showSection(id);
+ document.getElementById('sec-' + id)?.scrollIntoView?.({ behavior: 'smooth', block: 'start' });
+ });
+}
+
function openInitialSection() {
function openInitialSection() {
const pathSection = { '/themes': 'themes', '/notifications': 'notifications', '/verify-email': 'notifications', '/linked': 'linked', '/security': 'security', '/machines': 'machines', '/profile': 'profile', '/history': 'history' }[window.location.pathname];
const pathSection = { '/themes': 'themes', '/notifications': 'notifications', '/verify-email': 'notifications', '/linked': 'linked', '/security': 'security', '/machines': 'machines', '/profile': 'profile', '/history': 'history' }[window.location.pathname];
// #delete is the re-authentication return target: the delete step lives in the accounts section.
// #delete is the re-authentication return target: the delete step lives in the accounts section.
@@ -193,7 +203,9 @@ function openInitialSection() {
@@ -193,7 +203,9 @@ function openInitialSection() {
showSection(routeSection);
showSection(routeSection);
// On openvibe.network the section sits below the account home, so the page scrolls to it; openvibe.link's hub
// On openvibe.network the section sits below the account home, so the page scrolls to it; openvibe.link's hub
// (body.link-hub) is the section alone and stays at the top.
// (body.link-hub) is the section alone and stays at the top.
- if (routeSection === 'machines' && !document.body?.classList?.contains('link-hub')) document.getElementById('sec-machines')?.scrollIntoView?.({ block: 'start' });
+ // A section named in the address (openvibe.my/history, /my#history, the menu's Full history) is where the reader
+ // lands: it sits below the account home, so the page scrolls to it (it opened the section and stayed at the top).
+ if (routeSection !== 'profile' && !document.body?.classList?.contains('link-hub')) document.getElementById('sec-' + routeSection)?.scrollIntoView?.({ block: 'start' });
if (routeSection === 'machines' && hash === 'pair') setTimeout(() => mcPair(), 0);
if (routeSection === 'machines' && hash === 'pair') setTimeout(() => mcPair(), 0);
}
}
}
}
@@ -97,7 +97,8 @@ function renderActions(unread) {
@@ -97,7 +97,8 @@ function renderActions(unread) {
if (hero && color && hero.style && hero.style.setProperty) hero.style.setProperty('--me', color);
if (hero && color && hero.style && hero.style.setProperty) hero.style.setProperty('--me', color);
const streamer = !!(currentUser && currentUser.role && currentUser.role !== 'user');
const streamer = !!(currentUser && currentUser.role && currentUser.role !== 'user');
const count = unread && Number(unread.count) > 0 ? Number(unread.count) : 0;
const count = unread && Number(unread.count) > 0 ? Number(unread.count) : 0;
- const dock = (tab) => () => { try { const nb = globalThis.OpenVibeNavbar; if (nb && nb.openDock) { nb.openDock(tab); return true; } } catch (e) { /* the link works */ } return false; };
+ // The menu at a place (openvibe-shared 4.223.0 OpenVibeNavbar.openMenu); before it, the Dock's opener; else the link.
+ const dock = (tab) => () => { try { const nb = globalThis.OpenVibeNavbar; if (nb && nb.openMenu) { nb.openMenu(tab); return true; } if (nb && nb.openDock) { nb.openDock(tab); return true; } } catch (e) { /* the link works */ } return false; };
const acts = [
const acts = [
streamer ? ['Go live', 'fa-tower-broadcast', 'https://openvibe.live/broadcast', 'live'] : ['Watch live', 'fa-circle-play', 'https://openvibe.live/', 'live'],
streamer ? ['Go live', 'fa-tower-broadcast', 'https://openvibe.live/broadcast', 'live'] : ['Watch live', 'fa-circle-play', 'https://openvibe.live/', 'live'],
['Messages', 'fa-comment-dots', 'https://openvibe.chat/messages', 'chat', dock('chats')],
['Messages', 'fa-comment-dots', 'https://openvibe.chat/messages', 'chat', dock('chats')],
@@ -105,7 +106,7 @@ function renderActions(unread) {
@@ -105,7 +106,7 @@ function renderActions(unread) {
['Your files', 'fa-folder', 'https://openvibe.download/files', 'files'],
['Your files', 'fa-folder', 'https://openvibe.download/files', 'files'],
['Codes', 'fa-code', 'https://openvibe.codes/', 'codes'],
['Codes', 'fa-code', 'https://openvibe.codes/', 'codes'],
['Wallet', 'fa-wallet', '/wallet', 'wallet'],
['Wallet', 'fa-wallet', '/wallet', 'wallet'],
- ['Notifications', 'fa-bell', '#notifications', 'bell', () => { const bell = document.querySelector('[data-ov-bell], .openvibe-navbar-bell, #openvibe-notif-btn'); if (bell) { bell.click(); return true; } showSection('notifications'); return true; }, count],
+ ['Notifications', 'fa-bell', '#notifications', 'bell', () => { try { const nb = globalThis.OpenVibeNavbar; if (nb && nb.openMenu) { nb.openMenu('notifications'); return true; } } catch (e) { /* the section below */ } showSection('notifications'); document.getElementById('sec-notifications')?.scrollIntoView?.({ behavior: 'smooth', block: 'start' }); return true; }, count],
['Settings', 'fa-gear', '#settings', 'gear'],
['Settings', 'fa-gear', '#settings', 'gear'],
];
];
nav.replaceChildren(...acts.map(([label, icon, href, tone, open, n]) => {
nav.replaceChildren(...acts.map(([label, icon, href, tone, open, n]) => {
@@ -44,13 +44,17 @@ function serviceFromUrl(url) {
@@ -44,13 +44,17 @@ function serviceFromUrl(url) {
} catch { return { service: null, sub: null }; }
} catch { return { service: null, sub: null }; }
}
}
+// Times leave with their zone (the column holds UTC as "YYYY-MM-DD HH:MM:SS", which a browser reads as its local time:
+// west of UTC every visit looked hours ahead and said "just now", 2026-11-12).
+const isoUtc = (t) => (typeof t === 'string' && /^\d{4}-\d\d-\d\d[ T]\d\d:\d\d(:\d\d(\.\d+)?)?$/.test(t) ? `${t.replace(' ', 'T')}Z` : t);
+
function rowOut(r) {
function rowOut(r) {
let meta = null;
let meta = null;
try { meta = r.meta ? JSON.parse(r.meta) : null; } catch { meta = null; }
try { meta = r.meta ? JSON.parse(r.meta) : null; } catch { meta = null; }
return {
return {
id: r.id, type: r.type, title: r.title, url: r.url, icon: r.icon, image_url: r.image_url || null, service: r.service, sub: r.sub,
id: r.id, type: r.type, title: r.title, url: r.url, icon: r.icon, image_url: r.image_url || null, service: r.service, sub: r.sub,
service_label: r.sub ? `${r.sub}.${SERVICE_LABELS[r.service] || r.service || ''}` : (SERVICE_LABELS[r.service] || r.service || null),
service_label: r.sub ? `${r.sub}.${SERVICE_LABELS[r.service] || r.service || ''}` : (SERVICE_LABELS[r.service] || r.service || null),
- meta, hits: r.hits, created_at: r.created_at, updated_at: r.updated_at,
+ meta, hits: r.hits, created_at: isoUtc(r.created_at), updated_at: isoUtc(r.updated_at),
};
};
}
}