diff --git a/package-lock.json b/package-lock.json
index bd7313c5..81a8a206 100644
Binary files a/package-lock.json and b/package-lock.json differ
diff --git a/package.json b/package.json
index b73adf46..42ccf199 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
- "lint": "eslint src/*/** --quiet"
+ "lint": "eslint src/*/** --quiet",
+ "check": "svelte-check --tsconfig ./tsconfig.json --threshold error"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^1.1.0",
@@ -23,6 +24,7 @@
"@bugsnag/js": "^7.18.0",
"@fortawesome/fontawesome-free": "^6.2.1",
"@noble/secp256k1": "^1.7.0",
+ "@tsconfig/svelte": "^3.0.0",
"classnames": "^2.3.2",
"compressorjs": "^1.1.1",
"dexie": "^3.2.2",
@@ -31,8 +33,10 @@
"hurdak": "github:ConsignCloud/hurdak",
"nostr-tools": "^1.1.1",
"ramda": "^0.28.0",
+ "svelte-check": "^3.0.3",
"svelte-link-preview": "^0.3.3",
"svelte-loading-spinners": "^0.3.4",
+ "svelte-preprocess": "^5.0.1",
"svelte-routing": "^1.6.0",
"svelte-switch": "^0.0.5",
"throttle-debounce": "^5.0.0",
diff --git a/src/App.svelte b/src/App.svelte
index 80bb3bca..e0355f01 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -130,7 +130,7 @@
document.body.style.position = `fixed`
}
} else {
- document.body.style = ''
+ document.body.setAttribute('style', '')
window.scrollTo(0, scrollY)
}
})
@@ -151,18 +151,18 @@
{#key params.npub}
-
+
{/key}
{#key params.entity}
-
+
{/key}
{#key params.entity}
-
+
{/key}
@@ -173,7 +173,7 @@
{#key params.entity}
-
+
{/key}
@@ -259,10 +259,10 @@
border-b border-medium z-10"
>
-
+
-
+
Coracle
{#if $mostRecentAlert > $lastCheckedAlerts || hasNewMessages}
@@ -272,12 +272,12 @@
{#if keys.canSign()}
{/if}
diff --git a/src/agent/data.js b/src/agent/data.js
index 49a71109..917b8a42 100644
--- a/src/agent/data.js
+++ b/src/agent/data.js
@@ -31,7 +31,7 @@ export const ready = writable(false)
export const people = writable([])
// Bootstrap our people observable
-db.people.toArray().then($p => {
+db.table('people').toArray().then($p => {
people.set(createMap('pubkey', $p))
ready.set(true)
})
@@ -51,7 +51,7 @@ export const updatePeople = async updates => {
people.update($people => ({...$people, ...updates}))
// Sync to our database
- await db.people.bulkPut(Object.values(updates))
+ await db.table('people').bulkPut(Object.values(updates))
}
// Hooks
@@ -126,7 +126,7 @@ const processProfileEvents = async events => {
}
},
default: () => {
- console.log(`Received unsupported event type ${event.kind}`)
+ console.log(`Received unsupported event type ${e.kind}`)
},
}),
updated_at: now(),
@@ -155,7 +155,7 @@ const processRoomEvents = async events => {
continue
}
- const room = await db.rooms.get(roomId)
+ const room = await db.table('rooms').get(roomId)
// Merge edits but don't let old ones override new ones
if (room?.edited_at > e.created_at) {
@@ -178,7 +178,7 @@ const processRoomEvents = async events => {
}
}
- await db.rooms.bulkPut(Object.values(updates))
+ await db.table('rooms').bulkPut(Object.values(updates))
}
const processMessages = async events => {
@@ -187,7 +187,7 @@ const processMessages = async events => {
.map(e => ({...e, recipient: Tags.from(e).type("p").values().first()}))
if (messages.length > 0) {
- await db.messages.bulkPut(messages)
+ await db.table('messages').bulkPut(messages)
}
}
diff --git a/src/agent/index.js b/src/agent/index.ts
similarity index 93%
rename from src/agent/index.js
rename to src/agent/index.ts
index de8c74b0..a641d7b8 100644
--- a/src/agent/index.js
+++ b/src/agent/index.ts
@@ -37,7 +37,7 @@ export const getFollows = pubkey => {
return Tags.wrap(person?.petnames || defaults.petnames).values().all()
}
-export const getRelays = pubkey => {
+export const getRelays = (pubkey?: string) => {
let relays = getPerson(pubkey)?.relays
if (!relays?.length) {
@@ -71,7 +71,7 @@ export const publish = async (relays, event) => {
return signedEvent
}
-export const load = async (relays, filter, opts) => {
+export const load = async (relays, filter, opts?) => {
const events = await pool.request(relays, filter, opts)
await processEvents(events)
@@ -79,7 +79,7 @@ export const load = async (relays, filter, opts) => {
return events
}
-export const listen = async (relays, filter, onEvent, {shouldProcess = true} = {}) => {
+export const listen = async (relays, filter, onEvent, {shouldProcess = true}: any = {}) => {
const sub = await pool.subscribe(relays, filter)
sub.onEvent(e => {
diff --git a/src/agent/keys.js b/src/agent/keys.ts
similarity index 75%
rename from src/agent/keys.js
rename to src/agent/keys.ts
index 9bd9285c..0b6f5ed5 100644
--- a/src/agent/keys.js
+++ b/src/agent/keys.ts
@@ -7,8 +7,8 @@ let signingFunction
const pubkey = synced('agent/user/pubkey')
const privkey = synced('agent/user/privkey')
-const hasExtension = () => Boolean(window.nostr)
-const canSign = () => Boolean(hasExtension() || get(privkey))
+const getExtension = () => (window as {nostr?: any}).nostr
+const canSign = () => Boolean(getExtension() || get(privkey))
const setPrivateKey = _privkey => {
privkey.set(_privkey)
@@ -16,9 +16,11 @@ const setPrivateKey = _privkey => {
}
const setPublicKey = _pubkey => {
- if (window.nostr) {
+ const nostr = getExtension()
+
+ if (nostr) {
signingFunction = async event => {
- const {sig} = await window.nostr.signEvent(event)
+ const {sig} = await nostr.signEvent(event)
return sig
}
@@ -44,8 +46,9 @@ const sign = async event => {
const getCrypt = () => {
const $privkey = get(privkey)
+ const nostr = getExtension()
- if (!$privkey && !hasExtension()) {
+ if (!$privkey && !nostr) {
throw new Error('No encryption method available.')
}
@@ -53,13 +56,13 @@ const getCrypt = () => {
encrypt: (pubkey, message) => {
return $privkey
? nip04.encrypt($privkey, pubkey, message)
- : window.nostr.nip04.encrypt(pubkey, message)
+ : nostr.nip04.encrypt(pubkey, message)
},
decrypt: async (pubkey, message) => {
try {
return $privkey
? nip04.decrypt($privkey, pubkey, message)
- : await window.nostr.nip04.decrypt(pubkey, message)
+ : await nostr.nip04.decrypt(pubkey, message)
} catch (e) {
console.error(e)
return ``
@@ -72,6 +75,6 @@ const getCrypt = () => {
setPublicKey(get(pubkey))
export default {
- pubkey, privkey, hasExtension, canSign, setPrivateKey, setPublicKey, clear,
+ pubkey, privkey, canSign, setPrivateKey, setPublicKey, clear,
sign, getCrypt,
}
diff --git a/src/agent/pool.js b/src/agent/pool.js
index 45e480e8..a01b4f71 100644
--- a/src/agent/pool.js
+++ b/src/agent/pool.js
@@ -71,7 +71,7 @@ const findConnection = url => find(whereEq({url}), connections)
const connect = async url => {
const conn = findConnection(url) || new Connection(url)
- await db.relays.put({url})
+ await db.table('relays').put({url})
await Promise.race([conn.connect(), sleep(5000)])
if (conn.status === 'ready') {
diff --git a/src/app/alerts.js b/src/app/alerts.js
index 305f9ba7..fe98c6fd 100644
--- a/src/app/alerts.js
+++ b/src/app/alerts.js
@@ -17,7 +17,7 @@ const onChunk = async (relays, pubkey, events) => {
const context = await loaders.loadContext(relays, events, {threshold: 2})
const notes = threadify(events, context, {muffle: getMuffle()})
- await db.alerts.bulkPut(notes)
+ await db.table('alerts').bulkPut(notes)
mostRecentAlert.update($t => events.reduce((t, e) => Math.max(t, e.created_at), $t))
}
diff --git a/src/app/index.js b/src/app/index.ts
similarity index 96%
rename from src/app/index.js
rename to src/app/index.ts
index 3a76e49d..b07798de 100644
--- a/src/app/index.js
+++ b/src/app/index.ts
@@ -14,7 +14,7 @@ import loaders from 'src/app/loaders'
export {toast, modal, settings, alerts, messages, logUsage}
-export const login = async ({privkey, pubkey}, usingExtension = false) => {
+export const login = async ({privkey, pubkey}: {privkey?: string, pubkey?: string}, usingExtension = false) => {
if (privkey) {
keys.setPrivateKey(privkey)
} else {
@@ -79,7 +79,7 @@ export const setRelayWriteCondition = async (url, write) => {
}
}
-export const render = (note, {showEntire = false}) => {
+export const renderNote = (note, {showEntire = false}) => {
const shouldEllipsize = note.content.length > 500 && !showEntire
const $people = get(people)
const peopleByPubkey = createMap(
diff --git a/src/app/loaders.js b/src/app/loaders.ts
similarity index 97%
rename from src/app/loaders.js
rename to src/app/loaders.ts
index 6681ceca..8dc28081 100644
--- a/src/app/loaders.js
+++ b/src/app/loaders.ts
@@ -44,7 +44,7 @@ const loadNetwork = async (relays, pubkey) => {
await loadPeople(tags.relays(), tags.values().all())
}
-const loadContext = async (relays, notes, {loadParents = false, depth = 0, ...opts} = {}) => {
+const loadContext = async (relays, notes, {loadParents = false, depth = 0, ...opts}: any = {}) => {
notes = ensurePlural(notes)
if (notes.length === 0) {
@@ -58,7 +58,7 @@ const loadContext = async (relays, notes, {loadParents = false, depth = 0, ...op
const parentTags = uniq(chunk.map(findReply).filter(identity))
const parentIds = Tags.wrap(parentTags).values().all()
const combinedRelays = uniq(relays.concat(Tags.wrap(parentTags).relays()))
- const filter = [{kinds: [1, 7], '#e': chunkIds}]
+ const filter = [{kinds: [1, 7], '#e': chunkIds} as {}]
if (authors.length > 0) {
filter.push({kinds: personKinds, authors})
diff --git a/src/app/messages.js b/src/app/messages.js
index fc2547ad..e850fec1 100644
--- a/src/app/messages.js
+++ b/src/app/messages.js
@@ -25,7 +25,7 @@ const listen = async (relays, pubkey) => {
// Reload annotated messages, don't alert about messages to self
const messages = reject(
e => e.pubkey === e.recipient,
- await db.messages.toArray()
+ await db.table('messages').toArray()
)
if (messages.length > 0) {
diff --git a/src/app/ui.js b/src/app/ui.ts
similarity index 90%
rename from src/app/ui.js
rename to src/app/ui.ts
index b34ebbe7..ad740834 100644
--- a/src/app/ui.js
+++ b/src/app/ui.ts
@@ -1,6 +1,7 @@
import Bugsnag from "@bugsnag/js"
import {prop} from "ramda"
import {uuid} from "hurdak/lib/hurdak"
+import type {Writable} from 'svelte/store'
import {navigate} from "svelte-routing"
import {nip19} from 'nostr-tools'
import {writable, get} from "svelte/store"
@@ -15,7 +16,11 @@ export const routes = {
// Toast
-export const toast = writable(null)
+export interface Toast extends Writable {
+ show(type: string, message: string, timeout?: number): void
+}
+
+export const toast = writable(null) as Toast
toast.show = (type, message, timeout = 5) => {
const id = uuid()
@@ -29,6 +34,7 @@ toast.show = (type, message, timeout = 5) => {
}, timeout * 1000)
}
+
// Modals
export const modal = {
diff --git a/src/main.js b/src/main.js
index fbc6d284..d60b0d51 100644
--- a/src/main.js
+++ b/src/main.js
@@ -9,9 +9,6 @@ Bugsnag.start({
import App from 'src/App.svelte'
-// Annoying global always fails silently. TODO: figure out an eslint rule instead
-window.find = null
-
const app = new App({
target: document.getElementById('app')
})
diff --git a/src/partials/Card.svelte b/src/partials/Card.svelte
index fac3e63a..042a87a0 100644
--- a/src/partials/Card.svelte
+++ b/src/partials/Card.svelte
@@ -6,7 +6,7 @@
export let invertColors = false
-
-
+
diff --git a/src/partials/Channel.svelte b/src/partials/Channel.svelte
index 5cfb41e7..b8fddc2d 100644
--- a/src/partials/Channel.svelte
+++ b/src/partials/Channel.svelte
@@ -9,10 +9,10 @@
import Anchor from 'src/partials/Anchor.svelte'
import Spinner from 'src/partials/Spinner.svelte'
import {user, getPerson} from 'src/agent'
- import {render} from 'src/app'
+ import {renderNote} from 'src/app'
export let name
- export let link
+ export let link = null
export let about
export let picture
export let loadMessages
@@ -42,7 +42,7 @@
}
// flex-col means the first is the last
- const getLastListItem = () => document.querySelector('ul[name=messages] li')
+ const getLastListItem = () => document.querySelector('ul[class=channel-messages] li')
const stickToBottom = async (behavior, cb) => {
const shouldStick = window.scrollY + window.innerHeight > document.body.scrollHeight - 200
@@ -123,7 +123,7 @@
-
+
{#each annotatedMessages as m (m.id)}
-
{#if type === 'chat' && m.showPerson}
@@ -142,7 +142,7 @@
'bg-light text-black rounded-br-none': type === 'dm' && m.person.pubkey === $user.pubkey,
'bg-dark rounded-bl-none': type === 'dm' && m.person.pubkey !== $user.pubkey,
})}>
- {@html render(m, {showEntire: true})}
+ {@html renderNote(m, {showEntire: true})}
@@ -157,7 +157,9 @@
-
navigate("/chat")} />
+
{/if}
@@ -199,12 +201,12 @@
on:keypress={onKeyPress}
class="w-full p-2 text-white bg-medium
placeholder:text-light outline-0 resize-none" />
-
-
+
{#if showNewMessages}
diff --git a/src/partials/Compose.svelte b/src/partials/Compose.svelte
index 525fe1a7..3c0a28be 100644
--- a/src/partials/Compose.svelte
+++ b/src/partials/Compose.svelte
@@ -180,13 +180,13 @@
{#if suggestions.length > 0}
{#each suggestions as person, i (person.pubkey)}
-
pickSuggestion(person)}>
-
+
{/each}
{/if}
diff --git a/src/partials/Input.svelte b/src/partials/Input.svelte
index e5550ce6..b09fb179 100644
--- a/src/partials/Input.svelte
+++ b/src/partials/Input.svelte
@@ -2,7 +2,7 @@
import cx from "classnames"
export let wrapperClass = ""
- export let value
+ export let value = ""
const className = cx(
$$props.class,
diff --git a/src/partials/Like.svelte b/src/partials/Like.svelte
index 0ad25bb4..7b65b374 100644
--- a/src/partials/Like.svelte
+++ b/src/partials/Like.svelte
@@ -24,29 +24,29 @@
}
-
modal.set({type: 'note/detail', note})}>
-
+
{quantify(note.people.length, 'person', 'people')} liked your note.
-
+
{#if isOpen}
-
-
+
{#each uniqBy(prop('pubkey'), note.people) as person (person.pubkey)}
{/each}
-
+
{/if}
{formatTimestamp(note.created_at)}
{ellipsize(note.content, 120)}
-
+
diff --git a/src/partials/Modal.svelte b/src/partials/Modal.svelte
index 2cea8b81..f84e6ecb 100644
--- a/src/partials/Modal.svelte
+++ b/src/partials/Modal.svelte
@@ -12,7 +12,7 @@
}} />
-
diff --git a/src/partials/Note.svelte b/src/partials/Note.svelte
index eb9bad17..aad26c87 100644
--- a/src/partials/Note.svelte
+++ b/src/partials/Note.svelte
@@ -1,4 +1,4 @@
-
{
- if (!e.target.closest('.fa-reply') && !e.target.closest('.note-reply')) {
- resetReply()
- }
- }}
+ on:click={onBodyClick}
on:keydown={e => {
if (e.key === 'Escape') {
resetReply()
@@ -150,30 +156,28 @@
{:else}
-
{@html render(note, {showEntire})}
+
{@html renderNote(note, {showEntire})}
{#each links.slice(-2) as link}
-
e.stopPropagation()}>
+
e.stopPropagation()}>
-
+
{/each}
-
+
{note.replies.length}
- like ? deleteReaction(like) : react("+")} />
+ on:click|stopPropagation={() => like ? deleteReaction(like) : react("+")} />
{likes.length}
- react("-")} />
+ react("-")} />
{flags.length}
@@ -185,13 +189,13 @@
{#if replyMentions.length > 0}
@@ -201,7 +205,7 @@
{#each replyMentions as p}
- removeMention(p)} />
+ removeMention(p)} />
{displayPerson(getPerson(p, true))}
{/each}
@@ -214,10 +218,10 @@
{#if depth > 0}
{#if !showEntire && note.replies.length > 3}
-
+
Show {quantify(note.replies.length - 3, 'other reply', 'more replies')}
-
+
{/if}
{#each note.replies.slice(showEntire ? 0 : -3) as r (r.id)}
diff --git a/src/partials/Notes.svelte b/src/partials/Notes.svelte
index b69e2779..33340fba 100644
--- a/src/partials/Notes.svelte
+++ b/src/partials/Notes.svelte
@@ -48,12 +48,12 @@
{#if newNotes.length > 0}
-
Load {quantify(newNotes.length, 'new note')}
-
+
{/if}
diff --git a/src/partials/Preview.svelte b/src/partials/Preview.svelte
index d971a873..3e8dfcb1 100644
--- a/src/partials/Preview.svelte
+++ b/src/partials/Preview.svelte
@@ -36,7 +36,7 @@
href={url}
class="rounded border border-solid border-medium flex flex-col bg-white overflow-hidden">
{#if preview.image}
-
+
{/if}
{#if preview.title}
diff --git a/src/partials/Room.svelte b/src/partials/Room.svelte
index 3a873a69..0655bb65 100644
--- a/src/partials/Room.svelte
+++ b/src/partials/Room.svelte
@@ -33,7 +33,7 @@
})
-
setRoom(room)}
in:fly={{y: 20}}>
@@ -81,4 +81,4 @@
{/if}
-
+
diff --git a/src/partials/RoomBadge.svelte b/src/partials/RoomBadge.svelte
index 05da05ab..052ee526 100644
--- a/src/partials/RoomBadge.svelte
+++ b/src/partials/RoomBadge.svelte
@@ -3,7 +3,7 @@
export let setRoom
- setRoom(room.id)}>
{/if}
-
+
diff --git a/src/partials/SelectButton.svelte b/src/partials/SelectButton.svelte
index d2258972..681ef27f 100644
--- a/src/partials/SelectButton.svelte
+++ b/src/partials/SelectButton.svelte
@@ -9,14 +9,14 @@
{#each options as option, i}
- 0,
"bg-accent": value === option,
})}
on:click={() => { value = option }}>
{option}
-
+
{/each}
diff --git a/src/partials/Tabs.svelte b/src/partials/Tabs.svelte
index 1edd022f..b6006e28 100644
--- a/src/partials/Tabs.svelte
+++ b/src/partials/Tabs.svelte
@@ -7,13 +7,13 @@
export let setActiveTab
-