Skip to content

Commit

Permalink
Merge pull request #7 from ocknamo/feature/adopt-lightning-address
Browse files Browse the repository at this point in the history
Feature/adopt lightning address
  • Loading branch information
ocknamo authored Dec 17, 2023
2 parents c42ac57 + 596ddb9 commit 058275e
Show file tree
Hide file tree
Showing 10 changed files with 4,116 additions and 590 deletions.
3,564 changes: 3,564 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"vitest": "^0.33.0"
},
"dependencies": {
"nostr-tools": "1.12.0"
"nostr-tools": "1.12.0",
"svelte-qrcode": "^1.0.0"
}
}
126 changes: 103 additions & 23 deletions src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
import { isValidUrl } from './lib/isValidUrl';
import { fetchNip5json } from './lib/fetchNip5json';
import RedirectingDialog from './lib/component/RedirectingDialog.svelte';
import LightningTipDialog from './lib/component/LightningTipDialog.svelte';
import { sleep } from './lib/sleep';
import { isValidLnAddress } from './lib/is-valid-lnaddress';
// nip07 types
interface Window {
Expand All @@ -35,6 +37,10 @@
let nip5Name: string | null = null;
let inputUrl: string | null = null;
// flags
let isMobile = false;
let isLnMode = false;
// interaction state
$: disabled = !nip5 || !nip5Name || !inputUrl;
let disabledInput = false;
Expand All @@ -59,6 +65,9 @@
];
onMount(async () => {
isMobile = document.body.clientWidth <= 1024;
isLnMode = location.pathname === '/ln';
const hash = window.location.hash;
console.info(hash);
if (!hash) {
Expand Down Expand Up @@ -89,7 +98,7 @@
// Validation
// fetch nip5 pub
const nip5Json = await fetchNip5json(nip5);
const nip5Json = await fetchNip5json(nip5, nip5Name);
if (!nip5Json || !nip5Json['names']) {
alert(`NIP-5: ${nip5}\nNIP5 not found. \nNIP-5がありません`);
reset();
Expand Down Expand Up @@ -123,18 +132,23 @@
console.info(`redirect URL: ${redirectUrl}`);
if (isLnMode) {
// Noop
return;
}
// Wait cancel action.
await sleep(3000);
if (redirectCancel) {
reset();
sub.close();
sub.unsub();
pool.close(relays);
return;
}
window.location.href = redirectUrl;
sub.close();
sub.unsub();
pool.close(relays);
});
});
Expand All @@ -156,6 +170,11 @@
if (!nip5 || !nip5Name || !inputUrl) {
return;
}
if (isLnMode && !isValidLnAddress(inputUrl)) {
alert(`Invalid lightning addredd. \n不正なライトニングアドレスです`);
return;
}
// fetch nip5 pub
const nip5Json = await fetchNip5json(nip5, nip5Name);
if (!nip5Json || !nip5Json['names'] || !nip5Json['names'][nip5Name]) {
Expand Down Expand Up @@ -206,15 +225,17 @@
[tagKey, contentId],
[dTag, appName],
],
content: inputUrl!,
content: isLnMode ? `lnurlp://${inputUrl}` : inputUrl,
pubkey: pk ?? '',
};
const event = await (window as Window).nostr?.signEvent(unsignedEvent);
pool.publish(relays, event!);
// TODO: Convert safety NIP-05 name and nip5 domain. eg. `/`, `@` value are not safety.
result = `${baseUrl}#${nip5Name}@${nip5}/${tagKey}/${contentId}`;
result = `${baseUrl}${
isLnMode ? '/ln' : ''
}#${nip5Name}@${nip5}/${tagKey}/${contentId}`;
// cooling time
setTimeout(() => {
Expand All @@ -223,7 +244,7 @@
}
function clickTitle() {
window.location.href = baseUrl;
window.location.href = isLnMode ? baseUrl : `${baseUrl}/ln`;
}
async function copyUrl() {
Expand All @@ -249,11 +270,25 @@
/>
<main>
<div class="head-space" />
<Fab extended class="fab-title" on:click={clickTitle}>
<Icon class="material-icons" style="margin-right: 4px;">link</Icon>
<Fab
extended
style={isLnMode ? 'background-color:orange' : ''}
class="fab-title"
on:click={clickTitle}
>
<Icon class="material-icons" style="margin-right: 4px;"
>{isLnMode ? 'bolt' : 'link'}</Icon
>
<Label>{appName}</Label>
</Fab>
<p class="app-description">Shorted URL generator by NIP-05</p>
<div>
<Icon class="material-icons" style="color:gray;"
>{isLnMode ? 'toggle_on' : 'toggle_off'}</Icon
>
</div>
<p class="app-description">
{isLnMode ? '[BETA] Generate Lightning Tip link' : 'Shorted URL generator'}
</p>
<div class="top-space" />
<div class="input-flex-container">
<div class="card-container">
Expand Down Expand Up @@ -310,12 +345,14 @@
type="text"
variant="outlined"
bind:value={inputUrl}
label="Input your long url"
label={isLnMode
? '⚡Input your Lightning address!'
: 'Input your long url'}
class="text-input"
disabled={disabledInput}
>
<HelperText persistent slot="helper"
>example: "https://xxxxxxx"</HelperText
>example: "jhondoe@ocknamo.com"</HelperText
>
</Textfield>
</div>
Expand All @@ -324,40 +361,58 @@
<Button style="margin:8px;" variant="raised" on:click={onclick} {disabled}
>Submit</Button
>
<p>
<p class="nip-05">
NIP-5:{nip5 ?? ' ???'}
</p>
<div class="footer">
<a
href="https://github.com/ocknamo/shotr/issues"
target="_blank"
rel="noopener noreferrer"><p>Report</p></a
>
{#if isMobile}
<a
href="lnurlp://s14pes@getalby.com"
target="_blank"
rel="noopener noreferrer"
>
<p>Tip</p>
</a>
{/if}
{#if !isMobile}
<a
href="https://github.com/ocknamo/shotr/issues"
target="_blank"
rel="noopener noreferrer"><p>Report</p></a
>
{/if}
<a
href="https://github.com/ocknamo/shotr"
target="_blank"
rel="noopener noreferrer"
>
<img width="50em" src={githubLogo} alt="github" />
<img class="github-link" width="50em" src={githubLogo} alt="github" />
</a>
</div>

{#if redirect}
{#if redirect && !isLnMode}
<RedirectingDialog
bind:canceled={redirectCancel}
bind:nip5
bind:nip5Name
bind:redirectUrl={inputUrl}
></RedirectingDialog>
{:else if redirect && isLnMode}
<LightningTipDialog
bind:canceled={redirectCancel}
bind:nip5
bind:nip5Name
bind:lightningAddressUrl={inputUrl}
></LightningTipDialog>
{/if}
</main>

<style>
.head-space {
margin-top: 6em;
margin-top: 4em;
}
.top-space {
margin-top: 4em;
margin-top: 3em;
}
@media screen and (max-height: 740px) {
.top-space {
Expand All @@ -368,14 +423,20 @@
}
}
.card-container {
margin: 4em;
margin: 3em;
width: 100%;
min-width: 200px;
max-width: 300px;
color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
}
@media screen and (max-height: 740px) {
.card-container {
margin: 3em;
}
}
.input-flex-container {
display: flex;
justify-content: center;
Expand All @@ -392,7 +453,10 @@
.text-input-container {
margin: 0.5em;
max-width: fit-content;
min-width: 200px;
min-width: 300px;
}
.nip-05 {
color: black;
}
.footer {
position: absolute;
Expand All @@ -402,8 +466,24 @@
justify-content: end;
vertical-align: bottom;
width: 100%;
align-items: center;
}
@media screen and (max-height: 700px) {
.footer {
bottom: 0.2em;
right: 0.5em;
margin-bottom: -10px;
}
}
.footer a {
margin-left: 1em;
}
.github-link {
width: 40px;
}
@media screen and (max-height: 700px) {
.github-link {
width: 20px;
}
}
</style>
3 changes: 2 additions & 1 deletion src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ body {
margin: 0;
display: flex;
place-items: center;
overflow-y: hidden;
}

input {
Expand All @@ -37,7 +38,6 @@ input {
#app {
width: 100%;
margin: 0 auto;
padding: 2rem 0;
text-align: center;
}

Expand All @@ -52,6 +52,7 @@ input {

.text-input {
background-color: azure;
width: 100%;
}

.fab-title {
Expand Down
58 changes: 58 additions & 0 deletions src/lib/component/LightningTipDialog.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<script lang="ts">
import Dialog, { Content, Actions } from '@smui/dialog';
import Button, { Label } from '@smui/button';
import QrCode from 'svelte-qrcode';
export let canceled = false;
export let nip5Name: string | null = null;
export let nip5: string | null = null;
export let lightningAddressUrl: string | null = null;
$: lightningAddress = lightningAddressUrl
? lightningAddressUrl.replace('lnurlp://', '')
: '';
</script>

<Dialog
open
scrimClickAction=""
escapeKeyAction=""
aria-labelledby="redirect notify"
aria-describedby="Which URLs to redirect to. Information about who created the URL."
>
<Content id="redirect-to">
<!-- XXXX: Depending on the environment, 'lnurlp://' could not be used. Fix me if you can. e.g. Pixel 6 -->
<QrCode value={'lightning://' + lightningAddress}></QrCode>

<p class="redirect-url">
<span style="font-weight: 600">Tip to: </span>{lightningAddress ?? '???'}
</p>
<span style="font-weight: 600">Who made this link? :</span>
<br />
<a
href={`https://nostter.app/${nip5Name}@${nip5}`}
target="_blank"
rel="noopener noreferrer"
>
{nip5Name ?? ''}@{nip5 ?? '???'}
</a>
</Content>
<div style="display: flex; justify-content: center"></div>
<Actions>
<a href={lightningAddressUrl} target="_blank" rel="noopener noreferrer">
<Button style="background-color:orange;margin-right:1em;min-width:80px">
<Label>⚡Tip!</Label>
</Button>
</a>
<Button on:click={() => (canceled = true)}>
<Label>Cancel</Label>
</Button>
</Actions>
</Dialog>

<style>
.redirect-url {
overflow-wrap: break-word;
}
</style>
2 changes: 1 addition & 1 deletion src/lib/fetch-application-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function fetchApplicationEvent(
if (!!event) {
resolve(event);
}
sub.close();
sub.unsub();
});

setTimeout(() => {
Expand Down
14 changes: 14 additions & 0 deletions src/lib/is-valid-lnaddress.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { describe, expect, it } from 'vitest';
import { isValidLnAddress } from './is-valid-lnaddress';

describe('isValidLnAddress', () => {
it('shoud return true with valid lightning address', () => {
expect(isValidLnAddress('jhondoe@ocknamo.com')).toBe(true);
expect(isValidLnAddress('s14pes@getalby.com')).toBe(true);
});

it('shoud return true with invalid address', () => {
expect(isValidLnAddress('@jhondoe@ocknamo.com')).toBe(false);
expect(isValidLnAddress('s14pes@getalby#.com')).toBe(false);
});
});
Loading

0 comments on commit 058275e

Please sign in to comment.