Skip to content

Commit

Permalink
import and delete profiles
Browse files Browse the repository at this point in the history
  • Loading branch information
diegogurpegui committed Dec 27, 2023
1 parent c47da85 commit da125eb
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 18 deletions.
1 change: 1 addition & 0 deletions src/assets/icons/add-circle-outline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/icons/arrow-up-circle-outline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/icons/download-outline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion src/components/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import React, { useRef, useEffect, useState } from 'react';

interface ModalProps {
show: boolean;
className?: string;
onClose: () => {};
}

export function Modal({
show,
onClose,
className,
children
}: React.PropsWithChildren<ModalProps>) {
const modalRef = useRef<HTMLDivElement>(null);
Expand All @@ -34,7 +36,7 @@ export function Modal({
// }, [show]);

return show ? (
<div className={`modal-wrapper`} ref={modalRef}>
<div className={`modal-wrapper ${className}`} ref={modalRef}>
<div className={`modal`}>{children}</div>
<div className="overlay" onClick={handleOverlayClick}></div>
</div>
Expand Down
133 changes: 118 additions & 15 deletions src/options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ import {
import * as Storage from './storage';
import { getPermissionsString, isHexadecimal, isValidRelayURL } from './common';
import logotype from './assets/logo/logotype.png';
import WarningIcon from './assets/icons/warning-outline.svg';
import AddCircleIcon from './assets/icons/add-circle-outline.svg';
import ArrowUpCircleIcon from './assets/icons/arrow-up-circle-outline.svg';
import CopyIcon from './assets/icons/copy-outline.svg';
import DiceIcon from './assets/icons/dice-outline.svg';
import DownloadIcon from './assets/icons/download-outline.svg';
import RadioIcon from './assets/icons/radio-outline.svg';
import TrashIcon from './assets/icons/trash-outline.svg';
import WarningIcon from './assets/icons/warning-outline.svg';

type RelayConfig = {
url: string;
Expand All @@ -30,8 +33,10 @@ function Options() {
let [selectedProfilePubKey, setSelectedProfilePubKey] = useState<string>('');
let [profiles, setProfiles] = useState<ProfilesConfig>({});
let [isLoadingProfile, setLoadingProfile] = useState(false);
let [profileJson, setProfileJson] = useState('');
let [isModalShown, setModalShown] = useState(false);
let [profileExportJson, setProfileExportJson] = useState('');
let [profileImportJson, setProfileImportJson] = useState('');
let [isExportModalShown, setExportModalShown] = useState(false);
let [isImportModalShown, setImportModalShown] = useState(false);

let [privateKey, setPrivateKey] = useState('');
let [isKeyHidden, setKeyHidden] = useState(true);
Expand Down Expand Up @@ -94,7 +99,11 @@ function Options() {
loadAndSelectProfile(selectedProfilePubKey);
}, [selectedProfilePubKey]);

const showMessage = useCallback((msg, type = 'info', timeout = 3000) => {
const showMessage: (
msg: string,
type?: 'info' | 'success' | 'warning',
timeout?: number
) => {} = useCallback((msg, type = 'info', timeout = 3000) => {
setMessageType(type);
setMessage(msg);
if (timeout > 0) {
Expand Down Expand Up @@ -160,20 +169,82 @@ function Options() {
function handleExportProfileClick() {
const profile = getSelectedProfile();
const profileJson = JSON.stringify(profile);
setProfileJson(profileJson);
setModalShown(true);
setProfileExportJson(profileJson);
setExportModalShown(true);
}

function handleExportProfileCopyClick() {
navigator.clipboard.writeText(profileJson);
navigator.clipboard.writeText(profileExportJson);
}

function handleExportModalClose() {
setExportModalShown(false);
}

function handleImportProfileClick() {
setImportModalShown(true);
}

function handleChangeProfileImportJson(e) {
setProfileImportJson(e.target.value);
}

async function handleImportProfileImportClick() {
let newProfile: ProfileConfig;
// validations
try {
newProfile = JSON.parse(profileImportJson);
} catch (error) {
console.warn(`Error parsing the entered JSON`, error);
showMessage(
`There was an error parsing the JSON. ${error.message}`,
'warning'
);
return;
}
if (!newProfile) {
console.warn(`The imported profile is empty.`);
showMessage(`The imported profile is invalid.`, 'warning');
}

// store the new profile
await Storage.addProfile(newProfile);

const newPubKey = getPublicKey(newProfile.privateKey);
setProfiles({ ...profiles, ...{ [newPubKey]: newProfile } });

// now load in the component
if (newProfile.privateKey) {
setPrivateKey(nip19.nsecEncode(newProfile.privateKey));
} else {
setPrivateKey('');
}
setSelectedProfilePubKey(newPubKey);

setImportModalShown(false);
}

function handleModalClose() {
setModalShown(false);
function handleImportModalClose() {
setImportModalShown(false);
}

function saveProfiles() {
Storage.updateProfiles(profiles);
async function handleDeleteProfileClick(e) {
e.preventDefault();
if (
window.confirm(
`Delete the profile "${nip19.npubEncode(selectedProfilePubKey)}"?`
)
) {
const updateProfiles = profiles;
delete updateProfiles[selectedProfilePubKey];
console.debug('updated profiles', updateProfiles);
setProfiles(updateProfiles);
await saveProfiles();
}
}

async function saveProfiles() {
await Storage.updateProfiles(profiles);
}

//#endregion Profiles
Expand Down Expand Up @@ -205,7 +276,7 @@ function Options() {
delete profiles[selectedProfilePubKey];
setSelectedProfilePubKey(newPubKey); // this re-loads the profile in the screen

saveProfiles();
await saveProfiles();
} else {
console.warn('Saving and empty private key');
}
Expand Down Expand Up @@ -403,9 +474,24 @@ function Options() {
disabled={isNewProfilePending()}
onClick={handleNewProfileClick}
>
<AddCircleIcon />
New profile
</button>
<button onClick={handleExportProfileClick}>Export profile</button>
<button onClick={handleExportProfileClick}>
<DownloadIcon />
Export profile
</button>
<button onClick={handleImportProfileClick}>
<ArrowUpCircleIcon />
Import profile
</button>
<button
onClick={handleDeleteProfileClick}
className="button button-danger"
>
<TrashIcon />
Delete profile
</button>
</div>
</section>

Expand Down Expand Up @@ -551,16 +637,33 @@ function Options() {
</main>
<footer>version {version}</footer>

<Modal show={isModalShown} onClose={handleModalClose}>
<Modal
show={isExportModalShown}
className="export-modal"
onClose={handleExportModalClose}
>
<p>
This is the JSON that represents your profile (WARNING: it contains
your private key):
</p>
<code>{profileJson}</code>
<code>{profileExportJson}</code>
<button onClick={handleExportProfileCopyClick}>
<CopyIcon /> Copy
</button>
</Modal>

<Modal
show={isImportModalShown}
className="import-modal"
onClose={handleImportModalClose}
>
<p>Paste the profile JSON in the following box:</p>
<textarea
value={profileImportJson}
onChange={handleChangeProfileImportJson}
></textarea>
<button onClick={handleImportProfileImportClick}>Import</button>
</Modal>
</>
);
}
Expand Down
20 changes: 18 additions & 2 deletions src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export async function readActiveRelays(): Promise<RelaysConfig> {
export async function updateRelays(profilePublicKey: string, newRelays) {
if (newRelays) {
const profile = await getProfile(profilePublicKey);
if (!profile) {
console.warn(`There is no profile with the key '${profilePublicKey}'`);
return;
}
profile.relays = newRelays;
return updateProfile(profile);
}
Expand Down Expand Up @@ -130,7 +134,19 @@ export async function getProfile(publicKey: string): Promise<ProfileConfig> {
export async function updateProfiles(
profiles: ProfilesConfig
): Promise<ProfilesConfig> {
browser.storage.local.set({
await browser.storage.local.set({
[ConfigurationKeys.PROFILES]: profiles
});

return profiles;
}
export async function addProfile(
profile: ProfileConfig
): Promise<ProfilesConfig> {
const profiles = await readProfiles();
profiles[getPublicKey(profile.privateKey)] = profile;

await browser.storage.local.set({
[ConfigurationKeys.PROFILES]: profiles
});

Expand All @@ -142,7 +158,7 @@ export async function updateProfile(
const profiles = await readProfiles();
profiles[getPublicKey(profile.privateKey)] = profile;

browser.storage.local.set({
await browser.storage.local.set({
[ConfigurationKeys.PROFILES]: profiles
});

Expand Down
10 changes: 10 additions & 0 deletions src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -467,11 +467,21 @@ input,

.modal {
max-width: 95%;
}
.export-modal {
code {
display: block;
overflow: scroll;
}
}
.import-modal {
textarea {
display: block;
width: 100%;
overflow: scroll;
font-family: 'Courier New', Courier, monospace;
}
}
}

/* ---- Prompt ---- */
Expand Down

0 comments on commit da125eb

Please sign in to comment.