mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-06 23:12:22 +02:00
settings.js is 5,721 lines and the registry/navigation/search/sidebar/
lifecycle primitives already live in static/js/settings/. What was left
behind in the coordinator was the layer above them: what happens when a
panel becomes active, where an admin-managed tab is handed to admin.js,
which elements are admin-only, the Appearance window fade, and the
public open/close. That layer reached module-global `modalEl` and
`initialized` directly, so none of it could be exercised without
booting every panel in the file — and every panel I eventually move out
would have to route back through it.
Three modules, no behavior change:
shell.js panel-activation side effects, the admin handoff,
.admin-only visibility, open()/close(). Takes what it
needs from settings.js as injected callbacks, the same
shape bindSettingsNavigation() already uses, so it
holds no panel state.
peek.js the Appearance window fade and its toggle. It is window
chrome rather than Appearance panel data, and it has to
be cleared when the user leaves that panel.
oauthReturn.js the once-per-load return path from the Google OAuth
redirect. It was an IIFE running at module evaluation
in the middle of a 5,700-line file.
settings.js keeps open/close/syncAdminVisibility as exports, so every
caller (app.js, calendar.js, chatStream.js, gallery.js, admin.js,
modelPicker.js, slashCommands.js, chatRenderer.js, emailLibrary.js) is
untouched. 5,721 -> 5,583 lines; the settings/ modules go 887 -> 1,107.
The real-ESM coordinator smoke now links the three new files and asserts
what moved: admin-only elements hidden for a non-admin and shown for an
admin, an admin-managed tab click handed to admin.js without a second
local activation, and the Peek fade applying on Appearance and clearing
when the user navigates away. The OAuth test follows its handler to the
new file and additionally pins the coordinator wiring, since "uses the
module-local open()" is now a property of the seam rather than of one
source slice.
No new module needs a cache-busting query or an sw.js precache entry:
the existing settings/ submodules have neither, they load transitively
from settings.js's versioned URL, and sw.js serves JS network-first.
admin.js stays where it is. It has no shell to extract — open() and
close() already delegate to settingsModule, and its 4,122 lines are all
panel code. That is a panel split, not this one.
48 lines
2.0 KiB
JavaScript
48 lines
2.0 KiB
JavaScript
// Return path from the Google OAuth2 redirect.
|
|
//
|
|
// The provider sends the browser back to the app root with a result in the
|
|
// query string, so something has to notice on load and put the user back where
|
|
// they were — the Integrations panel — with the outcome visible. That is
|
|
// navigation, not an Integrations concern, and it runs once per page load
|
|
// rather than once per panel open.
|
|
|
|
/**
|
|
* @param {object} options
|
|
* @param {(tab: string) => void} options.openSettings the Settings open() entry point
|
|
*/
|
|
export function handleSettingsOauthReturn({ openSettings } = {}) {
|
|
const sp = new URLSearchParams(window.location.search);
|
|
if (!sp.has('email_oauth_success') && !sp.has('email_oauth_error')) return;
|
|
// Strip params from URL without a page reload.
|
|
const clean = window.location.pathname + window.location.hash;
|
|
window.history.replaceState(null, '', clean);
|
|
const success = sp.has('email_oauth_success');
|
|
const errMsg = sp.get('email_oauth_error') || '';
|
|
|
|
// The caller passes its own open() in, so this never has to wait for a
|
|
// window-level alias to exist.
|
|
function _showResult() {
|
|
openSettings('integrations');
|
|
// Brief toast-style banner.
|
|
const banner = document.createElement('div');
|
|
banner.textContent = success
|
|
? 'Google account connected — email is ready'
|
|
: `Google OAuth failed: ${errMsg || 'unknown error'}`;
|
|
Object.assign(banner.style, {
|
|
position: 'fixed', bottom: '24px', left: '50%', transform: 'translateX(-50%)',
|
|
background: success ? 'var(--accent, #50fa7b)' : 'var(--red, #ff5555)',
|
|
color: '#000', padding: '8px 18px', borderRadius: '6px', fontSize: '12px',
|
|
fontWeight: '600', zIndex: '99999', pointerEvents: 'none',
|
|
boxShadow: '0 2px 12px rgba(0,0,0,0.3)',
|
|
});
|
|
document.body.appendChild(banner);
|
|
setTimeout(() => banner.remove(), 4000);
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', _showResult, { once: true });
|
|
} else {
|
|
_showResult();
|
|
}
|
|
}
|