Sign events from NIP-07 ext

This commit is contained in:
Kieran 2022-12-29 15:21:03 +00:00
parent 987ef0ed7b
commit 523d1951fa
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
8 changed files with 131 additions and 38 deletions

View File

@ -3,6 +3,9 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@noble/secp256k1": "^1.7.0",
"@reduxjs/toolkit": "^1.9.1",
"bech32": "^2.0.0",

View File

@ -39,12 +39,17 @@ code {
user-select: none;
background-color: #000;
border: 1px solid;
display: inline-block;
}
.btn:hover {
background-color: #333;
}
.btn-sm {
padding: 5px;
}
input[type="text"], input[type="password"] {
padding: 10px;
border-radius: 5px;
@ -93,6 +98,7 @@ div.form-group {
div.form-group > div {
padding: 3px 5px;
word-break: break-word;
}
div.form-group > div:first-child {

View File

@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { setPrivateKey, setPublicKey } from "../state/Login";
import { setPrivateKey, setNip07PubKey } from "../state/Login";
import * as secp from '@noble/secp256k1';
import { bech32 } from "bech32";
import { useNavigate } from "react-router-dom";
@ -32,12 +32,11 @@ export default function LoginPage() {
async function doNip07Login() {
let pubKey = await window.nostr.getPublicKey();
dispatch(setPublicKey(pubKey));
dispatch(setNip07PubKey(pubKey));
}
function altLogins() {
let nip07 = 'nostr' in window;
if (!nip07) {
return null;
}

View File

@ -8,8 +8,8 @@
}
.profile .avatar {
width: 256px;
height: 256px;
width: 128px;
height: 128px;
background-size: contain;
cursor: pointer;
}
@ -26,4 +26,15 @@
.profile .avatar .edit:hover {
opacity: 0.5;
}
}
@media(max-width: 720px) {
.profile {
flex-direction: column;
align-items: center;
}
.profile > div:last-child {
margin: 0;
width: 100%;
}
}

View File

@ -8,6 +8,9 @@ import Nostrich from "../nostrich.jpg";
import useEventPublisher from "./feed/EventPublisher";
import useTimelineFeed from "./feed/TimelineFeed";
import Note from "../element/Note";
import { bech32 } from "bech32";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faQrcode } from "@fortawesome/free-solid-svg-icons";
export default function ProfilePage() {
const dispatch = useDispatch();
@ -37,6 +40,23 @@ export default function ProfilePage() {
}
}, [user]);
useEffect(() => {
// some clients incorrectly set this to LNURL service, patch this
if (lud16.toLowerCase().startsWith("lnurl")) {
let decoded = bech32.decode(lud16, 1000);
let url = new TextDecoder().decode(Uint8Array.from(bech32.fromWords(decoded.words)));
if (url.startsWith("http")) {
let parsedUri = new URL(url);
// is lightning address
if (parsedUri.pathname.startsWith("/.well-known/lnurlp/")) {
let pathParts = parsedUri.pathname.split('/');
let username = pathParts[pathParts.length - 1];
setLud16(`${username}@${parsedUri.hostname}`);
}
}
}
}, [lud16]);
async function saveProfile() {
let ev = await publisher.metadata({
name,
@ -79,7 +99,7 @@ export default function ProfilePage() {
</div>
</div>
<div className="form-group">
<div>Lightning Address:</div>
<div>LN Address:</div>
<div>
<input type="text" value={lud16} onChange={(e) => setLud16(e.target.value)} />
</div>
@ -116,18 +136,23 @@ export default function ProfilePage() {
{website}
</div>
</div> : null}
<div className="form-group">
<div>NIP-05:</div>
<div>
{nip05}
</div>
</div>
<div className="form-group">
<div>Lightning Address:</div>
<div>
{lud16}
</div>
</div>
{nip05 ?
<div className="form-group">
<div>NIP-05:</div>
<div>
{nip05}
</div>
</div> : null}
{lud16 ?
<div className="form-group">
<div>LN Address:</div>
<div>
{lud16}&nbsp;
<div className="btn btn-sm" onClick={() => { }}>
<FontAwesomeIcon icon={faQrcode} size="lg" />
</div>
</div>
</div> : null}
</>
)
}
@ -135,15 +160,13 @@ export default function ProfilePage() {
return (
<>
<div className="profile">
<div>
<div style={{ backgroundImage: `url(${picture})` }} className="avatar">
{isMe ?
<div className="edit">
<div>Edit</div>
</div>
: null
}
</div>
<div style={{ backgroundImage: `url(${picture})` }} className="avatar">
{isMe ?
<div className="edit">
<div>Edit</div>
</div>
: null
}
</div>
<div>
{isMe ? editor() : details()}

View File

@ -9,6 +9,26 @@ export default function useEventPublisher() {
const system = useContext(NostrContext);
const pubKey = useSelector(s => s.login.publicKey);
const privKey = useSelector(s => s.login.privateKey);
const nip07 = useSelector(s => s.login.nip07);
const hasNip07 = 'nostr' in window;
/**
*
* @param {Event} ev
* @param {*} privKey
* @returns
*/
async function signEvent(ev, privKey) {
if(nip07 === true && hasNip07) {
ev.Id = await ev.CreateId();
let tmpEv = await window.nostr.signEvent(ev.ToObject());
console.log(tmpEv);
return Event.FromObject(tmpEv);
} else {
await ev.Sign(privKey);
}
return ev;
}
return {
broadcast: (ev) => {
@ -19,8 +39,7 @@ export default function useEventPublisher() {
let ev = Event.ForPubKey(pubKey);
ev.Kind = EventKind.SetMetadata;
ev.Content = JSON.stringify(obj);
await ev.Sign(privKey);
return ev;
return await signEvent(ev, privKey);
},
note: async (msg) => {
if(typeof msg !== "string") {
@ -29,8 +48,7 @@ export default function useEventPublisher() {
let ev = Event.ForPubKey(pubKey);
ev.Kind = EventKind.TextNote;
ev.Content = msg;
await ev.Sign(privKey);
return ev;
return await signEvent(ev, privKey);
},
like: async (evRef) => {
let ev = Event.ForPubKey(pubKey);
@ -38,8 +56,7 @@ export default function useEventPublisher() {
ev.Content = "+";
ev.Tags.push(new Tag(["e", evRef.Id], 0));
ev.Tags.push(new Tag(["p", evRef.PubKey], 1));
await ev.Sign(privKey);
return ev;
return await signEvent(ev, privKey);
},
dislike: async (evRef) => {
let ev = Event.ForPubKey(pubKey);
@ -47,8 +64,7 @@ export default function useEventPublisher() {
ev.Content = "-";
ev.Tags.push(new Tag(["e", evRef.Id], 0));
ev.Tags.push(new Tag(["p", evRef.PubKey], 1));
await ev.Sign(privKey);
return ev;
return await signEvent(ev, privKey);
}
}
}

View File

@ -24,7 +24,12 @@ const LoginSlice = createSlice({
/**
* A list of pubkeys this user follows
*/
follows: []
follows: [],
/**
* Login keys are managed by extension
*/
nip07: false,
},
reducers: {
init: (state) => {
@ -47,6 +52,10 @@ const LoginSlice = createSlice({
setPublicKey: (state, action) => {
state.publicKey = action.payload;
},
setNip07PubKey: (state, action) => {
state.publicKey = action.payload;
state.nip07 = true;
},
setRelays: (state, action) => {
state.relays = action.payload;
},
@ -60,5 +69,5 @@ const LoginSlice = createSlice({
}
});
export const { init, setPrivateKey, setPublicKey, setRelays, setFollows, logout } = LoginSlice.actions;
export const { init, setPrivateKey, setPublicKey, setNip07PubKey, setRelays, setFollows, logout } = LoginSlice.actions;
export const reducer = LoginSlice.reducer;

View File

@ -1200,6 +1200,32 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@fortawesome/fontawesome-common-types@6.2.1":
version "6.2.1"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.2.1.tgz#411e02a820744d3f7e0d8d9df9d82b471beaa073"
integrity sha512-Sz07mnQrTekFWLz5BMjOzHl/+NooTdW8F8kDQxjWwbpOJcnoSg4vUDng8d/WR1wOxM0O+CY9Zw0nR054riNYtQ==
"@fortawesome/fontawesome-svg-core@^6.2.1":
version "6.2.1"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.2.1.tgz#e87e905e444b5e7b715af09b64d27b53d4c8f9d9"
integrity sha512-HELwwbCz6C1XEcjzyT1Jugmz2NNklMrSPjZOWMlc+ZsHIVk+XOvOXLGGQtFBwSyqfJDNgRq4xBCwWOaZ/d9DEA==
dependencies:
"@fortawesome/fontawesome-common-types" "6.2.1"
"@fortawesome/free-solid-svg-icons@^6.2.1":
version "6.2.1"
resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.2.1.tgz#2290ea5adcf1537cbd0c43de6feb38af02141d27"
integrity sha512-oKuqrP5jbfEPJWTij4sM+/RvgX+RMFwx3QZCZcK9PrBDgxC35zuc7AOFsyMjMd/PIFPeB2JxyqDr5zs/DZFPPw==
dependencies:
"@fortawesome/fontawesome-common-types" "6.2.1"
"@fortawesome/react-fontawesome@^0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz#d90dd8a9211830b4e3c08e94b63a0ba7291ddcf4"
integrity sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==
dependencies:
prop-types "^15.8.1"
"@humanwhocodes/config-array@^0.11.6":
version "0.11.8"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9"