- Add Dev Mode toggle to settings that persists in vault metadata - Add test permission prompt button (✨) to all page headers when dev mode enabled - Move devMode and onTestPrompt to NavComponent base class for inheritance - Refactor all home components to extend NavComponent - Simplify permission prompt layout: remove duplicate domain from header - Convert permission descriptions to flowing single paragraphs - Update header-buttons styling for consistent lock/magic button layout Files modified: - projects/common/src/lib/common/nav-component.ts (devMode, onTestPrompt) - projects/common/src/lib/services/storage/types.ts (devMode property) - projects/common/src/lib/services/storage/signer-meta-handler.ts (setDevMode) - projects/common/src/lib/styles/_common.scss (header-buttons styling) - projects/*/src/app/components/home/*/settings.component.* (dev mode UI) - projects/*/src/app/components/home/*/*.component.* (extend NavComponent) - projects/*/public/prompt.html (simplified layout) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
154 lines
4.5 KiB
TypeScript
154 lines
4.5 KiB
TypeScript
import { Component, inject, OnInit } from '@angular/core';
|
|
import { Router } from '@angular/router';
|
|
import {
|
|
BrowserSyncData,
|
|
BrowserSyncFlow,
|
|
ConfirmComponent,
|
|
DateHelper,
|
|
LoggerService,
|
|
NavComponent,
|
|
NavItemComponent,
|
|
StartupService,
|
|
StorageService,
|
|
} from '@common';
|
|
import { getNewStorageServiceConfig } from '../../../common/data/get-new-storage-service-config';
|
|
import { Buffer } from 'buffer';
|
|
|
|
@Component({
|
|
selector: 'app-settings',
|
|
imports: [ConfirmComponent, NavItemComponent],
|
|
templateUrl: './settings.component.html',
|
|
styleUrl: './settings.component.scss',
|
|
})
|
|
export class SettingsComponent extends NavComponent implements OnInit {
|
|
readonly #router = inject(Router);
|
|
syncFlow: string | undefined;
|
|
override devMode = false;
|
|
|
|
readonly #storage = inject(StorageService);
|
|
readonly #startup = inject(StartupService);
|
|
readonly #logger = inject(LoggerService);
|
|
|
|
ngOnInit(): void {
|
|
const vault = JSON.stringify(
|
|
this.#storage.getBrowserSyncHandler().browserSyncData
|
|
);
|
|
console.log(vault.length / 1024 + ' KB');
|
|
|
|
switch (this.#storage.getSignerMetaHandler().signerMetaData?.syncFlow) {
|
|
case BrowserSyncFlow.NO_SYNC:
|
|
this.syncFlow = 'Off';
|
|
break;
|
|
|
|
case BrowserSyncFlow.BROWSER_SYNC:
|
|
this.syncFlow = 'Google Chrome';
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Load dev mode setting
|
|
this.devMode = this.#storage.getSignerMetaHandler().signerMetaData?.devMode ?? false;
|
|
}
|
|
|
|
async onToggleDevMode(event: Event) {
|
|
const checked = (event.target as HTMLInputElement).checked;
|
|
this.devMode = checked;
|
|
await this.#storage.getSignerMetaHandler().setDevMode(checked);
|
|
}
|
|
|
|
override async onTestPrompt() {
|
|
// Open a test permission prompt window
|
|
const testEvent = {
|
|
kind: 1,
|
|
content: 'This is a test note for permission prompt preview.',
|
|
tags: [],
|
|
created_at: Math.floor(Date.now() / 1000),
|
|
};
|
|
const base64Event = Buffer.from(JSON.stringify(testEvent, null, 2)).toString('base64');
|
|
const currentIdentity = this.#storage.getBrowserSessionHandler().browserSessionData?.identities.find(
|
|
i => i.id === this.#storage.getBrowserSessionHandler().browserSessionData?.selectedIdentityId
|
|
);
|
|
const nick = currentIdentity?.nick ?? 'Test Identity';
|
|
|
|
const width = 375;
|
|
const height = 600;
|
|
const left = Math.round((screen.width - width) / 2);
|
|
const top = Math.round((screen.height - height) / 2);
|
|
|
|
chrome.windows.create({
|
|
type: 'popup',
|
|
url: `prompt.html?method=signEvent&host=example.com&id=test-${Date.now()}&nick=${encodeURIComponent(nick)}&event=${base64Event}`,
|
|
width,
|
|
height,
|
|
left,
|
|
top,
|
|
});
|
|
}
|
|
|
|
async onResetExtension() {
|
|
try {
|
|
this.#logger.logVaultReset();
|
|
await this.#storage.resetExtension();
|
|
this.#startup.startOver(getNewStorageServiceConfig());
|
|
} catch (error) {
|
|
console.log(error);
|
|
// TODO
|
|
}
|
|
}
|
|
|
|
onImportVault() {
|
|
(this as unknown as HTMLInputElement).click();
|
|
}
|
|
|
|
async onImportFileChange(event: Event) {
|
|
try {
|
|
const element = event.currentTarget as HTMLInputElement;
|
|
const file = element.files !== null ? element.files[0] : undefined;
|
|
if (!file) {
|
|
return;
|
|
}
|
|
|
|
const text = await file.text();
|
|
const vault = JSON.parse(text) as BrowserSyncData;
|
|
|
|
await this.#storage.deleteVault(true);
|
|
await this.#storage.importVault(vault);
|
|
this.#logger.logVaultImport(file.name);
|
|
this.#storage.isInitialized = false;
|
|
this.#startup.startOver(getNewStorageServiceConfig());
|
|
} catch (error) {
|
|
console.log(error);
|
|
// TODO
|
|
}
|
|
}
|
|
|
|
async onClickExportVault() {
|
|
const jsonVault = this.#storage.exportVault();
|
|
|
|
const dateTimeString = DateHelper.dateToISOLikeButLocal(new Date());
|
|
const fileName = `Plebeian Signer Chrome - Vault Export - ${dateTimeString}.json`;
|
|
|
|
this.#downloadJson(jsonVault, fileName);
|
|
this.#logger.logVaultExport(fileName);
|
|
}
|
|
|
|
#downloadJson(jsonString: string, fileName: string) {
|
|
const dataStr =
|
|
'data:text/json;charset=utf-8,' + encodeURIComponent(jsonString);
|
|
const downloadAnchorNode = document.createElement('a');
|
|
downloadAnchorNode.setAttribute('href', dataStr);
|
|
downloadAnchorNode.setAttribute('download', fileName);
|
|
document.body.appendChild(downloadAnchorNode);
|
|
downloadAnchorNode.click();
|
|
downloadAnchorNode.remove();
|
|
}
|
|
|
|
async onClickLock() {
|
|
this.#logger.logVaultLock();
|
|
await this.#storage.lockVault();
|
|
this.#router.navigateByUrl('/vault-login');
|
|
}
|
|
}
|