mirror of
https://github.com/LBRYFoundation/Watch-on-LBRY.git
synced 2025-08-23 17:47:26 +00:00
🍣 more bugfixes
This commit is contained in:
parent
6aa507c53d
commit
cddd415e22
9 changed files with 176 additions and 41 deletions
|
@ -59,13 +59,17 @@ h6 {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
white-space: break-spaces;
|
||||
}
|
||||
|
||||
.options {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-template-columns: repeat(auto-fit, minmax(3em, 1fr));
|
||||
justify-content: center;
|
||||
gap: .25em;
|
||||
padding: 0 1.5em;
|
||||
}
|
||||
|
||||
.button {
|
||||
|
@ -82,6 +86,7 @@ h6 {
|
|||
border: unset;
|
||||
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button.active {
|
||||
|
|
|
@ -79,6 +79,7 @@ export function Dialogs(params: { manager: ReturnType<typeof createDialogManager
|
|||
margin-bottom: 0;
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
padding: 1.5em;
|
||||
}
|
||||
|
||||
.alert-dialog::before {
|
||||
|
@ -94,7 +95,7 @@ export function Dialogs(params: { manager: ReturnType<typeof createDialogManager
|
|||
|
||||
.alert-dialog form {
|
||||
display: grid;
|
||||
gap: 1em
|
||||
gap: 2em
|
||||
}
|
||||
|
||||
.alert-dialog form .fields {
|
||||
|
@ -102,6 +103,10 @@ export function Dialogs(params: { manager: ReturnType<typeof createDialogManager
|
|||
gap: .5em
|
||||
}
|
||||
|
||||
.alert-dialog form .fields pre {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.alert-dialog form .actions {
|
||||
display: flex;
|
||||
gap: .5em;
|
||||
|
@ -119,10 +124,13 @@ export function Dialogs(params: { manager: ReturnType<typeof createDialogManager
|
|||
currentAlert.resolve(cancelled ? null : currentAlert.type === 'confirm' ? true : value)
|
||||
}}>
|
||||
<div class="fields">
|
||||
<label>{currentAlert.message}</label>
|
||||
<pre>{currentAlert.message}</pre>
|
||||
{currentAlert.type === 'prompt' && <input type='text' onInput={(event) => setValue(event.currentTarget.value)} />}
|
||||
</div>
|
||||
<div class="actions">
|
||||
{/* This is here to capture, return key */}
|
||||
<button style="position:0;opacity:0;pointer-events:none"></button>
|
||||
|
||||
{currentAlert.type !== 'alert' && <button className='button' onClick={() => cancelled = true}>Cancel</button>}
|
||||
<button className='button active'>
|
||||
{
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
"web_accessible_resources": [
|
||||
"pages/popup/index.html",
|
||||
"pages/YTtoLBRY/index.html",
|
||||
"pages/import/index.html",
|
||||
"assets/icons/lbry/lbry-logo.svg",
|
||||
"assets/icons/lbry/odysee-logo.svg",
|
||||
"assets/icons/lbry/madiator-logo.svg"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import path from 'path'
|
||||
import { DialogManager } from '../../components/dialogs'
|
||||
import { getExtensionSettingsAsync, setExtensionSetting, ytUrlResolversSettings } from "../../settings"
|
||||
import { getFileContent } from '../file'
|
||||
|
||||
async function generateKeys() {
|
||||
const keys = await window.crypto.subtle.generateKey(
|
||||
|
@ -156,22 +157,13 @@ function download(data: string, filename: string, type: string) {
|
|||
})
|
||||
}
|
||||
|
||||
async function readFile() {
|
||||
return await new Promise<string | null>((resolve) => {
|
||||
const input = document.createElement("input")
|
||||
input.type = 'file'
|
||||
input.accept = '.wol-keys.json'
|
||||
|
||||
input.click()
|
||||
input.addEventListener("change", () => {
|
||||
if (!input.files?.[0]) return
|
||||
const myFile = input.files[0]
|
||||
const reader = new FileReader()
|
||||
|
||||
reader.addEventListener('load', () => resolve(reader.result?.toString() ?? null))
|
||||
reader.readAsText(myFile)
|
||||
})
|
||||
})
|
||||
// Using callback here because there is no good solution for detecting cancel event
|
||||
export function inputKeyFile(callback: (file: File | null) => void) {
|
||||
const input = document.createElement("input")
|
||||
input.type = 'file'
|
||||
input.accept = '.wol-keys.json'
|
||||
input.click()
|
||||
input.addEventListener("change", () => callback(input.files?.[0] ?? null))
|
||||
}
|
||||
|
||||
interface ExportedProfileKeysFile {
|
||||
|
@ -190,14 +182,23 @@ export async function exportProfileKeysAsFile() {
|
|||
download(json, `watch-on-lbry-profile-export-${friendlyPublicKey(publicKey)}.wol-keys.json`, 'application/json')
|
||||
}
|
||||
|
||||
export async function importProfileKeysFromFile(dialogManager: DialogManager) {
|
||||
export async function importProfileKeysFromFile(dialogManager: DialogManager, file: File) {
|
||||
try {
|
||||
const json = await readFile()
|
||||
if (!json) throw new Error("Invalid")
|
||||
let settings = await getExtensionSettingsAsync()
|
||||
if (settings.publicKey && !await dialogManager.confirm(
|
||||
"This will overwrite your old keypair." +
|
||||
"\nStill wanna continue?\n\n" +
|
||||
"NOTE: Without keypair you can't purge your data online.\n" +
|
||||
"So if you wish to purge, please use purging instead."
|
||||
)) return false
|
||||
const json = await getFileContent(file)
|
||||
if (!json) return false
|
||||
const { publicKey, privateKey } = JSON.parse(json) as ExportedProfileKeysFile
|
||||
setExtensionSetting('publicKey', publicKey)
|
||||
setExtensionSetting('privateKey', privateKey)
|
||||
return true
|
||||
} catch (error: any) {
|
||||
await dialogManager.alert(error.message)
|
||||
return false
|
||||
}
|
||||
}
|
14
src/pages/import/index.html
Normal file
14
src/pages/import/index.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../assets/styles/common.css" />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<script src="main.tsx" defer></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root" />
|
||||
</body>
|
||||
|
||||
</html>
|
47
src/pages/import/main.tsx
Normal file
47
src/pages/import/main.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { h, render } from 'preact'
|
||||
import { useState } from 'preact/hooks'
|
||||
import { createDialogManager, Dialogs } from '../../components/dialogs'
|
||||
import { importProfileKeysFromFile, inputKeyFile } from '../../modules/crypto'
|
||||
|
||||
function ImportPage() {
|
||||
const [loading, updateLoading] = useState(() => false)
|
||||
|
||||
async function loads<T>(operation: Promise<T>) {
|
||||
try {
|
||||
updateLoading(true)
|
||||
await operation
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
finally {
|
||||
updateLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
function importProfile() {
|
||||
inputKeyFile(async (file) => file && await loads(
|
||||
importProfileKeysFromFile(dialogManager, file)
|
||||
.then((success) => success && (location.pathname = '/pages/popup/index.html'))
|
||||
))
|
||||
}
|
||||
|
||||
const dialogManager = createDialogManager()
|
||||
|
||||
return <div id='popup'>
|
||||
<Dialogs manager={dialogManager} />
|
||||
<main>
|
||||
<section>
|
||||
<label>Import your profile</label>
|
||||
<p>Import your unique keypair.</p>
|
||||
<div className='options'>
|
||||
<a onClick={() => importProfile()} className={`button`}>Import</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
{loading && <div class="overlay">
|
||||
<span>Loading...</span>
|
||||
</div>}
|
||||
</div>
|
||||
}
|
||||
|
||||
render(<ImportPage />, document.getElementById('root')!)
|
35
src/pages/import/style.css
Normal file
35
src/pages/import/style.css
Normal file
|
@ -0,0 +1,35 @@
|
|||
header {
|
||||
display: grid;
|
||||
gap: .5em;
|
||||
padding: .75em;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: rgba(19, 19, 19, 0.5);
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
main {
|
||||
display: grid;
|
||||
gap: 2em;
|
||||
padding: 1.5em 0.5em;
|
||||
}
|
||||
|
||||
section {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
text-align: center;
|
||||
gap: .75em;
|
||||
}
|
||||
|
||||
section label {
|
||||
font-size: 1.75em;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#popup {
|
||||
width: 35em;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
margin: auto;
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import { h, render } from 'preact'
|
||||
import { useState } from 'preact/hooks'
|
||||
import { createDialogManager, Dialogs } from '../../components/dialogs'
|
||||
import { exportProfileKeysAsFile, friendlyPublicKey, generateProfileAndSetNickname, getProfile, importProfileKeysFromFile, purgeProfile, resetProfileSettings } from '../../modules/crypto'
|
||||
import { exportProfileKeysAsFile, friendlyPublicKey, generateProfileAndSetNickname, getProfile, purgeProfile, resetProfileSettings } from '../../modules/crypto'
|
||||
import { LbryPathnameCache } from '../../modules/yt/urlCache'
|
||||
import { getTargetPlatfromSettingsEntiries, getYtUrlResolversSettingsEntiries, setExtensionSetting, useExtensionSettings } from '../../settings'
|
||||
|
||||
|
@ -13,11 +13,13 @@ const ytUrlResolverOptions = getYtUrlResolversSettingsEntiries()
|
|||
function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfile>> | null }) {
|
||||
const { redirect, targetPlatform, urlResolver, privateKey, publicKey } = useExtensionSettings()
|
||||
let [loading, updateLoading] = useState(() => false)
|
||||
let [popupRoute, updateRoute] = useState<string | null>(() => null)
|
||||
let [route, updateRoute] = useState<string | null>(() => null)
|
||||
|
||||
const dialogManager = createDialogManager()
|
||||
const nickname = params.profile ? params.profile.nickname ?? 'No Nickname' : '...'
|
||||
|
||||
async function startAsyncOperation<T>(operation: Promise<T>) {
|
||||
|
||||
async function loads<T>(operation: Promise<T>) {
|
||||
try {
|
||||
updateLoading(true)
|
||||
await operation
|
||||
|
@ -29,7 +31,19 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
|||
}
|
||||
}
|
||||
|
||||
const dialogManager = createDialogManager()
|
||||
async function importButtonClick() {
|
||||
const importPopupWindow = open(
|
||||
'/pages/import/index.html',
|
||||
'Import Profile',
|
||||
[
|
||||
`height=${Math.max(document.body.clientHeight, screen.height * .5)}`,
|
||||
`width=${document.body.clientWidth}`,
|
||||
`toolbar=0,menubar=0,location=0`,
|
||||
`top=${screenY}`,
|
||||
`left=${screenX}`
|
||||
].join(','))
|
||||
importPopupWindow?.focus()
|
||||
}
|
||||
|
||||
return <div id='popup'>
|
||||
<Dialogs manager={dialogManager} />
|
||||
|
@ -44,7 +58,7 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
|||
</section>
|
||||
<section>
|
||||
{
|
||||
popupRoute === 'profile'
|
||||
route === 'profile'
|
||||
? <a onClick={() => updateRoute('')} className="filled">⇐ Back</a>
|
||||
: <a className='filled' onClick={() => updateRoute('profile')} href="#profile">Profile Settings</a>
|
||||
}
|
||||
|
@ -52,19 +66,19 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
|||
</header>
|
||||
: <header>
|
||||
{
|
||||
popupRoute === 'profile'
|
||||
route === 'profile'
|
||||
? <a onClick={() => updateRoute('')} className="filled">⇐ Back</a>
|
||||
: <a className='filled' onClick={() => updateRoute('profile')} href="#profile">Profile Settings</a>
|
||||
}
|
||||
</header>
|
||||
}
|
||||
{
|
||||
popupRoute === 'profile' ?
|
||||
route === 'profile' ?
|
||||
publicKey ?
|
||||
<main>
|
||||
<section>
|
||||
<div className='options'>
|
||||
<a onClick={() => startAsyncOperation(generateProfileAndSetNickname(dialogManager)).then(() => renderPopup())} className={`button active`}>
|
||||
<a onClick={() => loads(generateProfileAndSetNickname(dialogManager)).then(() => renderPopup())} className={`button active`}>
|
||||
Change Nickname
|
||||
</a>
|
||||
<a onClick={async () =>
|
||||
|
@ -85,10 +99,7 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
|||
<a onClick={() => exportProfileKeysAsFile()} className={`button active`}>
|
||||
Export
|
||||
</a>
|
||||
<a onClick={async () =>
|
||||
await dialogManager.confirm("This will overwrite your old keypair.\nStill wanna continue?\n\nNOTE: Without keypair you can't purge your data online.\nSo if you wish to purge, please use purging instead.")
|
||||
&& startAsyncOperation(importProfileKeysFromFile(dialogManager)).then(() => renderPopup())
|
||||
}
|
||||
<a onClick={() => importButtonClick()}
|
||||
className={`button`}
|
||||
>
|
||||
Import
|
||||
|
@ -99,8 +110,10 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
|||
<label>Purge your profile and data!</label>
|
||||
<p>Purge your profile data online and offline.</p>
|
||||
<div className='options'>
|
||||
<span className="filled button">(╯°□°)╯︵ ┻━┻</span>
|
||||
<a onClick={() => startAsyncOperation(purgeProfile(dialogManager)).then(() => renderPopup())} className={`button`}>
|
||||
<div className="purge-aaaaaaa">
|
||||
<span className='filled'>(╯°□°)╯︵ ┻━┻</span>
|
||||
</div>
|
||||
<a onClick={() => loads(purgeProfile(dialogManager)).then(() => renderPopup())} className={`button`}>
|
||||
Purge Everything!!
|
||||
</a>
|
||||
</div>
|
||||
|
@ -110,7 +123,7 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
|||
<p>Generate a new keypair.</p>
|
||||
<div className='options'>
|
||||
<a onClick={async () => await dialogManager.confirm("This will overwrite your old keypair.\nStill wanna continue?\n\nNOTE: Without keypair you can't purge your data online.\nSo if you wish to purge, please use purging instead.")
|
||||
&& startAsyncOperation(generateProfileAndSetNickname(dialogManager, true)).then(() => renderPopup())
|
||||
&& loads(generateProfileAndSetNickname(dialogManager, true)).then(() => renderPopup())
|
||||
}
|
||||
className={`button`}
|
||||
>
|
||||
|
@ -125,10 +138,10 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
|||
<label>You don't have a profile.</label>
|
||||
<p>You can either import keypair for an existing profile or generate a new profile keypair.</p>
|
||||
<div className='options'>
|
||||
<a onClick={() => startAsyncOperation(importProfileKeysFromFile(dialogManager)).then(() => renderPopup())} className={`button`}>
|
||||
<a onClick={() => importButtonClick()} className={`button`}>
|
||||
Import
|
||||
</a>
|
||||
<a onClick={() => startAsyncOperation(generateProfileAndSetNickname(dialogManager)).then(() => renderPopup())} className={`button active`}>
|
||||
<a onClick={() => loads(generateProfileAndSetNickname(dialogManager)).then(() => renderPopup())} className={`button active`}>
|
||||
Generate
|
||||
</a>
|
||||
</div>
|
||||
|
@ -166,7 +179,7 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
|||
</a>
|
||||
)}
|
||||
</div>
|
||||
<a onClick={() => startAsyncOperation(LbryPathnameCache.clearAll().then(() => dialogManager.alert("Cleared Cache!")))} className={`button active`}>
|
||||
<a onClick={() => loads(LbryPathnameCache.clearAll().then(() => dialogManager.alert("Cleared Cache!")))} className={`button active`}>
|
||||
Clear Resolver Cache
|
||||
</a>
|
||||
</section>
|
||||
|
|
|
@ -17,17 +17,28 @@ main {
|
|||
section {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
text-align: center;
|
||||
gap: .75em;
|
||||
}
|
||||
|
||||
section label {
|
||||
section>label {
|
||||
font-size: 1.75em;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
section>.options {
|
||||
padding: 0 1.5em;
|
||||
}
|
||||
|
||||
#popup {
|
||||
width: 35em;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.purge-aaaaaaa {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
}
|
Loading…
Add table
Reference in a new issue