Release v1.0.11 - Add dev mode with test prompt button on all headers
- 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>
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "plebeian-signer",
|
"name": "plebeian-signer",
|
||||||
"version": "v1.0.10",
|
"version": "v1.0.11",
|
||||||
"custom": {
|
"custom": {
|
||||||
"chrome": {
|
"chrome": {
|
||||||
"version": "v1.0.10"
|
"version": "v1.0.11"
|
||||||
},
|
},
|
||||||
"firefox": {
|
"firefox": {
|
||||||
"version": "v1.0.10"
|
"version": "v1.0.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Plebeian Signer - Nostr Identity Manager & Signer",
|
"name": "Plebeian Signer - Nostr Identity Manager & Signer",
|
||||||
"description": "Manage and switch between multiple identities while interacting with Nostr apps",
|
"description": "Manage and switch between multiple identities while interacting with Nostr apps",
|
||||||
"version": "1.0.10",
|
"version": "1.0.11",
|
||||||
"homepage_url": "https://github.com/PlebeianApp/plebeian-signer",
|
"homepage_url": "https://github.com/PlebeianApp/plebeian-signer",
|
||||||
"options_page": "options.html",
|
"options_page": "options.html",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
|
|||||||
@@ -27,11 +27,66 @@
|
|||||||
.page {
|
.page {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 1fr 60px;
|
grid-template-rows: 1fr auto;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
padding: var(--size);
|
||||||
|
background: var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-label {
|
||||||
|
width: 60px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons button {
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reject {
|
||||||
|
background: var(--muted);
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reject:hover {
|
||||||
|
background: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-accept {
|
||||||
|
background: var(--primary);
|
||||||
|
color: var(--primary-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-accept:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
padding: var(--size);
|
padding: var(--size);
|
||||||
background: var(--background-light);
|
background: var(--background-light);
|
||||||
@@ -54,6 +109,12 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: gray;
|
color: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -63,64 +124,31 @@
|
|||||||
<span id="titleSpan" style="font-weight: 400 !important"></span>
|
<span id="titleSpan" style="font-weight: 400 !important"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span
|
|
||||||
class="host-INSERT sam-align-self-center sam-text-muted"
|
|
||||||
style="font-weight: 500"
|
|
||||||
></span>
|
|
||||||
|
|
||||||
<!-- Card for getPublicKey -->
|
<!-- Card for getPublicKey -->
|
||||||
<div id="cardGetPublicKey" class="card sam-mt sam-ml sam-mr">
|
<div id="cardGetPublicKey" class="card sam-mt sam-ml sam-mr">
|
||||||
<span style="text-align: center">
|
<p class="description">
|
||||||
<b><span class="host-INSERT color-primary"></span></b>
|
<b class="host-INSERT color-primary"></b> is requesting permission to
|
||||||
is requesting permission to<br />
|
<b class="color-primary">read your public key</b> for the selected identity
|
||||||
<br />
|
<b class="nick-INSERT color-primary"></b>.
|
||||||
<b class="color-primary">read your public key</b> <br />
|
</p>
|
||||||
<br />
|
|
||||||
<span>
|
|
||||||
for the selected identity
|
|
||||||
<span
|
|
||||||
style="font-weight: 500"
|
|
||||||
class="nick-INSERT color-primary"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card for getRelays -->
|
<!-- Card for getRelays -->
|
||||||
<div id="cardGetRelays" class="card sam-mt sam-ml sam-mr">
|
<div id="cardGetRelays" class="card sam-mt sam-ml sam-mr">
|
||||||
<span style="text-align: center">
|
<p class="description">
|
||||||
<b><span class="host-INSERT color-primary"></span></b>
|
<b class="host-INSERT color-primary"></b> is requesting permission to
|
||||||
is requesting permission to<br />
|
<b class="color-primary">read your relays</b> for the selected identity
|
||||||
<br />
|
<b class="nick-INSERT color-primary"></b>.
|
||||||
<b class="color-primary">read your relays</b> <br />
|
</p>
|
||||||
<br />
|
|
||||||
<span>
|
|
||||||
for the selected identity
|
|
||||||
<span
|
|
||||||
style="font-weight: 500"
|
|
||||||
class="nick-INSERT color-primary"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card for signEvent -->
|
<!-- Card for signEvent -->
|
||||||
<div id="cardSignEvent" class="card sam-mt sam-ml sam-mr">
|
<div id="cardSignEvent" class="card sam-mt sam-ml sam-mr">
|
||||||
<span style="text-align: center">
|
<p class="description">
|
||||||
<b><span class="host-INSERT color-primary"></span></b>
|
<b class="host-INSERT color-primary"></b> is requesting permission to
|
||||||
is requesting permission to<br />
|
<b class="color-primary">sign an event</b> (kind <span id="kindSpan"></span>)
|
||||||
<br />
|
for the selected identity <b class="nick-INSERT color-primary"></b>.
|
||||||
<b class="color-primary">sign an event</b> (kind
|
</p>
|
||||||
<span id="kindSpan"></span>) <br />
|
|
||||||
<br />
|
|
||||||
<span>
|
|
||||||
for the selected identity
|
|
||||||
<span
|
|
||||||
style="font-weight: 500"
|
|
||||||
class="nick-INSERT color-primary"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card2 for signEvent -->
|
<!-- Card2 for signEvent -->
|
||||||
@@ -130,20 +158,11 @@
|
|||||||
|
|
||||||
<!-- Card for nip04.encrypt -->
|
<!-- Card for nip04.encrypt -->
|
||||||
<div id="cardNip04Encrypt" class="card sam-mt sam-ml sam-mr">
|
<div id="cardNip04Encrypt" class="card sam-mt sam-ml sam-mr">
|
||||||
<span style="text-align: center">
|
<p class="description">
|
||||||
<b><span class="host-INSERT color-primary"></span></b>
|
<b class="host-INSERT color-primary"></b> is requesting permission to
|
||||||
is requesting permission to<br />
|
<b class="color-primary">encrypt a text</b> (NIP04) for the selected identity
|
||||||
<br />
|
<b class="nick-INSERT color-primary"></b>.
|
||||||
<b class="color-primary">encrypt a text</b> (NIP04) <br />
|
</p>
|
||||||
<br />
|
|
||||||
<span>
|
|
||||||
for the selected identity
|
|
||||||
<span
|
|
||||||
style="font-weight: 500"
|
|
||||||
class="nick-INSERT color-primary"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card2 for nip04.encrypt -->
|
<!-- Card2 for nip04.encrypt -->
|
||||||
@@ -153,20 +172,11 @@
|
|||||||
|
|
||||||
<!-- Card for nip44.encrypt -->
|
<!-- Card for nip44.encrypt -->
|
||||||
<div id="cardNip44Encrypt" class="card sam-mt sam-ml sam-mr">
|
<div id="cardNip44Encrypt" class="card sam-mt sam-ml sam-mr">
|
||||||
<span style="text-align: center">
|
<p class="description">
|
||||||
<b><span class="host-INSERT color-primary"></span></b>
|
<b class="host-INSERT color-primary"></b> is requesting permission to
|
||||||
is requesting permission to<br />
|
<b class="color-primary">encrypt a text</b> (NIP44) for the selected identity
|
||||||
<br />
|
<b class="nick-INSERT color-primary"></b>.
|
||||||
<b class="color-primary">encrypt a text</b> (NIP44) <br />
|
</p>
|
||||||
<br />
|
|
||||||
<span>
|
|
||||||
for the selected identity
|
|
||||||
<span
|
|
||||||
style="font-weight: 500"
|
|
||||||
class="nick-INSERT color-primary"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card2 for nip44.encrypt -->
|
<!-- Card2 for nip44.encrypt -->
|
||||||
@@ -176,20 +186,11 @@
|
|||||||
|
|
||||||
<!-- Card for nip04.decrypt -->
|
<!-- Card for nip04.decrypt -->
|
||||||
<div id="cardNip04Decrypt" class="card sam-mt sam-ml sam-mr">
|
<div id="cardNip04Decrypt" class="card sam-mt sam-ml sam-mr">
|
||||||
<span style="text-align: center">
|
<p class="description">
|
||||||
<b><span class="host-INSERT color-primary"></span></b>
|
<b class="host-INSERT color-primary"></b> is requesting permission to
|
||||||
is requesting permission to<br />
|
<b class="color-primary">decrypt a text</b> (NIP04) for the selected identity
|
||||||
<br />
|
<b class="nick-INSERT color-primary"></b>.
|
||||||
<b class="color-primary">decrypt a text</b> (NIP04) <br />
|
</p>
|
||||||
<br />
|
|
||||||
<span>
|
|
||||||
for the selected identity
|
|
||||||
<span
|
|
||||||
style="font-weight: 500"
|
|
||||||
class="nick-INSERT color-primary"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card2 for nip04.decrypt -->
|
<!-- Card2 for nip04.decrypt -->
|
||||||
@@ -199,20 +200,11 @@
|
|||||||
|
|
||||||
<!-- Card for nip44.decrypt -->
|
<!-- Card for nip44.decrypt -->
|
||||||
<div id="cardNip44Decrypt" class="card sam-mt sam-ml sam-mr">
|
<div id="cardNip44Decrypt" class="card sam-mt sam-ml sam-mr">
|
||||||
<span style="text-align: center">
|
<p class="description">
|
||||||
<b><span class="host-INSERT color-primary"></span></b>
|
<b class="host-INSERT color-primary"></b> is requesting permission to
|
||||||
is requesting permission to<br />
|
<b class="color-primary">decrypt a text</b> (NIP44) for the selected identity
|
||||||
<br />
|
<b class="nick-INSERT color-primary"></b>.
|
||||||
<b class="color-primary">decrypt a text</b> (NIP44) <br />
|
</p>
|
||||||
<br />
|
|
||||||
<span>
|
|
||||||
for the selected identity
|
|
||||||
<span
|
|
||||||
style="font-weight: 500"
|
|
||||||
class="nick-INSERT color-primary"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card2 for nip44.decrypt -->
|
<!-- Card2 for nip44.decrypt -->
|
||||||
@@ -224,47 +216,20 @@
|
|||||||
<!------------->
|
<!------------->
|
||||||
<!-- ACTIONS -->
|
<!-- ACTIONS -->
|
||||||
<!------------->
|
<!------------->
|
||||||
<div class="sam-footer-grid-2">
|
<div class="actions">
|
||||||
<div class="btn-group">
|
<div class="action-row">
|
||||||
<button id="rejectOnceButton" type="button" class="btn btn-secondary">
|
<span class="action-label">Reject</span>
|
||||||
Reject
|
<div class="action-buttons">
|
||||||
</button>
|
<button id="rejectOnceButton" type="button" class="btn-reject">Once</button>
|
||||||
<button
|
<button id="rejectAlwaysButton" type="button" class="btn-reject">Always</button>
|
||||||
type="button"
|
</div>
|
||||||
class="btn btn-secondary dropdown-toggle dropdown-toggle-split"
|
|
||||||
data-bs-toggle="dropdown"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
<span class="visually-hidden">Toggle Dropdown</span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-end">
|
|
||||||
<li>
|
|
||||||
<button id="rejectAlwaysButton" class="dropdown-item">
|
|
||||||
Reject Always
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="action-row">
|
||||||
<div class="btn-group">
|
<span class="action-label">Accept</span>
|
||||||
<button id="approveAlwaysButton" type="button" class="btn btn-primary">
|
<div class="action-buttons">
|
||||||
Approve Always
|
<button id="approveOnceButton" type="button" class="btn-accept">Once</button>
|
||||||
</button>
|
<button id="approveAlwaysButton" type="button" class="btn-accept">Always</button>
|
||||||
<button
|
</div>
|
||||||
type="button"
|
|
||||||
class="btn btn-primary dropdown-toggle dropdown-toggle-split"
|
|
||||||
data-bs-toggle="dropdown"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
<span class="visually-hidden">Toggle Dropdown</span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li>
|
|
||||||
<button id="approveOnceButton" class="dropdown-item">
|
|
||||||
Approve Once
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
<div class="sam-text-header">
|
<div class="sam-text-header">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<button class="back-btn" title="Go Back" (click)="goBack()">
|
<button class="back-btn" title="Go Back" (click)="goBack()">
|
||||||
<span class="emoji">←</span>
|
<span class="emoji">←</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { Router } from '@angular/router';
|
|||||||
import {
|
import {
|
||||||
ConfirmComponent,
|
ConfirmComponent,
|
||||||
LoggerService,
|
LoggerService,
|
||||||
|
NavComponent,
|
||||||
SignerMetaData_VaultSnapshot,
|
SignerMetaData_VaultSnapshot,
|
||||||
StartupService,
|
StartupService,
|
||||||
StorageService,
|
|
||||||
} from '@common';
|
} from '@common';
|
||||||
import { getNewStorageServiceConfig } from '../../../common/data/get-new-storage-service-config';
|
import { getNewStorageServiceConfig } from '../../../common/data/get-new-storage-service-config';
|
||||||
|
|
||||||
@@ -15,9 +15,8 @@ import { getNewStorageServiceConfig } from '../../../common/data/get-new-storage
|
|||||||
styleUrl: './backups.component.scss',
|
styleUrl: './backups.component.scss',
|
||||||
imports: [ConfirmComponent],
|
imports: [ConfirmComponent],
|
||||||
})
|
})
|
||||||
export class BackupsComponent implements OnInit {
|
export class BackupsComponent extends NavComponent implements OnInit {
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
readonly #storage = inject(StorageService);
|
|
||||||
readonly #startup = inject(StartupService);
|
readonly #startup = inject(StartupService);
|
||||||
readonly #logger = inject(LoggerService);
|
readonly #logger = inject(LoggerService);
|
||||||
|
|
||||||
@@ -27,11 +26,11 @@ export class BackupsComponent implements OnInit {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loadBackups();
|
this.loadBackups();
|
||||||
this.maxBackups = this.#storage.getSignerMetaHandler().getMaxBackups();
|
this.maxBackups = this.storage.getSignerMetaHandler().getMaxBackups();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadBackups(): void {
|
loadBackups(): void {
|
||||||
this.backups = this.#storage.getSignerMetaHandler().getBackups();
|
this.backups = this.storage.getSignerMetaHandler().getBackups();
|
||||||
}
|
}
|
||||||
|
|
||||||
async onMaxBackupsChange(event: Event): Promise<void> {
|
async onMaxBackupsChange(event: Event): Promise<void> {
|
||||||
@@ -39,14 +38,14 @@ export class BackupsComponent implements OnInit {
|
|||||||
const value = parseInt(input.value, 10);
|
const value = parseInt(input.value, 10);
|
||||||
if (!isNaN(value) && value >= 1 && value <= 20) {
|
if (!isNaN(value) && value >= 1 && value <= 20) {
|
||||||
this.maxBackups = value;
|
this.maxBackups = value;
|
||||||
await this.#storage.getSignerMetaHandler().setMaxBackups(value);
|
await this.storage.getSignerMetaHandler().setMaxBackups(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createManualBackup(): Promise<void> {
|
async createManualBackup(): Promise<void> {
|
||||||
const browserSyncData = this.#storage.getBrowserSyncHandler().browserSyncData;
|
const browserSyncData = this.storage.getBrowserSyncHandler().browserSyncData;
|
||||||
if (browserSyncData) {
|
if (browserSyncData) {
|
||||||
await this.#storage.getSignerMetaHandler().createBackup(browserSyncData, 'manual');
|
await this.storage.getSignerMetaHandler().createBackup(browserSyncData, 'manual');
|
||||||
this.loadBackups();
|
this.loadBackups();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,22 +54,22 @@ export class BackupsComponent implements OnInit {
|
|||||||
this.restoringBackupId = backupId;
|
this.restoringBackupId = backupId;
|
||||||
try {
|
try {
|
||||||
// First, create a pre-restore backup of current state
|
// First, create a pre-restore backup of current state
|
||||||
const currentData = this.#storage.getBrowserSyncHandler().browserSyncData;
|
const currentData = this.storage.getBrowserSyncHandler().browserSyncData;
|
||||||
if (currentData) {
|
if (currentData) {
|
||||||
await this.#storage.getSignerMetaHandler().createBackup(currentData, 'pre-restore');
|
await this.storage.getSignerMetaHandler().createBackup(currentData, 'pre-restore');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the backup data
|
// Get the backup data
|
||||||
const backupData = this.#storage.getSignerMetaHandler().getBackupData(backupId);
|
const backupData = this.storage.getSignerMetaHandler().getBackupData(backupId);
|
||||||
if (!backupData) {
|
if (!backupData) {
|
||||||
throw new Error('Backup not found');
|
throw new Error('Backup not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import the backup
|
// Import the backup
|
||||||
await this.#storage.deleteVault(true);
|
await this.storage.deleteVault(true);
|
||||||
await this.#storage.importVault(backupData);
|
await this.storage.importVault(backupData);
|
||||||
this.#logger.logVaultImport('Backup Restore');
|
this.#logger.logVaultImport('Backup Restore');
|
||||||
this.#storage.isInitialized = false;
|
this.storage.isInitialized = false;
|
||||||
this.#startup.startOver(getNewStorageServiceConfig());
|
this.#startup.startOver(getNewStorageServiceConfig());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to restore backup:', error);
|
console.error('Failed to restore backup:', error);
|
||||||
@@ -79,7 +78,7 @@ export class BackupsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async deleteBackup(backupId: string): Promise<void> {
|
async deleteBackup(backupId: string): Promise<void> {
|
||||||
await this.#storage.getSignerMetaHandler().deleteBackup(backupId);
|
await this.storage.getSignerMetaHandler().deleteBackup(backupId);
|
||||||
this.loadBackups();
|
this.loadBackups();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +119,7 @@ export class BackupsComponent implements OnInit {
|
|||||||
|
|
||||||
async onClickLock(): Promise<void> {
|
async onClickLock(): Promise<void> {
|
||||||
this.#logger.logVaultLock();
|
this.#logger.logVaultLock();
|
||||||
await this.#storage.lockVault();
|
await this.storage.lockVault();
|
||||||
this.#router.navigateByUrl('/vault-login');
|
this.#router.navigateByUrl('/vault-login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->
|
<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->
|
||||||
<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->
|
<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->
|
||||||
<div class="sam-text-header">
|
<div class="sam-text-header">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<span>Bookmarks</span>
|
<span>Bookmarks</span>
|
||||||
<button class="add-btn" title="Bookmark This Page" (click)="onBookmarkThisPage()">
|
<button class="add-btn" title="Bookmark This Page" (click)="onBookmarkThisPage()">
|
||||||
<span class="emoji">➕</span>
|
<span class="emoji">➕</span>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, inject, OnInit } from '@angular/core';
|
import { Component, inject, OnInit } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { Bookmark, LoggerService, SignerMetaData, StorageService } from '@common';
|
import { Bookmark, LoggerService, NavComponent, SignerMetaData } from '@common';
|
||||||
import { ChromeMetaHandler } from '../../../common/data/chrome-meta-handler';
|
import { ChromeMetaHandler } from '../../../common/data/chrome-meta-handler';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -9,10 +9,9 @@ import { ChromeMetaHandler } from '../../../common/data/chrome-meta-handler';
|
|||||||
styleUrl: './bookmarks.component.scss',
|
styleUrl: './bookmarks.component.scss',
|
||||||
imports: [],
|
imports: [],
|
||||||
})
|
})
|
||||||
export class BookmarksComponent implements OnInit {
|
export class BookmarksComponent extends NavComponent implements OnInit {
|
||||||
readonly #logger = inject(LoggerService);
|
readonly #logger = inject(LoggerService);
|
||||||
readonly #metaHandler = new ChromeMetaHandler();
|
readonly #metaHandler = new ChromeMetaHandler();
|
||||||
readonly #storage = inject(StorageService);
|
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
|
|
||||||
bookmarks: Bookmark[] = [];
|
bookmarks: Bookmark[] = [];
|
||||||
@@ -93,7 +92,7 @@ export class BookmarksComponent implements OnInit {
|
|||||||
|
|
||||||
async onClickLock() {
|
async onClickLock() {
|
||||||
this.#logger.logVaultLock();
|
this.#logger.logVaultLock();
|
||||||
await this.#storage.lockVault();
|
await this.storage.lockVault();
|
||||||
this.#router.navigateByUrl('/vault-login');
|
this.#router.navigateByUrl('/vault-login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->
|
<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->
|
||||||
<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->
|
<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->
|
||||||
<div class="custom-header" style="position: sticky; top: 0">
|
<div class="custom-header" style="position: sticky; top: 0">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<span class="text">Identities</span>
|
<span class="text">Identities</span>
|
||||||
|
|
||||||
<button class="add-btn" title="New Identity" (click)="onClickNewIdentity()">
|
<button class="add-btn" title="New Identity" (click)="onClickNewIdentity()">
|
||||||
|
|||||||
@@ -19,9 +19,16 @@
|
|||||||
background: var(--background);
|
background: var(--background);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.lock-btn,
|
.header-buttons {
|
||||||
.add-btn {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-btn,
|
||||||
|
.add-btn {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
@@ -41,11 +48,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.lock-btn {
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-btn {
|
.add-btn {
|
||||||
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
Identity_DECRYPTED,
|
Identity_DECRYPTED,
|
||||||
LoggerService,
|
LoggerService,
|
||||||
|
NavComponent,
|
||||||
NostrHelper,
|
NostrHelper,
|
||||||
ProfileMetadata,
|
ProfileMetadata,
|
||||||
ProfileMetadataService,
|
ProfileMetadataService,
|
||||||
@@ -17,8 +18,8 @@ import {
|
|||||||
styleUrl: './identities.component.scss',
|
styleUrl: './identities.component.scss',
|
||||||
imports: [IconButtonComponent, ToastComponent],
|
imports: [IconButtonComponent, ToastComponent],
|
||||||
})
|
})
|
||||||
export class IdentitiesComponent implements OnInit {
|
export class IdentitiesComponent extends NavComponent implements OnInit {
|
||||||
readonly storage = inject(StorageService);
|
override readonly storage = inject(StorageService);
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
readonly #profileMetadata = inject(ProfileMetadataService);
|
readonly #profileMetadata = inject(ProfileMetadataService);
|
||||||
readonly #logger = inject(LoggerService);
|
readonly #logger = inject(LoggerService);
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->
|
<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->
|
||||||
<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->
|
<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->
|
||||||
<div class="sam-text-header">
|
<div class="sam-text-header">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<span>You</span>
|
<span>You</span>
|
||||||
<button class="edit-btn" title="Edit profile" (click)="onClickEditProfile()">
|
<button class="edit-btn" title="Edit profile" (click)="onClickEditProfile()">
|
||||||
<span class="emoji">📝</span>
|
<span class="emoji">📝</span>
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import { Router } from '@angular/router';
|
|||||||
import {
|
import {
|
||||||
Identity_DECRYPTED,
|
Identity_DECRYPTED,
|
||||||
LoggerService,
|
LoggerService,
|
||||||
|
NavComponent,
|
||||||
NostrHelper,
|
NostrHelper,
|
||||||
ProfileMetadata,
|
ProfileMetadata,
|
||||||
ProfileMetadataService,
|
ProfileMetadataService,
|
||||||
PubkeyComponent,
|
PubkeyComponent,
|
||||||
StorageService,
|
|
||||||
ToastComponent,
|
ToastComponent,
|
||||||
VisualNip05Pipe,
|
VisualNip05Pipe,
|
||||||
validateNip05,
|
validateNip05,
|
||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
templateUrl: './identity.component.html',
|
templateUrl: './identity.component.html',
|
||||||
styleUrl: './identity.component.scss',
|
styleUrl: './identity.component.scss',
|
||||||
})
|
})
|
||||||
export class IdentityComponent implements OnInit {
|
export class IdentityComponent extends NavComponent implements OnInit {
|
||||||
selectedIdentity: Identity_DECRYPTED | undefined;
|
selectedIdentity: Identity_DECRYPTED | undefined;
|
||||||
selectedIdentityNpub: string | undefined;
|
selectedIdentityNpub: string | undefined;
|
||||||
profile: ProfileMetadata | null = null;
|
profile: ProfileMetadata | null = null;
|
||||||
@@ -27,7 +27,6 @@ export class IdentityComponent implements OnInit {
|
|||||||
validating = false;
|
validating = false;
|
||||||
loading = true;
|
loading = true;
|
||||||
|
|
||||||
readonly #storage = inject(StorageService);
|
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
readonly #profileMetadata = inject(ProfileMetadataService);
|
readonly #profileMetadata = inject(ProfileMetadataService);
|
||||||
readonly #logger = inject(LoggerService);
|
readonly #logger = inject(LoggerService);
|
||||||
@@ -82,17 +81,17 @@ export class IdentityComponent implements OnInit {
|
|||||||
|
|
||||||
async onClickLock() {
|
async onClickLock() {
|
||||||
this.#logger.logVaultLock();
|
this.#logger.logVaultLock();
|
||||||
await this.#storage.lockVault();
|
await this.storage.lockVault();
|
||||||
this.#router.navigateByUrl('/vault-login');
|
this.#router.navigateByUrl('/vault-login');
|
||||||
}
|
}
|
||||||
|
|
||||||
async #loadData() {
|
async #loadData() {
|
||||||
try {
|
try {
|
||||||
const selectedIdentityId =
|
const selectedIdentityId =
|
||||||
this.#storage.getBrowserSessionHandler().browserSessionData
|
this.storage.getBrowserSessionHandler().browserSessionData
|
||||||
?.selectedIdentityId ?? null;
|
?.selectedIdentityId ?? null;
|
||||||
|
|
||||||
const identity = this.#storage
|
const identity = this.storage
|
||||||
.getBrowserSessionHandler()
|
.getBrowserSessionHandler()
|
||||||
.browserSessionData?.identities.find(
|
.browserSessionData?.identities.find(
|
||||||
(x) => x.id === selectedIdentityId
|
(x) => x.id === selectedIdentityId
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
<div class="sam-text-header">
|
<div class="sam-text-header">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<span> Plebeian Signer </span>
|
<span> Plebeian Signer </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, inject } from '@angular/core';
|
import { Component, inject } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { LoggerService, StorageService } from '@common';
|
import { LoggerService, NavComponent } from '@common';
|
||||||
import packageJson from '../../../../../../../package.json';
|
import packageJson from '../../../../../../../package.json';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -8,16 +8,15 @@ import packageJson from '../../../../../../../package.json';
|
|||||||
templateUrl: './info.component.html',
|
templateUrl: './info.component.html',
|
||||||
styleUrl: './info.component.scss',
|
styleUrl: './info.component.scss',
|
||||||
})
|
})
|
||||||
export class InfoComponent {
|
export class InfoComponent extends NavComponent {
|
||||||
readonly #logger = inject(LoggerService);
|
readonly #logger = inject(LoggerService);
|
||||||
readonly #storage = inject(StorageService);
|
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
|
|
||||||
version = packageJson.custom.chrome.version;
|
version = packageJson.custom.chrome.version;
|
||||||
|
|
||||||
async onClickLock() {
|
async onClickLock() {
|
||||||
this.#logger.logVaultLock();
|
this.#logger.logVaultLock();
|
||||||
await this.#storage.lockVault();
|
await this.storage.lockVault();
|
||||||
this.#router.navigateByUrl('/vault-login');
|
this.#router.navigateByUrl('/vault-login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
<div class="sam-text-header">
|
<div class="sam-text-header">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<span>Logs</span>
|
<span>Logs</span>
|
||||||
<div class="logs-actions">
|
<div class="logs-actions">
|
||||||
<button class="btn btn-sm btn-secondary" title="Refresh logs" (click)="onRefresh()">Refresh</button>
|
<button class="btn btn-sm btn-secondary" title="Refresh logs" (click)="onRefresh()">Refresh</button>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, inject, OnInit } from '@angular/core';
|
import { Component, inject, OnInit } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { LoggerService, LogEntry, StorageService } from '@common';
|
import { LoggerService, LogEntry, NavComponent } from '@common';
|
||||||
import { DatePipe } from '@angular/common';
|
import { DatePipe } from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -9,9 +9,8 @@ import { DatePipe } from '@angular/common';
|
|||||||
styleUrl: './logs.component.scss',
|
styleUrl: './logs.component.scss',
|
||||||
imports: [DatePipe],
|
imports: [DatePipe],
|
||||||
})
|
})
|
||||||
export class LogsComponent implements OnInit {
|
export class LogsComponent extends NavComponent implements OnInit {
|
||||||
readonly #logger = inject(LoggerService);
|
readonly #logger = inject(LoggerService);
|
||||||
readonly #storage = inject(StorageService);
|
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
|
|
||||||
get logs(): LogEntry[] {
|
get logs(): LogEntry[] {
|
||||||
@@ -46,7 +45,7 @@ export class LogsComponent implements OnInit {
|
|||||||
|
|
||||||
async onClickLock() {
|
async onClickLock() {
|
||||||
this.#logger.logVaultLock();
|
this.#logger.logVaultLock();
|
||||||
await this.#storage.lockVault();
|
await this.storage.lockVault();
|
||||||
this.#router.navigateByUrl('/vault-login');
|
this.#router.navigateByUrl('/vault-login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,39 @@
|
|||||||
<div class="sam-text-header">
|
<div class="sam-text-header">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<span> Settings </span>
|
<span> Settings </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="vault-buttons">
|
||||||
|
<button class="btn btn-primary" (click)="onClickExportVault()">
|
||||||
|
Export Vault
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary" (click)="navigate('/vault-import')">
|
||||||
|
Import Vault
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<lib-nav-item text="💾 Backups" (click)="navigate('/home/backups')"></lib-nav-item>
|
||||||
|
<lib-nav-item text="🪵 Logs" (click)="navigate('/home/logs')"></lib-nav-item>
|
||||||
|
<lib-nav-item text="💡 Info" (click)="navigate('/home/info')"></lib-nav-item>
|
||||||
|
|
||||||
|
<div class="dev-mode-row">
|
||||||
|
<label class="toggle-label">
|
||||||
|
<input type="checkbox" [checked]="devMode" (change)="onToggleDevMode($event)" />
|
||||||
|
<span>Dev Mode</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sam-flex-grow"></div>
|
||||||
|
|
||||||
<div class="sync-info">
|
<div class="sync-info">
|
||||||
<span class="sync-label">SYNC: {{ syncFlow }}</span>
|
<span class="sync-label">SYNC: {{ syncFlow }}</span>
|
||||||
<p class="sync-note">
|
<p class="sync-note">
|
||||||
@@ -13,20 +42,6 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-primary" (click)="onClickExportVault()">
|
|
||||||
Export Vault
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="btn btn-primary" (click)="navigate('/vault-import')">
|
|
||||||
Import Vault
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<lib-nav-item text="💾 Backups" (click)="navigate('/home/backups')"></lib-nav-item>
|
|
||||||
<lib-nav-item text="🪵 Logs" (click)="navigate('/home/logs')"></lib-nav-item>
|
|
||||||
<lib-nav-item text="💡 Info" (click)="navigate('/home/info')"></lib-nav-item>
|
|
||||||
|
|
||||||
<div class="sam-flex-grow"></div>
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-danger"
|
class="btn btn-danger"
|
||||||
(click)="
|
(click)="
|
||||||
|
|||||||
@@ -16,6 +16,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vault-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--size);
|
||||||
|
|
||||||
|
button {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-mode-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--size);
|
||||||
|
|
||||||
|
.toggle-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--size-h);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.sync-info {
|
.sync-info {
|
||||||
.sync-label {
|
.sync-label {
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
StorageService,
|
StorageService,
|
||||||
} from '@common';
|
} from '@common';
|
||||||
import { getNewStorageServiceConfig } from '../../../common/data/get-new-storage-service-config';
|
import { getNewStorageServiceConfig } from '../../../common/data/get-new-storage-service-config';
|
||||||
|
import { Buffer } from 'buffer';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-settings',
|
selector: 'app-settings',
|
||||||
@@ -22,6 +23,7 @@ import { getNewStorageServiceConfig } from '../../../common/data/get-new-storage
|
|||||||
export class SettingsComponent extends NavComponent implements OnInit {
|
export class SettingsComponent extends NavComponent implements OnInit {
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
syncFlow: string | undefined;
|
syncFlow: string | undefined;
|
||||||
|
override devMode = false;
|
||||||
|
|
||||||
readonly #storage = inject(StorageService);
|
readonly #storage = inject(StorageService);
|
||||||
readonly #startup = inject(StartupService);
|
readonly #startup = inject(StartupService);
|
||||||
@@ -45,6 +47,44 @@ export class SettingsComponent extends NavComponent implements OnInit {
|
|||||||
default:
|
default:
|
||||||
break;
|
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() {
|
async onResetExtension() {
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
<div class="sam-text-header">
|
<div class="sam-text-header">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
@if (showBackButton) {
|
@if (showBackButton) {
|
||||||
<button class="back-btn" title="Go Back" (click)="goBack()">
|
<button class="back-btn" title="Go Back" (click)="goBack()">
|
||||||
<span class="emoji">←</span>
|
<span class="emoji">←</span>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { FormsModule } from '@angular/forms';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import {
|
import {
|
||||||
LoggerService,
|
LoggerService,
|
||||||
StorageService,
|
NavComponent,
|
||||||
NwcService,
|
NwcService,
|
||||||
NwcConnection_DECRYPTED,
|
NwcConnection_DECRYPTED,
|
||||||
CashuService,
|
CashuService,
|
||||||
@@ -35,9 +35,8 @@ type WalletSection =
|
|||||||
styleUrl: './wallet.component.scss',
|
styleUrl: './wallet.component.scss',
|
||||||
imports: [CommonModule, FormsModule],
|
imports: [CommonModule, FormsModule],
|
||||||
})
|
})
|
||||||
export class WalletComponent implements OnInit, OnDestroy {
|
export class WalletComponent extends NavComponent implements OnInit, OnDestroy {
|
||||||
readonly #logger = inject(LoggerService);
|
readonly #logger = inject(LoggerService);
|
||||||
readonly #storage = inject(StorageService);
|
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
readonly nwcService = inject(NwcService);
|
readonly nwcService = inject(NwcService);
|
||||||
readonly cashuService = inject(CashuService);
|
readonly cashuService = inject(CashuService);
|
||||||
@@ -195,7 +194,7 @@ export class WalletComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// Load current sync flow setting
|
// Load current sync flow setting
|
||||||
this.currentSyncFlow = this.#storage.getSyncFlow();
|
this.currentSyncFlow = this.storage.getSyncFlow();
|
||||||
|
|
||||||
// Refresh balances on init if we have connections
|
// Refresh balances on init if we have connections
|
||||||
if (this.connections.length > 0) {
|
if (this.connections.length > 0) {
|
||||||
@@ -937,7 +936,7 @@ export class WalletComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
async onClickLock() {
|
async onClickLock() {
|
||||||
this.#logger.logVaultLock();
|
this.#logger.logVaultLock();
|
||||||
await this.#storage.lockVault();
|
await this.storage.lockVault();
|
||||||
this.#router.navigateByUrl('/vault-login');
|
this.#router.navigateByUrl('/vault-login');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export class WhitelistedAppsComponent extends NavComponent {
|
|||||||
@ViewChild('toast') toast!: ToastComponent;
|
@ViewChild('toast') toast!: ToastComponent;
|
||||||
@ViewChild('confirm') confirm!: ConfirmComponent;
|
@ViewChild('confirm') confirm!: ConfirmComponent;
|
||||||
|
|
||||||
readonly storage = inject(StorageService);
|
override readonly storage = inject(StorageService);
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
|
|
||||||
get whitelistedHosts(): string[] {
|
get whitelistedHosts(): string[] {
|
||||||
|
|||||||
@@ -1,8 +1,29 @@
|
|||||||
import { inject } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
import { StorageService } from '../services/storage/storage.service';
|
||||||
|
import { Buffer } from 'buffer';
|
||||||
|
|
||||||
|
declare const chrome: {
|
||||||
|
windows: {
|
||||||
|
create: (options: {
|
||||||
|
type: string;
|
||||||
|
url: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
left: number;
|
||||||
|
top: number;
|
||||||
|
}) => void;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export class NavComponent {
|
export class NavComponent {
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
|
protected readonly storage = inject(StorageService);
|
||||||
|
devMode = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.devMode = this.storage.getSignerMetaHandler().signerMetaData?.devMode ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
navigateBack() {
|
navigateBack() {
|
||||||
window.history.back();
|
window.history.back();
|
||||||
@@ -11,4 +32,32 @@ export class NavComponent {
|
|||||||
navigate(path: string) {
|
navigate(path: string) {
|
||||||
this.#router.navigate([path]);
|
this.#router.navigate([path]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onTestPrompt() {
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export abstract class SignerMetaHandler {
|
|||||||
|
|
||||||
#signerMetaData?: SignerMetaData;
|
#signerMetaData?: SignerMetaData;
|
||||||
|
|
||||||
readonly metaProperties = ['syncFlow', 'vaultSnapshots', 'maxBackups', 'recklessMode', 'whitelistedHosts', 'bookmarks'];
|
readonly metaProperties = ['syncFlow', 'vaultSnapshots', 'maxBackups', 'recklessMode', 'whitelistedHosts', 'bookmarks', 'devMode'];
|
||||||
readonly DEFAULT_MAX_BACKUPS = 5;
|
readonly DEFAULT_MAX_BACKUPS = 5;
|
||||||
/**
|
/**
|
||||||
* Load the full data from the storage. If the storage is used for storing
|
* Load the full data from the storage. If the storage is used for storing
|
||||||
@@ -58,6 +58,21 @@ export abstract class SignerMetaHandler {
|
|||||||
await this.saveFullData(this.#signerMetaData);
|
await this.saveFullData(this.#signerMetaData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets dev mode and immediately saves it.
|
||||||
|
*/
|
||||||
|
async setDevMode(enabled: boolean): Promise<void> {
|
||||||
|
if (!this.#signerMetaData) {
|
||||||
|
this.#signerMetaData = {
|
||||||
|
devMode: enabled,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this.#signerMetaData.devMode = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.saveFullData(this.#signerMetaData);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a host to the whitelist and immediately saves it.
|
* Adds a host to the whitelist and immediately saves it.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -202,6 +202,9 @@ export interface SignerMetaData {
|
|||||||
|
|
||||||
// User bookmarks
|
// User bookmarks
|
||||||
bookmarks?: Bookmark[];
|
bookmarks?: Bookmark[];
|
||||||
|
|
||||||
|
// Dev mode: show test permission prompt button in settings
|
||||||
|
devMode?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,9 +16,16 @@
|
|||||||
letter-spacing: 0.1rem;
|
letter-spacing: 0.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lock-btn {
|
.header-buttons {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lock-btn,
|
||||||
|
.header-btn {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
@@ -37,6 +44,12 @@
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For backwards compatibility with single lock-btn
|
||||||
|
> .lock-btn {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sam-footer-grid-2 {
|
.sam-footer-grid-2 {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Plebeian Signer",
|
"name": "Plebeian Signer",
|
||||||
"description": "Nostr Identity Manager & Signer",
|
"description": "Nostr Identity Manager & Signer",
|
||||||
"version": "1.0.10",
|
"version": "1.0.11",
|
||||||
"homepage_url": "https://github.com/PlebeianApp/plebeian-signer",
|
"homepage_url": "https://github.com/PlebeianApp/plebeian-signer",
|
||||||
"options_page": "options.html",
|
"options_page": "options.html",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
|
|||||||
@@ -27,11 +27,66 @@
|
|||||||
.page {
|
.page {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 1fr 60px;
|
grid-template-rows: 1fr auto;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
padding: var(--size);
|
||||||
|
background: var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-label {
|
||||||
|
width: 60px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons button {
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reject {
|
||||||
|
background: var(--muted);
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reject:hover {
|
||||||
|
background: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-accept {
|
||||||
|
background: var(--primary);
|
||||||
|
color: var(--primary-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-accept:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
padding: var(--size);
|
padding: var(--size);
|
||||||
background: var(--background-light);
|
background: var(--background-light);
|
||||||
@@ -54,6 +109,12 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: gray;
|
color: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -63,64 +124,31 @@
|
|||||||
<span id="titleSpan" style="font-weight: 400 !important"></span>
|
<span id="titleSpan" style="font-weight: 400 !important"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span
|
|
||||||
class="host-INSERT sam-align-self-center sam-text-muted"
|
|
||||||
style="font-weight: 500"
|
|
||||||
></span>
|
|
||||||
|
|
||||||
<!-- Card for getPublicKey -->
|
<!-- Card for getPublicKey -->
|
||||||
<div id="cardGetPublicKey" class="card sam-mt sam-ml sam-mr">
|
<div id="cardGetPublicKey" class="card sam-mt sam-ml sam-mr">
|
||||||
<span style="text-align: center">
|
<p class="description">
|
||||||
<b><span class="host-INSERT color-primary"></span></b>
|
<b class="host-INSERT color-primary"></b> is requesting permission to
|
||||||
is requesting permission to<br />
|
<b class="color-primary">read your public key</b> for the selected identity
|
||||||
<br />
|
<b class="nick-INSERT color-primary"></b>.
|
||||||
<b class="color-primary">read your public key</b> <br />
|
</p>
|
||||||
<br />
|
|
||||||
<span>
|
|
||||||
for the selected identity
|
|
||||||
<span
|
|
||||||
style="font-weight: 500"
|
|
||||||
class="nick-INSERT color-primary"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card for getRelays -->
|
<!-- Card for getRelays -->
|
||||||
<div id="cardGetRelays" class="card sam-mt sam-ml sam-mr">
|
<div id="cardGetRelays" class="card sam-mt sam-ml sam-mr">
|
||||||
<span style="text-align: center">
|
<p class="description">
|
||||||
<b><span class="host-INSERT color-primary"></span></b>
|
<b class="host-INSERT color-primary"></b> is requesting permission to
|
||||||
is requesting permission to<br />
|
<b class="color-primary">read your relays</b> for the selected identity
|
||||||
<br />
|
<b class="nick-INSERT color-primary"></b>.
|
||||||
<b class="color-primary">read your relays</b> <br />
|
</p>
|
||||||
<br />
|
|
||||||
<span>
|
|
||||||
for the selected identity
|
|
||||||
<span
|
|
||||||
style="font-weight: 500"
|
|
||||||
class="nick-INSERT color-primary"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card for signEvent -->
|
<!-- Card for signEvent -->
|
||||||
<div id="cardSignEvent" class="card sam-mt sam-ml sam-mr">
|
<div id="cardSignEvent" class="card sam-mt sam-ml sam-mr">
|
||||||
<span style="text-align: center">
|
<p class="description">
|
||||||
<b><span class="host-INSERT color-primary"></span></b>
|
<b class="host-INSERT color-primary"></b> is requesting permission to
|
||||||
is requesting permission to<br />
|
<b class="color-primary">sign an event</b> (kind <span id="kindSpan"></span>)
|
||||||
<br />
|
for the selected identity <b class="nick-INSERT color-primary"></b>.
|
||||||
<b class="color-primary">sign an event</b> (kind
|
</p>
|
||||||
<span id="kindSpan"></span>) <br />
|
|
||||||
<br />
|
|
||||||
<span>
|
|
||||||
for the selected identity
|
|
||||||
<span
|
|
||||||
style="font-weight: 500"
|
|
||||||
class="nick-INSERT color-primary"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card2 for signEvent -->
|
<!-- Card2 for signEvent -->
|
||||||
@@ -130,20 +158,11 @@
|
|||||||
|
|
||||||
<!-- Card for nip04.encrypt -->
|
<!-- Card for nip04.encrypt -->
|
||||||
<div id="cardNip04Encrypt" class="card sam-mt sam-ml sam-mr">
|
<div id="cardNip04Encrypt" class="card sam-mt sam-ml sam-mr">
|
||||||
<span style="text-align: center">
|
<p class="description">
|
||||||
<b><span class="host-INSERT color-primary"></span></b>
|
<b class="host-INSERT color-primary"></b> is requesting permission to
|
||||||
is requesting permission to<br />
|
<b class="color-primary">encrypt a text</b> (NIP04) for the selected identity
|
||||||
<br />
|
<b class="nick-INSERT color-primary"></b>.
|
||||||
<b class="color-primary">encrypt a text</b> (NIP04) <br />
|
</p>
|
||||||
<br />
|
|
||||||
<span>
|
|
||||||
for the selected identity
|
|
||||||
<span
|
|
||||||
style="font-weight: 500"
|
|
||||||
class="nick-INSERT color-primary"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card2 for nip04.encrypt -->
|
<!-- Card2 for nip04.encrypt -->
|
||||||
@@ -153,20 +172,11 @@
|
|||||||
|
|
||||||
<!-- Card for nip44.encrypt -->
|
<!-- Card for nip44.encrypt -->
|
||||||
<div id="cardNip44Encrypt" class="card sam-mt sam-ml sam-mr">
|
<div id="cardNip44Encrypt" class="card sam-mt sam-ml sam-mr">
|
||||||
<span style="text-align: center">
|
<p class="description">
|
||||||
<b><span class="host-INSERT color-primary"></span></b>
|
<b class="host-INSERT color-primary"></b> is requesting permission to
|
||||||
is requesting permission to<br />
|
<b class="color-primary">encrypt a text</b> (NIP44) for the selected identity
|
||||||
<br />
|
<b class="nick-INSERT color-primary"></b>.
|
||||||
<b class="color-primary">encrypt a text</b> (NIP44) <br />
|
</p>
|
||||||
<br />
|
|
||||||
<span>
|
|
||||||
for the selected identity
|
|
||||||
<span
|
|
||||||
style="font-weight: 500"
|
|
||||||
class="nick-INSERT color-primary"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card2 for nip44.encrypt -->
|
<!-- Card2 for nip44.encrypt -->
|
||||||
@@ -176,20 +186,11 @@
|
|||||||
|
|
||||||
<!-- Card for nip04.decrypt -->
|
<!-- Card for nip04.decrypt -->
|
||||||
<div id="cardNip04Decrypt" class="card sam-mt sam-ml sam-mr">
|
<div id="cardNip04Decrypt" class="card sam-mt sam-ml sam-mr">
|
||||||
<span style="text-align: center">
|
<p class="description">
|
||||||
<b><span class="host-INSERT color-primary"></span></b>
|
<b class="host-INSERT color-primary"></b> is requesting permission to
|
||||||
is requesting permission to<br />
|
<b class="color-primary">decrypt a text</b> (NIP04) for the selected identity
|
||||||
<br />
|
<b class="nick-INSERT color-primary"></b>.
|
||||||
<b class="color-primary">decrypt a text</b> (NIP04) <br />
|
</p>
|
||||||
<br />
|
|
||||||
<span>
|
|
||||||
for the selected identity
|
|
||||||
<span
|
|
||||||
style="font-weight: 500"
|
|
||||||
class="nick-INSERT color-primary"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card2 for nip04.decrypt -->
|
<!-- Card2 for nip04.decrypt -->
|
||||||
@@ -199,20 +200,11 @@
|
|||||||
|
|
||||||
<!-- Card for nip44.decrypt -->
|
<!-- Card for nip44.decrypt -->
|
||||||
<div id="cardNip44Decrypt" class="card sam-mt sam-ml sam-mr">
|
<div id="cardNip44Decrypt" class="card sam-mt sam-ml sam-mr">
|
||||||
<span style="text-align: center">
|
<p class="description">
|
||||||
<b><span class="host-INSERT color-primary"></span></b>
|
<b class="host-INSERT color-primary"></b> is requesting permission to
|
||||||
is requesting permission to<br />
|
<b class="color-primary">decrypt a text</b> (NIP44) for the selected identity
|
||||||
<br />
|
<b class="nick-INSERT color-primary"></b>.
|
||||||
<b class="color-primary">decrypt a text</b> (NIP44) <br />
|
</p>
|
||||||
<br />
|
|
||||||
<span>
|
|
||||||
for the selected identity
|
|
||||||
<span
|
|
||||||
style="font-weight: 500"
|
|
||||||
class="nick-INSERT color-primary"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card2 for nip44.decrypt -->
|
<!-- Card2 for nip44.decrypt -->
|
||||||
@@ -224,47 +216,20 @@
|
|||||||
<!------------->
|
<!------------->
|
||||||
<!-- ACTIONS -->
|
<!-- ACTIONS -->
|
||||||
<!------------->
|
<!------------->
|
||||||
<div class="sam-footer-grid-2">
|
<div class="actions">
|
||||||
<div class="btn-group">
|
<div class="action-row">
|
||||||
<button id="rejectOnceButton" type="button" class="btn btn-secondary">
|
<span class="action-label">Reject</span>
|
||||||
Reject
|
<div class="action-buttons">
|
||||||
</button>
|
<button id="rejectOnceButton" type="button" class="btn-reject">Once</button>
|
||||||
<button
|
<button id="rejectAlwaysButton" type="button" class="btn-reject">Always</button>
|
||||||
type="button"
|
</div>
|
||||||
class="btn btn-secondary dropdown-toggle dropdown-toggle-split"
|
|
||||||
data-bs-toggle="dropdown"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
<span class="visually-hidden">Toggle Dropdown</span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-end">
|
|
||||||
<li>
|
|
||||||
<button id="rejectAlwaysButton" class="dropdown-item">
|
|
||||||
Reject Always
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="action-row">
|
||||||
<div class="btn-group">
|
<span class="action-label">Accept</span>
|
||||||
<button id="approveAlwaysButton" type="button" class="btn btn-primary">
|
<div class="action-buttons">
|
||||||
Approve Always
|
<button id="approveOnceButton" type="button" class="btn-accept">Once</button>
|
||||||
</button>
|
<button id="approveAlwaysButton" type="button" class="btn-accept">Always</button>
|
||||||
<button
|
</div>
|
||||||
type="button"
|
|
||||||
class="btn btn-primary dropdown-toggle dropdown-toggle-split"
|
|
||||||
data-bs-toggle="dropdown"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
<span class="visually-hidden">Toggle Dropdown</span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li>
|
|
||||||
<button id="approveOnceButton" class="dropdown-item">
|
|
||||||
Approve Once
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
<div class="sam-text-header">
|
<div class="sam-text-header">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<button class="back-btn" title="Go Back" (click)="goBack()">
|
<button class="back-btn" title="Go Back" (click)="goBack()">
|
||||||
<span class="emoji">←</span>
|
<span class="emoji">←</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { Router } from '@angular/router';
|
|||||||
import {
|
import {
|
||||||
ConfirmComponent,
|
ConfirmComponent,
|
||||||
LoggerService,
|
LoggerService,
|
||||||
|
NavComponent,
|
||||||
SignerMetaData_VaultSnapshot,
|
SignerMetaData_VaultSnapshot,
|
||||||
StartupService,
|
StartupService,
|
||||||
StorageService,
|
|
||||||
} from '@common';
|
} from '@common';
|
||||||
import { getNewStorageServiceConfig } from '../../../common/data/get-new-storage-service-config';
|
import { getNewStorageServiceConfig } from '../../../common/data/get-new-storage-service-config';
|
||||||
|
|
||||||
@@ -15,9 +15,8 @@ import { getNewStorageServiceConfig } from '../../../common/data/get-new-storage
|
|||||||
styleUrl: './backups.component.scss',
|
styleUrl: './backups.component.scss',
|
||||||
imports: [ConfirmComponent],
|
imports: [ConfirmComponent],
|
||||||
})
|
})
|
||||||
export class BackupsComponent implements OnInit {
|
export class BackupsComponent extends NavComponent implements OnInit {
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
readonly #storage = inject(StorageService);
|
|
||||||
readonly #startup = inject(StartupService);
|
readonly #startup = inject(StartupService);
|
||||||
readonly #logger = inject(LoggerService);
|
readonly #logger = inject(LoggerService);
|
||||||
|
|
||||||
@@ -27,11 +26,11 @@ export class BackupsComponent implements OnInit {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loadBackups();
|
this.loadBackups();
|
||||||
this.maxBackups = this.#storage.getSignerMetaHandler().getMaxBackups();
|
this.maxBackups = this.storage.getSignerMetaHandler().getMaxBackups();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadBackups(): void {
|
loadBackups(): void {
|
||||||
this.backups = this.#storage.getSignerMetaHandler().getBackups();
|
this.backups = this.storage.getSignerMetaHandler().getBackups();
|
||||||
}
|
}
|
||||||
|
|
||||||
async onMaxBackupsChange(event: Event): Promise<void> {
|
async onMaxBackupsChange(event: Event): Promise<void> {
|
||||||
@@ -39,14 +38,14 @@ export class BackupsComponent implements OnInit {
|
|||||||
const value = parseInt(input.value, 10);
|
const value = parseInt(input.value, 10);
|
||||||
if (!isNaN(value) && value >= 1 && value <= 20) {
|
if (!isNaN(value) && value >= 1 && value <= 20) {
|
||||||
this.maxBackups = value;
|
this.maxBackups = value;
|
||||||
await this.#storage.getSignerMetaHandler().setMaxBackups(value);
|
await this.storage.getSignerMetaHandler().setMaxBackups(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createManualBackup(): Promise<void> {
|
async createManualBackup(): Promise<void> {
|
||||||
const browserSyncData = this.#storage.getBrowserSyncHandler().browserSyncData;
|
const browserSyncData = this.storage.getBrowserSyncHandler().browserSyncData;
|
||||||
if (browserSyncData) {
|
if (browserSyncData) {
|
||||||
await this.#storage.getSignerMetaHandler().createBackup(browserSyncData, 'manual');
|
await this.storage.getSignerMetaHandler().createBackup(browserSyncData, 'manual');
|
||||||
this.loadBackups();
|
this.loadBackups();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,22 +54,22 @@ export class BackupsComponent implements OnInit {
|
|||||||
this.restoringBackupId = backupId;
|
this.restoringBackupId = backupId;
|
||||||
try {
|
try {
|
||||||
// First, create a pre-restore backup of current state
|
// First, create a pre-restore backup of current state
|
||||||
const currentData = this.#storage.getBrowserSyncHandler().browserSyncData;
|
const currentData = this.storage.getBrowserSyncHandler().browserSyncData;
|
||||||
if (currentData) {
|
if (currentData) {
|
||||||
await this.#storage.getSignerMetaHandler().createBackup(currentData, 'pre-restore');
|
await this.storage.getSignerMetaHandler().createBackup(currentData, 'pre-restore');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the backup data
|
// Get the backup data
|
||||||
const backupData = this.#storage.getSignerMetaHandler().getBackupData(backupId);
|
const backupData = this.storage.getSignerMetaHandler().getBackupData(backupId);
|
||||||
if (!backupData) {
|
if (!backupData) {
|
||||||
throw new Error('Backup not found');
|
throw new Error('Backup not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import the backup
|
// Import the backup
|
||||||
await this.#storage.deleteVault(true);
|
await this.storage.deleteVault(true);
|
||||||
await this.#storage.importVault(backupData);
|
await this.storage.importVault(backupData);
|
||||||
this.#logger.logVaultImport('Backup Restore');
|
this.#logger.logVaultImport('Backup Restore');
|
||||||
this.#storage.isInitialized = false;
|
this.storage.isInitialized = false;
|
||||||
this.#startup.startOver(getNewStorageServiceConfig());
|
this.#startup.startOver(getNewStorageServiceConfig());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to restore backup:', error);
|
console.error('Failed to restore backup:', error);
|
||||||
@@ -79,7 +78,7 @@ export class BackupsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async deleteBackup(backupId: string): Promise<void> {
|
async deleteBackup(backupId: string): Promise<void> {
|
||||||
await this.#storage.getSignerMetaHandler().deleteBackup(backupId);
|
await this.storage.getSignerMetaHandler().deleteBackup(backupId);
|
||||||
this.loadBackups();
|
this.loadBackups();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +119,7 @@ export class BackupsComponent implements OnInit {
|
|||||||
|
|
||||||
async onClickLock(): Promise<void> {
|
async onClickLock(): Promise<void> {
|
||||||
this.#logger.logVaultLock();
|
this.#logger.logVaultLock();
|
||||||
await this.#storage.lockVault();
|
await this.storage.lockVault();
|
||||||
this.#router.navigateByUrl('/vault-login');
|
this.#router.navigateByUrl('/vault-login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->
|
<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->
|
||||||
<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->
|
<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->
|
||||||
<div class="sam-text-header">
|
<div class="sam-text-header">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<span>Bookmarks</span>
|
<span>Bookmarks</span>
|
||||||
<button class="add-btn" title="Bookmark This Page" (click)="onBookmarkThisPage()">
|
<button class="add-btn" title="Bookmark This Page" (click)="onBookmarkThisPage()">
|
||||||
<span class="emoji">➕</span>
|
<span class="emoji">➕</span>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, inject, OnInit } from '@angular/core';
|
import { Component, inject, OnInit } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { Bookmark, LoggerService, SignerMetaData, StorageService } from '@common';
|
import { Bookmark, LoggerService, NavComponent, SignerMetaData } from '@common';
|
||||||
import { FirefoxMetaHandler } from '../../../common/data/firefox-meta-handler';
|
import { FirefoxMetaHandler } from '../../../common/data/firefox-meta-handler';
|
||||||
import browser from 'webextension-polyfill';
|
import browser from 'webextension-polyfill';
|
||||||
|
|
||||||
@@ -10,10 +10,9 @@ import browser from 'webextension-polyfill';
|
|||||||
styleUrl: './bookmarks.component.scss',
|
styleUrl: './bookmarks.component.scss',
|
||||||
imports: [],
|
imports: [],
|
||||||
})
|
})
|
||||||
export class BookmarksComponent implements OnInit {
|
export class BookmarksComponent extends NavComponent implements OnInit {
|
||||||
readonly #logger = inject(LoggerService);
|
readonly #logger = inject(LoggerService);
|
||||||
readonly #metaHandler = new FirefoxMetaHandler();
|
readonly #metaHandler = new FirefoxMetaHandler();
|
||||||
readonly #storage = inject(StorageService);
|
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
|
|
||||||
bookmarks: Bookmark[] = [];
|
bookmarks: Bookmark[] = [];
|
||||||
@@ -94,7 +93,7 @@ export class BookmarksComponent implements OnInit {
|
|||||||
|
|
||||||
async onClickLock() {
|
async onClickLock() {
|
||||||
this.#logger.logVaultLock();
|
this.#logger.logVaultLock();
|
||||||
await this.#storage.lockVault();
|
await this.storage.lockVault();
|
||||||
this.#router.navigateByUrl('/vault-login');
|
this.#router.navigateByUrl('/vault-login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->
|
<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->
|
||||||
<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->
|
<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->
|
||||||
<div class="custom-header" style="position: sticky; top: 0">
|
<div class="custom-header" style="position: sticky; top: 0">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<span class="text">Identities</span>
|
<span class="text">Identities</span>
|
||||||
|
|
||||||
<button class="add-btn" title="New Identity" (click)="onClickNewIdentity()">
|
<button class="add-btn" title="New Identity" (click)="onClickNewIdentity()">
|
||||||
|
|||||||
@@ -19,9 +19,16 @@
|
|||||||
background: var(--background);
|
background: var(--background);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.lock-btn,
|
.header-buttons {
|
||||||
.add-btn {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-btn,
|
||||||
|
.add-btn {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
@@ -41,11 +48,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.lock-btn {
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-btn {
|
.add-btn {
|
||||||
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
Identity_DECRYPTED,
|
Identity_DECRYPTED,
|
||||||
LoggerService,
|
LoggerService,
|
||||||
|
NavComponent,
|
||||||
NostrHelper,
|
NostrHelper,
|
||||||
ProfileMetadata,
|
ProfileMetadata,
|
||||||
ProfileMetadataService,
|
ProfileMetadataService,
|
||||||
@@ -17,8 +18,8 @@ import {
|
|||||||
templateUrl: './identities.component.html',
|
templateUrl: './identities.component.html',
|
||||||
styleUrl: './identities.component.scss',
|
styleUrl: './identities.component.scss',
|
||||||
})
|
})
|
||||||
export class IdentitiesComponent implements OnInit {
|
export class IdentitiesComponent extends NavComponent implements OnInit {
|
||||||
readonly storage = inject(StorageService);
|
override readonly storage = inject(StorageService);
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
readonly #profileMetadata = inject(ProfileMetadataService);
|
readonly #profileMetadata = inject(ProfileMetadataService);
|
||||||
readonly #logger = inject(LoggerService);
|
readonly #logger = inject(LoggerService);
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->
|
<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->
|
||||||
<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->
|
<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->
|
||||||
<div class="sam-text-header">
|
<div class="sam-text-header">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<span>You</span>
|
<span>You</span>
|
||||||
<button class="edit-btn" title="Edit profile" (click)="onClickEditProfile()">
|
<button class="edit-btn" title="Edit profile" (click)="onClickEditProfile()">
|
||||||
<span class="emoji">📝</span>
|
<span class="emoji">📝</span>
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import { Router } from '@angular/router';
|
|||||||
import {
|
import {
|
||||||
Identity_DECRYPTED,
|
Identity_DECRYPTED,
|
||||||
LoggerService,
|
LoggerService,
|
||||||
|
NavComponent,
|
||||||
NostrHelper,
|
NostrHelper,
|
||||||
ProfileMetadata,
|
ProfileMetadata,
|
||||||
ProfileMetadataService,
|
ProfileMetadataService,
|
||||||
PubkeyComponent,
|
PubkeyComponent,
|
||||||
StorageService,
|
|
||||||
ToastComponent,
|
ToastComponent,
|
||||||
VisualNip05Pipe,
|
VisualNip05Pipe,
|
||||||
validateNip05,
|
validateNip05,
|
||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
templateUrl: './identity.component.html',
|
templateUrl: './identity.component.html',
|
||||||
styleUrl: './identity.component.scss',
|
styleUrl: './identity.component.scss',
|
||||||
})
|
})
|
||||||
export class IdentityComponent implements OnInit {
|
export class IdentityComponent extends NavComponent implements OnInit {
|
||||||
selectedIdentity: Identity_DECRYPTED | undefined;
|
selectedIdentity: Identity_DECRYPTED | undefined;
|
||||||
selectedIdentityNpub: string | undefined;
|
selectedIdentityNpub: string | undefined;
|
||||||
profile: ProfileMetadata | null = null;
|
profile: ProfileMetadata | null = null;
|
||||||
@@ -27,7 +27,6 @@ export class IdentityComponent implements OnInit {
|
|||||||
validating = false;
|
validating = false;
|
||||||
loading = true;
|
loading = true;
|
||||||
|
|
||||||
readonly #storage = inject(StorageService);
|
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
readonly #profileMetadata = inject(ProfileMetadataService);
|
readonly #profileMetadata = inject(ProfileMetadataService);
|
||||||
readonly #logger = inject(LoggerService);
|
readonly #logger = inject(LoggerService);
|
||||||
@@ -82,17 +81,17 @@ export class IdentityComponent implements OnInit {
|
|||||||
|
|
||||||
async onClickLock() {
|
async onClickLock() {
|
||||||
this.#logger.logVaultLock();
|
this.#logger.logVaultLock();
|
||||||
await this.#storage.lockVault();
|
await this.storage.lockVault();
|
||||||
this.#router.navigateByUrl('/vault-login');
|
this.#router.navigateByUrl('/vault-login');
|
||||||
}
|
}
|
||||||
|
|
||||||
async #loadData() {
|
async #loadData() {
|
||||||
try {
|
try {
|
||||||
const selectedIdentityId =
|
const selectedIdentityId =
|
||||||
this.#storage.getBrowserSessionHandler().browserSessionData
|
this.storage.getBrowserSessionHandler().browserSessionData
|
||||||
?.selectedIdentityId ?? null;
|
?.selectedIdentityId ?? null;
|
||||||
|
|
||||||
const identity = this.#storage
|
const identity = this.storage
|
||||||
.getBrowserSessionHandler()
|
.getBrowserSessionHandler()
|
||||||
.browserSessionData?.identities.find(
|
.browserSessionData?.identities.find(
|
||||||
(x) => x.id === selectedIdentityId
|
(x) => x.id === selectedIdentityId
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
<div class="sam-text-header">
|
<div class="sam-text-header">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<span> Plebeian Signer </span>
|
<span> Plebeian Signer </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, inject } from '@angular/core';
|
import { Component, inject } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { LoggerService, StorageService } from '@common';
|
import { LoggerService, NavComponent } from '@common';
|
||||||
import packageJson from '../../../../../../../package.json';
|
import packageJson from '../../../../../../../package.json';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -8,16 +8,15 @@ import packageJson from '../../../../../../../package.json';
|
|||||||
templateUrl: './info.component.html',
|
templateUrl: './info.component.html',
|
||||||
styleUrl: './info.component.scss',
|
styleUrl: './info.component.scss',
|
||||||
})
|
})
|
||||||
export class InfoComponent {
|
export class InfoComponent extends NavComponent {
|
||||||
readonly #logger = inject(LoggerService);
|
readonly #logger = inject(LoggerService);
|
||||||
readonly #storage = inject(StorageService);
|
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
|
|
||||||
version = packageJson.custom.firefox.version;
|
version = packageJson.custom.firefox.version;
|
||||||
|
|
||||||
async onClickLock() {
|
async onClickLock() {
|
||||||
this.#logger.logVaultLock();
|
this.#logger.logVaultLock();
|
||||||
await this.#storage.lockVault();
|
await this.storage.lockVault();
|
||||||
this.#router.navigateByUrl('/vault-login');
|
this.#router.navigateByUrl('/vault-login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
<div class="sam-text-header">
|
<div class="sam-text-header">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<span>Logs</span>
|
<span>Logs</span>
|
||||||
<div class="logs-actions">
|
<div class="logs-actions">
|
||||||
<button class="btn btn-sm btn-secondary" title="Refresh logs" (click)="onRefresh()">Refresh</button>
|
<button class="btn btn-sm btn-secondary" title="Refresh logs" (click)="onRefresh()">Refresh</button>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, inject, OnInit } from '@angular/core';
|
import { Component, inject, OnInit } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { LoggerService, LogEntry, StorageService } from '@common';
|
import { LoggerService, LogEntry, NavComponent } from '@common';
|
||||||
import { DatePipe } from '@angular/common';
|
import { DatePipe } from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -9,9 +9,8 @@ import { DatePipe } from '@angular/common';
|
|||||||
styleUrl: './logs.component.scss',
|
styleUrl: './logs.component.scss',
|
||||||
imports: [DatePipe],
|
imports: [DatePipe],
|
||||||
})
|
})
|
||||||
export class LogsComponent implements OnInit {
|
export class LogsComponent extends NavComponent implements OnInit {
|
||||||
readonly #logger = inject(LoggerService);
|
readonly #logger = inject(LoggerService);
|
||||||
readonly #storage = inject(StorageService);
|
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
|
|
||||||
get logs(): LogEntry[] {
|
get logs(): LogEntry[] {
|
||||||
@@ -46,7 +45,7 @@ export class LogsComponent implements OnInit {
|
|||||||
|
|
||||||
async onClickLock() {
|
async onClickLock() {
|
||||||
this.#logger.logVaultLock();
|
this.#logger.logVaultLock();
|
||||||
await this.#storage.lockVault();
|
await this.storage.lockVault();
|
||||||
this.#router.navigateByUrl('/vault-login');
|
this.#router.navigateByUrl('/vault-login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,39 @@
|
|||||||
<div class="sam-text-header">
|
<div class="sam-text-header">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<span> Settings </span>
|
<span> Settings </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="vault-buttons">
|
||||||
|
<button class="btn btn-primary" (click)="onClickExportVault()">
|
||||||
|
Export Vault
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary" (click)="navigate('/vault-import')">
|
||||||
|
Import Vault
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<lib-nav-item text="💾 Backups" (click)="navigate('/home/backups')"></lib-nav-item>
|
||||||
|
<lib-nav-item text="🪵 Logs" (click)="navigate('/home/logs')"></lib-nav-item>
|
||||||
|
<lib-nav-item text="💡 Info" (click)="navigate('/home/info')"></lib-nav-item>
|
||||||
|
|
||||||
|
<div class="dev-mode-row">
|
||||||
|
<label class="toggle-label">
|
||||||
|
<input type="checkbox" [checked]="devMode" (change)="onToggleDevMode($event)" />
|
||||||
|
<span>Dev Mode</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sam-flex-grow"></div>
|
||||||
|
|
||||||
<div class="sync-info">
|
<div class="sync-info">
|
||||||
<span class="sync-label">SYNC: {{ syncFlow }}</span>
|
<span class="sync-label">SYNC: {{ syncFlow }}</span>
|
||||||
<p class="sync-note">
|
<p class="sync-note">
|
||||||
@@ -13,25 +42,11 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-primary" (click)="onClickExportVault()">
|
|
||||||
Export Vault
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="btn btn-primary" (click)="navigate('/vault-import')">
|
|
||||||
Import Vault
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<lib-nav-item text="💾 Backups" (click)="navigate('/home/backups')"></lib-nav-item>
|
|
||||||
<lib-nav-item text="🪵 Logs" (click)="navigate('/home/logs')"></lib-nav-item>
|
|
||||||
<lib-nav-item text="💡 Info" (click)="navigate('/home/info')"></lib-nav-item>
|
|
||||||
|
|
||||||
<div class="sam-flex-grow"></div>
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-danger"
|
class="btn btn-danger"
|
||||||
(click)="
|
(click)="
|
||||||
confirm.show(
|
confirm.show(
|
||||||
'Do you really want to reset your extension? All data will be lost.',
|
'Do you really want to reset your extension? Every data will be lost.',
|
||||||
onResetExtension.bind(this)
|
onResetExtension.bind(this)
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -16,6 +16,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vault-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--size);
|
||||||
|
|
||||||
|
button {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-mode-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--size);
|
||||||
|
|
||||||
|
.toggle-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--size-h);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.sync-info {
|
.sync-info {
|
||||||
.sync-label {
|
.sync-label {
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import {
|
|||||||
StorageService,
|
StorageService,
|
||||||
} from '@common';
|
} from '@common';
|
||||||
import { getNewStorageServiceConfig } from '../../../common/data/get-new-storage-service-config';
|
import { getNewStorageServiceConfig } from '../../../common/data/get-new-storage-service-config';
|
||||||
|
import { Buffer } from 'buffer';
|
||||||
|
import browser from 'webextension-polyfill';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-settings',
|
selector: 'app-settings',
|
||||||
@@ -21,6 +23,7 @@ import { getNewStorageServiceConfig } from '../../../common/data/get-new-storage
|
|||||||
export class SettingsComponent extends NavComponent implements OnInit {
|
export class SettingsComponent extends NavComponent implements OnInit {
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
syncFlow: string | undefined;
|
syncFlow: string | undefined;
|
||||||
|
override devMode = false;
|
||||||
|
|
||||||
readonly #storage = inject(StorageService);
|
readonly #storage = inject(StorageService);
|
||||||
readonly #startup = inject(StartupService);
|
readonly #startup = inject(StartupService);
|
||||||
@@ -44,6 +47,44 @@ export class SettingsComponent extends NavComponent implements OnInit {
|
|||||||
default:
|
default:
|
||||||
break;
|
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);
|
||||||
|
|
||||||
|
browser.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() {
|
async onResetExtension() {
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
<div class="sam-text-header">
|
<div class="sam-text-header">
|
||||||
<button class="lock-btn" title="Lock" (click)="onClickLock()">
|
<div class="header-buttons">
|
||||||
<span class="emoji">🔒</span>
|
<button class="header-btn" title="Lock" (click)="onClickLock()">
|
||||||
</button>
|
<span class="emoji">🔒</span>
|
||||||
|
</button>
|
||||||
|
@if (devMode) {
|
||||||
|
<button class="header-btn" title="Test Permission Prompt" (click)="onTestPrompt()">
|
||||||
|
<span class="emoji">✨</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
@if (showBackButton) {
|
@if (showBackButton) {
|
||||||
<button class="back-btn" title="Go Back" (click)="goBack()">
|
<button class="back-btn" title="Go Back" (click)="goBack()">
|
||||||
<span class="emoji">←</span>
|
<span class="emoji">←</span>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { FormsModule } from '@angular/forms';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import {
|
import {
|
||||||
LoggerService,
|
LoggerService,
|
||||||
StorageService,
|
NavComponent,
|
||||||
NwcService,
|
NwcService,
|
||||||
NwcConnection_DECRYPTED,
|
NwcConnection_DECRYPTED,
|
||||||
CashuService,
|
CashuService,
|
||||||
@@ -35,9 +35,8 @@ type WalletSection =
|
|||||||
styleUrl: './wallet.component.scss',
|
styleUrl: './wallet.component.scss',
|
||||||
imports: [CommonModule, FormsModule],
|
imports: [CommonModule, FormsModule],
|
||||||
})
|
})
|
||||||
export class WalletComponent implements OnInit, OnDestroy {
|
export class WalletComponent extends NavComponent implements OnInit, OnDestroy {
|
||||||
readonly #logger = inject(LoggerService);
|
readonly #logger = inject(LoggerService);
|
||||||
readonly #storage = inject(StorageService);
|
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
readonly nwcService = inject(NwcService);
|
readonly nwcService = inject(NwcService);
|
||||||
readonly cashuService = inject(CashuService);
|
readonly cashuService = inject(CashuService);
|
||||||
@@ -195,7 +194,7 @@ export class WalletComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// Load current sync flow setting
|
// Load current sync flow setting
|
||||||
this.currentSyncFlow = this.#storage.getSyncFlow();
|
this.currentSyncFlow = this.storage.getSyncFlow();
|
||||||
|
|
||||||
// Refresh balances on init if we have connections
|
// Refresh balances on init if we have connections
|
||||||
if (this.connections.length > 0) {
|
if (this.connections.length > 0) {
|
||||||
@@ -937,7 +936,7 @@ export class WalletComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
async onClickLock() {
|
async onClickLock() {
|
||||||
this.#logger.logVaultLock();
|
this.#logger.logVaultLock();
|
||||||
await this.#storage.lockVault();
|
await this.storage.lockVault();
|
||||||
this.#router.navigateByUrl('/vault-login');
|
this.#router.navigateByUrl('/vault-login');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export class WhitelistedAppsComponent extends NavComponent {
|
|||||||
@ViewChild('toast') toast!: ToastComponent;
|
@ViewChild('toast') toast!: ToastComponent;
|
||||||
@ViewChild('confirm') confirm!: ConfirmComponent;
|
@ViewChild('confirm') confirm!: ConfirmComponent;
|
||||||
|
|
||||||
readonly storage = inject(StorageService);
|
override readonly storage = inject(StorageService);
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
|
|
||||||
get whitelistedHosts(): string[] {
|
get whitelistedHosts(): string[] {
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user