mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +00:00
feat(ui): render Provider Settings from a store
initKeySetup keeps the setup requests, the ChatGPT OAuth flow, the pasted field values and the dialog's keyboard, and publishes the POWER UP chip, the dialog, its key rows and the status line to the gev-provider-settings store. ProviderSettingsView renders them and sends clicks and typing to the connected owner; in Cyber the chip is teleported into the top toolbar. A successful save renders the rows afresh so no field keeps a key, and the status line announces a repeated message again. The voice action opens and closes the dialog through the same controls.
This commit is contained in:
1 parent
6cb01ff975
commit
fe963a1219
9 files changed
+840
-517
No files matched your search
+17
-74
@@ -4,61 +4,14 @@ import {
|
||||
collectKeyUpdates,
|
||||
keySetupChipLabel,
|
||||
stripKeylessBasemapFromHash,
|
||||
bindKeySetupPlacement,
|
||||
waitForChatGptOAuth,
|
||||
initKeySetup,
|
||||
} from './keySetup.js';
|
||||
import { createPinia } from 'pinia';
|
||||
import { describeUnsavedWork } from './i18n/index.js';
|
||||
import { useProviderSettingsStore } from './ui/stores/providerSettings.js';
|
||||
|
||||
test('setup placement moves the same button only in Cyber and disconnects on teardown', () => {
|
||||
const home = {
|
||||
prepend: (node) => {
|
||||
node.parentNode = home;
|
||||
},
|
||||
};
|
||||
const chip = { parentNode: home, hidden: true };
|
||||
let moves = 0;
|
||||
const toolbar = {
|
||||
append: (node) => {
|
||||
moves++;
|
||||
node.parentNode = toolbar;
|
||||
},
|
||||
};
|
||||
const theme = { dataset: { uiTheme: 'tactical' } };
|
||||
let notify,
|
||||
disconnected = false;
|
||||
class Observer {
|
||||
constructor(callback) {
|
||||
notify = callback;
|
||||
}
|
||||
observe(target, options) {
|
||||
assert.equal(target, theme);
|
||||
assert.deepEqual(options.attributeFilter, ['data-ui-theme']);
|
||||
}
|
||||
disconnect() {
|
||||
disconnected = true;
|
||||
}
|
||||
}
|
||||
const dispose = bindKeySetupPlacement(
|
||||
{
|
||||
documentElement: theme,
|
||||
getElementById: () => toolbar,
|
||||
defaultView: { MutationObserver: Observer },
|
||||
},
|
||||
chip,
|
||||
);
|
||||
assert.equal(chip.parentNode, home);
|
||||
for (const variant of ['cyber', 'tactical', 'cyber', 'minimal']) {
|
||||
theme.dataset.uiTheme = variant;
|
||||
notify();
|
||||
assert.equal(chip.parentNode, variant === 'cyber' ? toolbar : home);
|
||||
notify();
|
||||
}
|
||||
assert.equal(moves, 2, 'identical theme sync does not reparent again');
|
||||
assert.equal(chip.hidden, true, 'placement cannot reveal a retired button');
|
||||
dispose();
|
||||
assert.equal(disconnected, true);
|
||||
});
|
||||
const freshStore = () => useProviderSettingsStore(createPinia());
|
||||
|
||||
test('the chip counts what is missing, and retires the count at zero', () => {
|
||||
assert.equal(keySetupChipLabel({ setCount: 0, total: 8 }), 'POWER UP · 8 KEYS WAITING');
|
||||
@@ -153,52 +106,42 @@ test('the first Google key strips ONLY the keyless OSM basemap from the share ha
|
||||
});
|
||||
|
||||
test('aborting pending setup removes its surface and ignores a late response', async () => {
|
||||
const { initKeySetup } = await import('./keySetup.js');
|
||||
const removed = [];
|
||||
const chip = { remove: () => removed.push('chip') };
|
||||
const root = { dataset: {}, remove: () => removed.push('root') };
|
||||
const store = freshStore();
|
||||
const root = { dataset: {} };
|
||||
let resolveResponse;
|
||||
let requestSignal;
|
||||
const controller = new AbortController();
|
||||
const pending = initKeySetup({
|
||||
documentRef: { getElementById: (id) => id === 'key-setup-chip' ? chip : root },
|
||||
documentRef: { getElementById: (id) => (id === 'key-setup' ? root : null) },
|
||||
signal: controller.signal,
|
||||
store,
|
||||
fetchImpl: (_url, { signal }) => {
|
||||
requestSignal = signal;
|
||||
return new Promise((resolve) => { resolveResponse = resolve; });
|
||||
},
|
||||
});
|
||||
assert.equal(store.controls !== null, true);
|
||||
controller.abort();
|
||||
assert.equal(requestSignal.aborted, true);
|
||||
assert.deepEqual(removed, ['chip', 'root']);
|
||||
assert.equal(store.view.available, false);
|
||||
assert.equal(store.controls, null);
|
||||
resolveResponse({ ok: true, json: async () => ({ keys: [] }) });
|
||||
assert.equal(await pending, null);
|
||||
assert.equal(store.view.available, false);
|
||||
});
|
||||
|
||||
test('a pasted, unsaved key makes the language switch ask first until teardown', async () => {
|
||||
const node = () => ({
|
||||
dataset: {},
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
setAttribute() {},
|
||||
querySelector: () => null,
|
||||
remove() {},
|
||||
});
|
||||
const chip = node();
|
||||
const input = { dataset: { envVar: 'GOOGLE_MAPS_API_KEY' }, value: '' };
|
||||
const root = {
|
||||
...node(),
|
||||
querySelectorAll: (selector) =>
|
||||
selector === 'input[data-env-var]' ? [input] : [],
|
||||
};
|
||||
const store = freshStore();
|
||||
const root = { dataset: {} };
|
||||
const controller = new AbortController();
|
||||
const setup = await initKeySetup({
|
||||
documentRef: {
|
||||
getElementById: (id) => (id === 'key-setup-chip' ? chip : root),
|
||||
getElementById: (id) => (id === 'key-setup' ? root : null),
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
},
|
||||
signal: controller.signal,
|
||||
store,
|
||||
fetchImpl: async () => ({
|
||||
ok: true,
|
||||
json: async () => ({ keys: [], setCount: 0, total: 1 }),
|
||||
@@ -206,9 +149,9 @@ test('a pasted, unsaved key makes the language switch ask first until teardown',
|
||||
});
|
||||
assert.ok(setup);
|
||||
assert.deepEqual(describeUnsavedWork(), []);
|
||||
input.value = ' ';
|
||||
store.controls.setField('GOOGLE_MAPS_API_KEY', ' ');
|
||||
assert.deepEqual(describeUnsavedWork(), []);
|
||||
input.value = 'AIza-pasted';
|
||||
store.controls.setField('GOOGLE_MAPS_API_KEY', 'AIza-pasted');
|
||||
assert.deepEqual(describeUnsavedWork(), ['unsaved provider keys']);
|
||||
// The field still holds the key, so only teardown dropping the check clears it.
|
||||
controller.abort();
|
||||
|
||||
Reference in new issue
Block a user