web: break out damus.js into more files
This commit is contained in:
parent
318cbe98ea
commit
e68a022952
8 changed files with 864 additions and 930 deletions
|
@ -11,9 +11,12 @@
|
||||||
<script defer src="js/util.js?v=5"></script>
|
<script defer src="js/util.js?v=5"></script>
|
||||||
<script defer src="js/ui/util.js?v=8"></script>
|
<script defer src="js/ui/util.js?v=8"></script>
|
||||||
<script defer src="js/ui/render.js?v=15"></script>
|
<script defer src="js/ui/render.js?v=15"></script>
|
||||||
|
<script defer src="js/ui/state.js?v=1"></script>
|
||||||
|
<script defer src="js/ui/fmt.js?v=1"></script>
|
||||||
<script defer src="js/noble-secp256k1.js?v=1"></script>
|
<script defer src="js/noble-secp256k1.js?v=1"></script>
|
||||||
<script defer src="js/bech32.js?v=1"></script>
|
<script defer src="js/bech32.js?v=1"></script>
|
||||||
<script defer src="js/nostr.js?v=7"></script>
|
<script defer src="js/nostr.js?v=7"></script>
|
||||||
|
<script defer src="js/core.js?v=1"></script>
|
||||||
<script defer src="js/damus.js?v=92"></script>
|
<script defer src="js/damus.js?v=92"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
253
web/js/core.js
Normal file
253
web/js/core.js
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
function get_local_state(key) {
|
||||||
|
if (DAMUS[key] != null)
|
||||||
|
return DAMUS[key]
|
||||||
|
return localStorage.getItem(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_local_state(key, val) {
|
||||||
|
DAMUS[key] = val
|
||||||
|
localStorage.setItem(key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sign_id(privkey, id) {
|
||||||
|
//const digest = nostrjs.hex_decode(id)
|
||||||
|
const sig = await nobleSecp256k1.schnorr.sign(id, privkey)
|
||||||
|
return nostrjs.hex_encode(sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function broadcast_related_events(ev) {
|
||||||
|
ev.tags
|
||||||
|
.reduce((evs, tag) => {
|
||||||
|
// cap it at something sane
|
||||||
|
if (evs.length >= 5)
|
||||||
|
return evs
|
||||||
|
const ev = get_tag_event(tag)
|
||||||
|
if (!ev)
|
||||||
|
return evs
|
||||||
|
insert_event_sorted(evs, ev) // for uniqueness
|
||||||
|
return evs
|
||||||
|
}, [])
|
||||||
|
.forEach((ev, i) => {
|
||||||
|
// so we don't get rate limited
|
||||||
|
setTimeout(() => {
|
||||||
|
log_debug("broadcasting related event", ev)
|
||||||
|
broadcast_event(ev)
|
||||||
|
}, (i+1)*1200)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function broadcast_event(ev) {
|
||||||
|
DAMUS.pool.send(["EVENT", ev])
|
||||||
|
}
|
||||||
|
|
||||||
|
/*async function update_profile() {
|
||||||
|
const kind = 0
|
||||||
|
const created_at = new_creation_time()
|
||||||
|
const pubkey = await get_pubkey()
|
||||||
|
const content = JSON.stringify({
|
||||||
|
name: "test",
|
||||||
|
about: "Testing",
|
||||||
|
picture: "",
|
||||||
|
nip05: ""
|
||||||
|
})
|
||||||
|
|
||||||
|
let ev = { pubkey, content, created_at, kind }
|
||||||
|
ev.id = await nostrjs.calculate_id(ev)
|
||||||
|
ev = await sign_event(ev)
|
||||||
|
broadcast_event(ev)
|
||||||
|
// TODO add error checking on updating profile
|
||||||
|
}*/
|
||||||
|
|
||||||
|
async function sign_event(ev) {
|
||||||
|
if (window.nostr && window.nostr.signEvent) {
|
||||||
|
const signed = await window.nostr.signEvent(ev)
|
||||||
|
if (typeof signed === 'string') {
|
||||||
|
ev.sig = signed
|
||||||
|
return ev
|
||||||
|
}
|
||||||
|
return signed
|
||||||
|
}
|
||||||
|
|
||||||
|
const privkey = get_privkey()
|
||||||
|
ev.sig = await sign_id(privkey, ev.id)
|
||||||
|
return ev
|
||||||
|
}
|
||||||
|
|
||||||
|
async function send_post() {
|
||||||
|
const input_el = document.querySelector("#post-input")
|
||||||
|
const cw_el = document.querySelector("#content-warning-input")
|
||||||
|
const cw = cw_el.value
|
||||||
|
const content = input_el.value
|
||||||
|
const created_at = new_creation_time()
|
||||||
|
const kind = 1
|
||||||
|
const tags = cw ? [["content-warning", cw]] : []
|
||||||
|
const pubkey = await get_pubkey()
|
||||||
|
|
||||||
|
let post = { pubkey, tags, content, created_at, kind }
|
||||||
|
post.id = await nostrjs.calculate_id(post)
|
||||||
|
post = await sign_event(post)
|
||||||
|
broadcast_event(post)
|
||||||
|
|
||||||
|
input_el.value = ""
|
||||||
|
cw_el.value = ""
|
||||||
|
post_input_changed(input_el)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function create_reply(pubkey, content, from) {
|
||||||
|
const tags = gather_reply_tags(pubkey, from)
|
||||||
|
const created_at = Math.floor(new Date().getTime() / 1000)
|
||||||
|
let kind = from.kind
|
||||||
|
|
||||||
|
// convert emoji replies into reactions
|
||||||
|
if (is_valid_reaction_content(content))
|
||||||
|
kind = 7
|
||||||
|
|
||||||
|
let reply = { pubkey, tags, content, created_at, kind }
|
||||||
|
|
||||||
|
reply.id = await nostrjs.calculate_id(reply)
|
||||||
|
reply = await sign_event(reply)
|
||||||
|
return reply
|
||||||
|
}
|
||||||
|
|
||||||
|
async function send_reply(content, replying_to) {
|
||||||
|
const ev = DAMUS.all_events[replying_to]
|
||||||
|
if (!ev)
|
||||||
|
return
|
||||||
|
|
||||||
|
const pubkey = await get_pubkey()
|
||||||
|
let reply = await create_reply(pubkey, content, ev)
|
||||||
|
|
||||||
|
broadcast_event(reply)
|
||||||
|
broadcast_related_events(reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function create_deletion_event(pubkey, target, content="") {
|
||||||
|
const created_at = Math.floor(new Date().getTime() / 1000)
|
||||||
|
let kind = 5
|
||||||
|
|
||||||
|
const tags = [["e", target]]
|
||||||
|
let del = { pubkey, tags, content, created_at, kind }
|
||||||
|
|
||||||
|
del.id = await nostrjs.calculate_id(del)
|
||||||
|
del = await sign_event(del)
|
||||||
|
return del
|
||||||
|
}
|
||||||
|
|
||||||
|
async function delete_post(id, reason) {
|
||||||
|
const ev = DAMUS.all_events[id]
|
||||||
|
if (!ev)
|
||||||
|
return
|
||||||
|
|
||||||
|
const pubkey = await get_pubkey()
|
||||||
|
let del = await create_deletion_event(pubkey, id, reason)
|
||||||
|
broadcast_event(del)
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_reactions(model, evid) {
|
||||||
|
const reactions_set = model.reactions_to[evid]
|
||||||
|
if (!reactions_set)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
let reactions = []
|
||||||
|
for (const id of reactions_set.keys()) {
|
||||||
|
if (is_deleted(model, id))
|
||||||
|
continue
|
||||||
|
const reaction = model.all_events[id]
|
||||||
|
if (!reaction)
|
||||||
|
continue
|
||||||
|
reactions.push(reaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
const groups = reactions.reduce((grp, r) => {
|
||||||
|
const e = get_reaction_emoji(r)
|
||||||
|
grp[e] = grp[e] || {}
|
||||||
|
grp[e][r.pubkey] = r
|
||||||
|
return grp
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
return groups
|
||||||
|
}
|
||||||
|
|
||||||
|
function gather_reply_tags(pubkey, from) {
|
||||||
|
let tags = []
|
||||||
|
let ids = new Set()
|
||||||
|
|
||||||
|
if (from.refs && from.refs.root) {
|
||||||
|
tags.push(["e", from.refs.root, "", "root"])
|
||||||
|
ids.add(from.refs.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
tags.push(["e", from.id, "", "reply"])
|
||||||
|
ids.add(from.id)
|
||||||
|
|
||||||
|
for (const tag of from.tags) {
|
||||||
|
if (tag.length >= 2) {
|
||||||
|
if (tag[0] === "p" && tag[1] !== pubkey) {
|
||||||
|
if (!ids.has(tag[1])) {
|
||||||
|
tags.push(["p", tag[1]])
|
||||||
|
ids.add(tag[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (from.pubkey !== pubkey && !ids.has(from.pubkey)) {
|
||||||
|
tags.push(["p", from.pubkey])
|
||||||
|
}
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_tag_event(tag)
|
||||||
|
{
|
||||||
|
if (tag.length < 2)
|
||||||
|
return null
|
||||||
|
|
||||||
|
if (tag[0] === "e")
|
||||||
|
return DAMUS.all_events[tag[1]]
|
||||||
|
|
||||||
|
if (tag[0] === "p")
|
||||||
|
return DAMUS.all_events[DAMUS.profile_events[tag[1]]]
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function* yield_etags(tags) {
|
||||||
|
for (const tag of tags) {
|
||||||
|
if (tag.length >= 2 && tag[0] === "e")
|
||||||
|
yield tag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_content_warning(tags) {
|
||||||
|
for (const tag of tags) {
|
||||||
|
if (tag.length >= 1 && tag[0] === "content-warning")
|
||||||
|
return tag[1] || ""
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
async function get_nip05_pubkey(email) {
|
||||||
|
const [user, host] = email.split("@")
|
||||||
|
const url = `https://${host}/.well-known/nostr.json?name=${user}`
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(url)
|
||||||
|
const json = await res.json()
|
||||||
|
log_debug("nip05 data", json)
|
||||||
|
return json.names[user]
|
||||||
|
} catch (e) {
|
||||||
|
log_error("fetching nip05 entry for %s", email, e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO rename handle_pubkey to fetch_pubkey
|
||||||
|
async function handle_pubkey(pubkey) {
|
||||||
|
if (pubkey[0] === "n")
|
||||||
|
pubkey = bech32_decode(pubkey)
|
||||||
|
if (pubkey.includes("@"))
|
||||||
|
pubkey = await get_nip05_pubkey(pubkey)
|
||||||
|
set_local_state('pubkey', pubkey)
|
||||||
|
return pubkey
|
||||||
|
}
|
||||||
|
|
1037
web/js/damus.js
1037
web/js/damus.js
File diff suppressed because it is too large
Load diff
74
web/js/ui/fmt.js
Normal file
74
web/js/ui/fmt.js
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
function linkify(text, show_media) {
|
||||||
|
return text.replace(URL_REGEX, function(match, p1, p2, p3) {
|
||||||
|
const url = p2+p3
|
||||||
|
const parsed = new URL(url)
|
||||||
|
let html;
|
||||||
|
if (show_media && is_img_url(parsed.pathname)) {
|
||||||
|
html = `
|
||||||
|
<img class="inline-img clickable" src="${url}" onclick="open_media_preview('${url}', 'image')"/>
|
||||||
|
`;
|
||||||
|
} else if (show_media && is_video_url(parsed.pathname)) {
|
||||||
|
html = `
|
||||||
|
<video controls class="inline-img" />
|
||||||
|
<source src="${url}">
|
||||||
|
</video>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
html = `<a target="_blank" rel="noopener noreferrer" href="${url}">${url}</a>`;
|
||||||
|
}
|
||||||
|
return p1+html;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function format_content(ev, show_media) {
|
||||||
|
if (ev.kind === 7) {
|
||||||
|
if (ev.content === "" || ev.content === "+")
|
||||||
|
return "❤️"
|
||||||
|
return sanitize(ev.content.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = ev.content.trim()
|
||||||
|
const body = convert_quote_blocks(content, show_media)
|
||||||
|
|
||||||
|
let cw = get_content_warning(ev.tags)
|
||||||
|
if (cw !== null) {
|
||||||
|
let cwHTML = "Content Warning"
|
||||||
|
if (cw === "") {
|
||||||
|
cwHTML += "."
|
||||||
|
} else {
|
||||||
|
cwHTML += `: "<span>${cw}</span>".`
|
||||||
|
}
|
||||||
|
const open = !!DAMUS.cw_open[ev.id]? "open" : ""
|
||||||
|
return `
|
||||||
|
<details ontoggle="toggle_content_warning(this)" class="cw" id="cw_${ev.id}" ${open}>
|
||||||
|
<summary class="event-message">${cwHTML}</summary>
|
||||||
|
${body}
|
||||||
|
</details>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
|
||||||
|
function convert_quote_blocks(content, show_media)
|
||||||
|
{
|
||||||
|
const split = content.split("\n")
|
||||||
|
let blockin = false
|
||||||
|
return split.reduce((str, line) => {
|
||||||
|
if (line !== "" && line[0] === '>') {
|
||||||
|
if (!blockin) {
|
||||||
|
str += "<span class='quote'>"
|
||||||
|
blockin = true
|
||||||
|
}
|
||||||
|
str += linkify(sanitize(line.slice(1)), show_media)
|
||||||
|
} else {
|
||||||
|
if (blockin) {
|
||||||
|
blockin = false
|
||||||
|
str += "</span>"
|
||||||
|
}
|
||||||
|
str += linkify(sanitize(line), show_media)
|
||||||
|
}
|
||||||
|
return str + "<br/>"
|
||||||
|
}, "")
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ function render_timeline_event(damus, view, ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
function render_events(damus, view) {
|
function render_events(damus, view) {
|
||||||
log_debug("rendering events")
|
|
||||||
return view.events
|
return view.events
|
||||||
.filter((ev, i) => i < 140)
|
.filter((ev, i) => i < 140)
|
||||||
.map((ev) => render_timeline_event(damus, view, ev)).join("\n")
|
.map((ev) => render_timeline_event(damus, view, ev)).join("\n")
|
||||||
|
@ -350,3 +349,4 @@ function render_loading_spinner()
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
116
web/js/ui/state.js
Normal file
116
web/js/ui/state.js
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
function get_view_el(name) {
|
||||||
|
return DAMUS.view_el.querySelector(`#${name}-view`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_default_max_depth(damus, view) {
|
||||||
|
return view.max_depth || damus.max_depth
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_thread_max_depth(damus, view, root_id) {
|
||||||
|
if (!view.depths[root_id])
|
||||||
|
return get_default_max_depth(damus, view)
|
||||||
|
|
||||||
|
return view.depths[root_id]
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldnt_render_event(our_pk, view, ev, opts) {
|
||||||
|
return !opts.is_composing &&
|
||||||
|
!view.expanded.has(ev.id) &&
|
||||||
|
view.rendered.has(ev.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle_content_warning(el) {
|
||||||
|
const id = el.id.split("_")[1]
|
||||||
|
const ev = DAMUS.all_events[id]
|
||||||
|
|
||||||
|
if (!ev) {
|
||||||
|
log_debug("could not find content-warning event", id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DAMUS.cw_open[id] = el.open
|
||||||
|
}
|
||||||
|
|
||||||
|
function expand_thread(id, reply_id) {
|
||||||
|
const view = get_current_view()
|
||||||
|
const root_id = get_thread_root_id(DAMUS, id)
|
||||||
|
if (!root_id) {
|
||||||
|
log_debug("could not get root_id for", DAMUS.all_events[id])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
view.expanded.add(reply_id)
|
||||||
|
view.depths[root_id] = get_thread_max_depth(DAMUS, view, root_id) + 1
|
||||||
|
redraw_events(DAMUS, view)
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_thread_root_id(damus, id) {
|
||||||
|
const ev = damus.all_events[id]
|
||||||
|
if (!ev) {
|
||||||
|
log_debug("expand_thread: no event found?", id)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return ev.refs && ev.refs.root
|
||||||
|
}
|
||||||
|
|
||||||
|
function redraw_events(damus, view) {
|
||||||
|
//log_debug("redrawing events for", view)
|
||||||
|
view.rendered = new Set()
|
||||||
|
const events_el = damus.view_el.querySelector(`#${view.name}-view > .events`)
|
||||||
|
events_el.innerHTML = render_events(damus, view)
|
||||||
|
}
|
||||||
|
|
||||||
|
function redraw_timeline_events(damus, name) {
|
||||||
|
const view = DAMUS.views[name]
|
||||||
|
const events_el = damus.view_el.querySelector(`#${name}-view > .events`)
|
||||||
|
if (view.events.length > 0) {
|
||||||
|
redraw_events(damus, view)
|
||||||
|
} else {
|
||||||
|
events_el.innerHTML = render_loading_spinner()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function switch_view(name, opts={})
|
||||||
|
{
|
||||||
|
if (name === DAMUS.current_view) {
|
||||||
|
log_debug("Not switching to '%s', we are already there", name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const last = get_current_view()
|
||||||
|
if (!last) {
|
||||||
|
// render initial
|
||||||
|
DAMUS.current_view = name
|
||||||
|
redraw_timeline_events(DAMUS, name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("switching to '%s' by hiding '%s'", name, DAMUS.current_view)
|
||||||
|
|
||||||
|
DAMUS.current_view = name
|
||||||
|
const current = get_current_view()
|
||||||
|
const last_el = get_view_el(last.name)
|
||||||
|
const current_el = get_view_el(current.name)
|
||||||
|
|
||||||
|
if (last_el)
|
||||||
|
last_el.classList.add("hide");
|
||||||
|
|
||||||
|
// TODO accomodate views that do not render events
|
||||||
|
// TODO find out if having multiple event divs is slow
|
||||||
|
//redraw_timeline_events(DAMUS, name)
|
||||||
|
|
||||||
|
find_node("#nav > div[data-active]").dataset.active = name;
|
||||||
|
|
||||||
|
if (current_el)
|
||||||
|
current_el.classList.remove("hide");
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_current_view()
|
||||||
|
{
|
||||||
|
// TODO resolve memory & html descriptencies
|
||||||
|
// Currently there is tracking of which divs are visible in HTML/CSS and
|
||||||
|
// which is active in memory, simply resolve this by finding the visible
|
||||||
|
// element instead of tracking it in memory (or remove dom elements). This
|
||||||
|
// would simplify state tracking IMO - Thomas
|
||||||
|
return DAMUS.views[DAMUS.current_view]
|
||||||
|
}
|
||||||
|
|
|
@ -129,3 +129,136 @@ function open_media_preview(url, type) {
|
||||||
function close_media_preview() {
|
function close_media_preview() {
|
||||||
find_node("#media-preview").classList.add("closed");
|
find_node("#media-preview").classList.add("closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function close_reply() {
|
||||||
|
const modal = document.querySelector("#reply-modal")
|
||||||
|
modal.classList.add("closed");
|
||||||
|
}
|
||||||
|
|
||||||
|
function press_logout() {
|
||||||
|
if (confirm("Are you sure you want to sign out?")) {
|
||||||
|
localStorage.clear();
|
||||||
|
const url = new URL(location.href)
|
||||||
|
url.searchParams.delete("pk")
|
||||||
|
window.location.href = url.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete_post_confirm(evid) {
|
||||||
|
if (!confirm("Are you sure you want to delete this post?"))
|
||||||
|
return
|
||||||
|
|
||||||
|
const reason = (prompt("Why you are deleting this? Leave empty to not specify. Type CANCEL to cancel.") || "").trim()
|
||||||
|
|
||||||
|
if (reason.toLowerCase() === "cancel")
|
||||||
|
return
|
||||||
|
|
||||||
|
delete_post(evid, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function do_send_reply() {
|
||||||
|
const modal = document.querySelector("#reply-modal")
|
||||||
|
const replying_to = modal.querySelector("#replying-to")
|
||||||
|
|
||||||
|
const evid = replying_to.dataset.evid
|
||||||
|
const reply_content_el = document.querySelector("#reply-content")
|
||||||
|
const content = reply_content_el.value
|
||||||
|
|
||||||
|
await send_reply(content, evid)
|
||||||
|
|
||||||
|
reply_content_el.value = ""
|
||||||
|
|
||||||
|
close_reply()
|
||||||
|
}
|
||||||
|
|
||||||
|
function reply_to(evid) {
|
||||||
|
const modal = document.querySelector("#reply-modal")
|
||||||
|
const replybox = modal.querySelector("#reply-content")
|
||||||
|
modal.classList.remove("closed")
|
||||||
|
const replying_to = modal.querySelector("#replying-to")
|
||||||
|
|
||||||
|
replying_to.dataset.evid = evid
|
||||||
|
|
||||||
|
const ev = DAMUS.all_events[evid]
|
||||||
|
const view = get_current_view()
|
||||||
|
replying_to.innerHTML = render_event(DAMUS, view, ev, {is_composing: true, nobar: true, max_depth: 1})
|
||||||
|
|
||||||
|
replybox.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function redraw_my_pfp(model, force = false) {
|
||||||
|
const p = model.profiles[model.pubkey]
|
||||||
|
if (!p) return;
|
||||||
|
const html = render_pfp(model.pubkey, p);
|
||||||
|
const el = document.querySelector(".my-userpic")
|
||||||
|
if (!force && el.dataset.loaded) return;
|
||||||
|
el.dataset.loaded = true;
|
||||||
|
el.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_favicon(path)
|
||||||
|
{
|
||||||
|
let link = document.querySelector("link[rel~='icon']");
|
||||||
|
const head = document.getElementsByTagName('head')[0]
|
||||||
|
|
||||||
|
if (!link) {
|
||||||
|
link = document.createElement('link');
|
||||||
|
link.rel = 'icon';
|
||||||
|
head.appendChild(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
link.href = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update_title updates the document title & visual indicators based on if the
|
||||||
|
// number of notifications that are unseen by the user.
|
||||||
|
function update_title(model) {
|
||||||
|
// TODO rename update_title to update_notification_state or similar
|
||||||
|
// TODO only clear notifications once they have seen all targeted events
|
||||||
|
if (document.visibilityState === 'visible') {
|
||||||
|
model.notifications = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const num = model.notifications
|
||||||
|
const has_notes = num !== 0
|
||||||
|
document.title = has_notes ? `(${num}) Damus` : "Damus";
|
||||||
|
update_favicon(has_notes ? "img/damus_notif.svg" : "img/damus.svg");
|
||||||
|
update_notification_markers(has_notes)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function get_pubkey() {
|
||||||
|
let pubkey = get_local_state('pubkey')
|
||||||
|
// qs pk overrides stored key
|
||||||
|
const qs_pk = get_qs().get("pk")
|
||||||
|
if (qs_pk)
|
||||||
|
return await handle_pubkey(qs_pk)
|
||||||
|
if (pubkey)
|
||||||
|
return pubkey
|
||||||
|
if (window.nostr && window.nostr.getPublicKey) {
|
||||||
|
console.log("calling window.nostr.getPublicKey()...")
|
||||||
|
const pubkey = await window.nostr.getPublicKey()
|
||||||
|
console.log("got %s pubkey from nos2x", pubkey)
|
||||||
|
return await handle_pubkey(pubkey)
|
||||||
|
}
|
||||||
|
pubkey = prompt("Enter nostr id (eg: jb55@jb55.com) or pubkey (hex or npub)")
|
||||||
|
if (!pubkey)
|
||||||
|
throw new Error("Need pubkey to continue")
|
||||||
|
return await handle_pubkey(pubkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_privkey() {
|
||||||
|
let privkey = get_local_state('privkey')
|
||||||
|
if (privkey)
|
||||||
|
return privkey
|
||||||
|
if (!privkey)
|
||||||
|
privkey = prompt("Enter private key")
|
||||||
|
if (!privkey)
|
||||||
|
throw new Error("can't get privkey")
|
||||||
|
if (privkey[0] === "n") {
|
||||||
|
privkey = bech32_decode(privkey)
|
||||||
|
}
|
||||||
|
set_local_state('privkey', privkey)
|
||||||
|
return privkey
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
144
web/js/util.js
144
web/js/util.js
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue