diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3ecd0e68..5a993545 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,7 +13,7 @@
- [x] Make chat header overlap main header to save space
- [x] Strip formatting when pasting into Compose
- [x] Upgraded nostr-tools to 1.4.1
-
+)
## 0.2.11
- [x] Converted threshold to percentage
diff --git a/ROADMAP.md b/ROADMAP.md
index b91215ea..2691c3bc 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,7 +1,5 @@
# Current
-- [ ] Review 10002 usage https://github.com/nostr-protocol/nips/blob/master/65.md
-- [ ] Remove relays from people, pull from routes only
- [ ] Fix anon/new user experience
- [ ] Clicking stuff that would publish kicks you to the login page, we should open a modal instead.
- [ ] Separate user info and relays so we can still select/figure out relays for anons
diff --git a/public/fonts/Montserrat b/public/fonts/Montserrat
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/public/fonts/Montserrat
@@ -0,0 +1 @@
+
diff --git a/src/App.svelte b/src/App.svelte
index 1eaeee28..3fb8dfba 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -2,182 +2,78 @@
import "@fortawesome/fontawesome-free/css/fontawesome.css"
import "@fortawesome/fontawesome-free/css/solid.css"
- import {find, is, identity, nthArg, pluck} from 'ramda'
- import {createMap, first} from 'hurdak/lib/hurdak'
- import {writable, get} from "svelte/store"
- import {fly, fade} from "svelte/transition"
- import {cubicInOut} from "svelte/easing"
+ import {onMount} from 'svelte'
import {Router, Route, links, navigate} from "svelte-routing"
import {globalHistory} from "svelte-routing/src/history"
+ import {cubicInOut} from "svelte/easing"
+ import {writable, get} from "svelte/store"
+ import {fly, fade} from "svelte/transition"
+ import {createMap, first} from 'hurdak/lib/hurdak'
+ import {find, is, identity, nthArg, pluck} from 'ramda'
import {log, warn} from 'src/util/logger'
- import {displayPerson, isLike} from 'src/util/nostr'
import {timedelta, shuffle, now, sleep} from 'src/util/misc'
+ import {displayPerson, isLike} from 'src/util/nostr'
import cmd from 'src/agent/cmd'
- import {user} from 'src/agent/user'
- import {getUserRelays} from 'src/agent/relays'
import database from 'src/agent/database'
import keys from 'src/agent/keys'
import network from 'src/agent/network'
import pool from 'src/agent/pool'
+ import {getUserRelays} from 'src/agent/relays'
+ import {relays} from 'src/agent/relays'
import sync from 'src/agent/sync'
- import {modal, toast, settings, logUsage, alerts, messages, loadAppData} from "src/app"
- import {routes} from "src/app/ui"
- import Anchor from 'src/partials/Anchor.svelte'
- import Content from 'src/partials/Content.svelte'
- import Spinner from 'src/partials/Spinner.svelte'
- import Modal from 'src/partials/Modal.svelte'
+ import {user} from 'src/agent/user'
+ import {loadAppData} from "src/app"
+ import alerts from "src/app/alerts"
+ import messages from "src/app/messages"
+ import {modal, toast, settings, routes, menuIsOpen, logUsage} from "src/app/ui"
import RelayCard from "src/partials/RelayCard.svelte"
- import SignUp from "src/views/SignUp.svelte"
- import PersonList from "src/views/PersonList.svelte"
- import PrivKeyLogin from "src/views/PrivKeyLogin.svelte"
- import PubKeyLogin from "src/views/PubKeyLogin.svelte"
- import NoteDetail from "src/views/NoteDetail.svelte"
- import PersonSettings from "src/views/PersonSettings.svelte"
- import PersonShare from "src/views/PersonShare.svelte"
- import NoteCreate from "src/views/NoteCreate.svelte"
- import ChatEdit from "src/views/ChatEdit.svelte"
- import NotFound from "src/routes/NotFound.svelte"
- import Search from "src/routes/Search.svelte"
- import Alerts from "src/routes/Alerts.svelte"
- import Notes from "src/routes/Notes.svelte"
- import Debug from "src/routes/Debug.svelte"
- import Login from "src/routes/Login.svelte"
- import Logout from "src/routes/Logout.svelte"
- import Profile from "src/routes/Profile.svelte"
- import Settings from "src/routes/Settings.svelte"
- import Keys from "src/routes/Keys.svelte"
- import RelayList from "src/routes/RelayList.svelte"
import AddRelay from "src/routes/AddRelay.svelte"
- import Person from "src/routes/Person.svelte"
+ import Alerts from "src/routes/Alerts.svelte"
import Bech32Entity from "src/routes/Bech32Entity.svelte"
import Chat from "src/routes/Chat.svelte"
import ChatRoom from "src/routes/ChatRoom.svelte"
+ import Debug from "src/routes/Debug.svelte"
+ import Keys from "src/routes/Keys.svelte"
+ import Login from "src/routes/Login.svelte"
+ import Logout from "src/routes/Logout.svelte"
import Messages from "src/routes/Messages.svelte"
+ import NotFound from "src/routes/NotFound.svelte"
+ import Notes from "src/routes/Notes.svelte"
+ import Person from "src/routes/Person.svelte"
+ import Profile from "src/routes/Profile.svelte"
+ import RelayList from "src/routes/RelayList.svelte"
+ import Search from "src/routes/Search.svelte"
+ import Settings from "src/routes/Settings.svelte"
+ import ChatEdit from "src/views/ChatEdit.svelte"
+ import NoteCreate from "src/views/NoteCreate.svelte"
+ import NoteDetail from "src/views/NoteDetail.svelte"
+ import PersonList from "src/views/PersonList.svelte"
+ import PersonSettings from "src/views/PersonSettings.svelte"
+ import PersonShare from "src/views/PersonShare.svelte"
+ import PrivKeyLogin from "src/views/PrivKeyLogin.svelte"
+ import PubKeyLogin from "src/views/PubKeyLogin.svelte"
+ import SignUp from "src/views/SignUp.svelte"
+ import Anchor from 'src/partials/Anchor.svelte'
+ import Content from 'src/partials/Content.svelte'
+ import Modal from 'src/partials/Modal.svelte'
+ import SideNav from 'src/partials/SideNav.svelte'
+ import Spinner from 'src/partials/Spinner.svelte'
+ import TopNav from 'src/partials/TopNav.svelte'
Object.assign(window, {cmd, database, keys, network, pool, sync})
export let url = ""
- const menuIsOpen = writable(false)
- const toggleMenu = () => menuIsOpen.update(x => !x)
+ let scrollY
- const searchIsOpen = writable(false)
- const toggleSearch = () => searchIsOpen.update(x => !x)
+ const {ready} = database
const closeModal = async () => {
modal.clear()
menuIsOpen.set(false)
}
- const {ready} = database
- const {lastCheckedAlerts, mostRecentAlert} = alerts
- const {lastCheckedByPubkey, mostRecentByPubkey} = messages
-
- let menuIcon
- let scrollY
- let suspendedSubs = []
- let slowConnections = []
- let hasNewMessages = false
-
- $: {
- hasNewMessages = Boolean(find(
- ([k, t]) => {
- return t > now() - timedelta(7, 'days') && ($lastCheckedByPubkey[k] || 0) < t
- },
- Object.entries($mostRecentByPubkey)
- ))
- }
-
- database.onReady(() => {
- if ($user) {
- loadAppData($user.pubkey)
- }
-
- // Background work
- const interval = setInterval(() => {
- alertSlowConnections()
- retrieveRelayMeta()
- }, 30_000)
-
- const alertSlowConnections = () => {
- // Only notify about relays the user is actually subscribed to
- const relayUrls = pluck('url', getUserRelays())
-
- // Prune connections we haven't used in a while
- pool.getConnections()
- .filter(conn => conn.lastRequest < Date.now() - 60_000)
- .forEach(conn => conn.disconnect())
-
- // Log stats for debugging purposes
- log(
- 'Connection stats',
- pool.getConnections()
- .map(c => `${c.nostr.url} ${c.getQuality().join(' ')}`)
- )
-
- // Alert the user to any heinously slow connections
- slowConnections = pool.getConnections()
- .filter(c => relayUrls.includes(c.nostr.url) && first(c.getQuality()) < 0.3)
- }
-
- const retrieveRelayMeta = async () => {
- const {dufflepudUrl} = $settings
-
- if (!dufflepudUrl) {
- return
- }
-
- // Find relays with old/missing metadata and refresh them. Only pick a
- // few so we're not sending too many concurrent http requests
- const staleRelays = shuffle(
- await database.relays.all({
- 'refreshed_at:lt': now() - timedelta(7, 'days'),
- })
- ).slice(0, 10)
-
- const freshRelays = await Promise.all(
- staleRelays.map(async ({url}) => {
- try {
- const res = await fetch(dufflepudUrl + '/relay/info', {
- method: 'POST',
- body: JSON.stringify({url}),
- headers: {
- 'Content-Type': 'application/json',
- },
- })
-
- return {...await res.json(), url, refreshed_at: now()}
- } catch (e) {
- if (!e.toString().includes('Failed to fetch')) {
- warn(e)
- }
-
- return {url, refreshed_at: now()}
- }
- })
- )
-
- database.relays.bulkPatch(createMap('url', freshRelays.filter(identity)))
- }
-
- // Close menu on click outside
- document.querySelector("html").addEventListener("click", e => {
- if (e.target !== menuIcon) {
- menuIsOpen.set(false)
- }
- })
-
- // Log usage on navigate
- const unsubHistory = globalHistory.listen(({location}) => {
- if (!location.hash) {
- // Remove identifying information, e.g. pubkeys, event ids, etc
- const name = location.pathname.slice(1)
- .replace(/(npub|nprofile|note|nevent)[^\/]+/g, (_, m) => `<${m}>`)
-
- logUsage(btoa(['page', name].join(':')))
- }
- })
-
+ onMount(() => {
// Keep scroll position on body, but don't allow scrolling
const unsubModal = modal.subscribe($modal => {
if ($modal) {
@@ -196,17 +92,79 @@
}
})
+ // Log usage on navigate
+ const unsubHistory = globalHistory.listen(({location}) => {
+ if (!location.hash) {
+ // Remove identifying information, e.g. pubkeys, event ids, etc
+ const name = location.pathname.slice(1)
+ .replace(/(npub|nprofile|note|nevent)[^\/]+/g, (_, m) => `<${m}>`)
+
+ logUsage(btoa(['page', name].join(':')))
+ }
+ })
+
return () => {
- clearInterval(interval)
unsubHistory()
unsubModal()
}
})
+
+ database.onReady(() => {
+ if ($user) {
+ loadAppData($user.pubkey)
+ }
+
+ const interval = setInterval(
+ async () => {
+ const {dufflepudUrl} = $settings
+
+ if (!dufflepudUrl) {
+ return
+ }
+
+ // Find relays with old/missing metadata and refresh them. Only pick a
+ // few so we're not sending too many concurrent http requests
+ const staleRelays = shuffle(
+ await database.relays.all({
+ 'refreshed_at:lt': now() - timedelta(7, 'days'),
+ })
+ ).slice(0, 10)
+
+ const freshRelays = await Promise.all(
+ staleRelays.map(async ({url}) => {
+ try {
+ const res = await fetch(dufflepudUrl + '/relay/info', {
+ method: 'POST',
+ body: JSON.stringify({url}),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+
+ return {...await res.json(), url, refreshed_at: now()}
+ } catch (e) {
+ if (!e.toString().includes('Failed to fetch')) {
+ warn(e)
+ }
+
+ return {url, refreshed_at: now()}
+ }
+ })
+ )
+
+ database.relays.bulkPatch(createMap('url', freshRelays.filter(identity)))
+ },
+ 30_000
+ )
+
+ return () => {
+ clearInterval(interval)
+ }
+ })
- {#if $ready}
@@ -241,107 +199,9 @@
- {/if}
-
-
-
-
-
-
-
-
- Coracle
-
- {#if $mostRecentAlert > $lastCheckedAlerts || hasNewMessages}
-
- {/if}
-
+
+
{#if $modal}
@@ -409,3 +269,4 @@
{/if}
+
diff --git a/src/Onboarding.svelte b/src/Onboarding.svelte
deleted file mode 100644
index 6c8a7b16..00000000
--- a/src/Onboarding.svelte
+++ /dev/null
@@ -1,2 +0,0 @@
-
-onboarding
diff --git a/src/Shell.svelte b/src/Shell.svelte
deleted file mode 100644
index 33d7c101..00000000
--- a/src/Shell.svelte
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-{#if $showOnboarding}
-
-{:else}
-
-{/if}
diff --git a/src/agent/database.ts b/src/agent/database.ts
index 61dc79f4..26a7c228 100644
--- a/src/agent/database.ts
+++ b/src/agent/database.ts
@@ -242,9 +242,9 @@ const relays = new Table('relays', 'url', {
{url: 'wss://nostr.zebedee.cloud'},
{url: 'wss://nostr-pub.wellorder.net'},
{url: 'wss://relay.nostr.band'},
- {url: 'nostr.pleb.network'},
- {url: 'relay.nostrich.de'},
- {url: 'relay.damus.io'},
+ {url: 'wss://nostr.pleb.network'},
+ {url: 'wss://relay.nostrich.de'},
+ {url: 'wss://relay.damus.io'},
])
return Object.assign(data, defaults)
diff --git a/src/agent/network.ts b/src/agent/network.ts
index 71d0b713..ad25b5c2 100644
--- a/src/agent/network.ts
+++ b/src/agent/network.ts
@@ -4,7 +4,7 @@ import {chunk} from 'hurdak/lib/hurdak'
import {batch, timedelta, now} from 'src/util/misc'
import {
getRelaysForEventParent, getAllPubkeyWriteRelays, aggregateScores,
- getUserNetworkWriteRelays,
+ getUserNetworkWriteRelays, getUserReadRelays,
} from 'src/agent/relays'
import database from 'src/agent/database'
import pool from 'src/agent/pool'
@@ -79,7 +79,7 @@ const loadPeople = (pubkeys, {kinds = personKinds, force = false, ...opts} = {})
}
return load(
- getAllPubkeyWriteRelays(pubkeys).slice(0, 10),
+ getUserReadRelays().concat(getAllPubkeyWriteRelays(pubkeys)).slice(0, 10),
{kinds, authors: pubkeys},
opts
)
diff --git a/src/agent/relays.ts b/src/agent/relays.ts
index 6fd5c446..b9930d25 100644
--- a/src/agent/relays.ts
+++ b/src/agent/relays.ts
@@ -1,8 +1,9 @@
import type {Relay} from 'src/util/types'
-import {writable, get} from 'svelte/store'
+import {get} from 'svelte/store'
import {pick, map, assoc, sortBy, uniq, uniqBy, prop} from 'ramda'
import {first} from 'hurdak/lib/hurdak'
import {Tags} from 'src/util/nostr'
+import {synced} from 'src/util/misc'
import {getFollows} from 'src/agent/social'
import database from 'src/agent/database'
import keys from 'src/agent/keys'
@@ -19,7 +20,7 @@ import keys from 'src/agent/keys'
// doesn't need to see.
// 5) Advertise relays — write and read back your own relay list
-export const relays = writable([])
+export const relays = synced('agent/relays', [])
// Pubkey relays
diff --git a/src/agent/sync.ts b/src/agent/sync.ts
index c20f5be0..d6b8b231 100644
--- a/src/agent/sync.ts
+++ b/src/agent/sync.ts
@@ -1,4 +1,4 @@
-import {pick, identity, isEmpty} from 'ramda'
+import {pick, objOf, identity, isEmpty} from 'ramda'
import {nip05} from 'nostr-tools'
import {noop, createMap, ensurePlural, switcherFn} from 'hurdak/lib/hurdak'
import {log, warn} from 'src/util/logger'
@@ -142,7 +142,7 @@ const calculateRoute = (pubkey, url, type, mode, created_at) => {
}
const processRoutes = async events => {
- const updates = []
+ let updates = []
// Sample events so we're not burning too many resources
for (const e of ensurePlural(shuffle(events)).slice(0, 10)) {
@@ -199,7 +199,10 @@ const processRoutes = async events => {
})
}
+ updates = updates.filter(identity)
+
if (!isEmpty(updates)) {
+ await database.relays.bulkPatch(createMap('url', updates.map(pick(['url']))))
await database.routes.bulkPut(createMap('id', updates.filter(identity)))
}
}
@@ -224,8 +227,12 @@ const verifyNip05 = (pubkey, as) =>
database.people.patch({...person, verified_as: as})
if (result.relays?.length > 0) {
+ const urls = result.relays.filter(isRelay)
+
+ database.relays.bulkPatch(createMap('url', urls.map(objOf('url'))))
+
database.routes.bulkPut(
- createMap('id', result.relays.filter(isRelay).flatMap(url =>[
+ createMap('id', urls.flatMap(url =>[
calculateRoute(pubkey, url, 'nip05', 'write', now()),
calculateRoute(pubkey, url, 'nip05', 'read', now()),
]))
diff --git a/src/app/connection.js b/src/app/connection.js
new file mode 100644
index 00000000..14806e42
--- /dev/null
+++ b/src/app/connection.js
@@ -0,0 +1,31 @@
+import {pluck} from 'ramda'
+import {first} from 'hurdak/lib/hurdak'
+import {log} from 'src/util/logger'
+import {writable} from 'svelte/store'
+import pool from 'src/agent/pool'
+import {getUserRelays} from 'src/agent/relays'
+
+export const slowConnections = writable([])
+
+setInterval(() => {
+ // Only notify about relays the user is actually subscribed to
+ const relayUrls = pluck('url', getUserRelays())
+
+ // Prune connections we haven't used in a while
+ pool.getConnections()
+ .filter(conn => conn.lastRequest < Date.now() - 60_000)
+ .forEach(conn => conn.disconnect())
+
+ // Log stats for debugging purposes
+ log(
+ 'Connection stats',
+ pool.getConnections()
+ .map(c => `${c.nostr.url} ${c.getQuality().join(' ')}`)
+ )
+
+ // Alert the user to any heinously slow connections
+ slowConnections.set(
+ pool.getConnections()
+ .filter(c => relayUrls.includes(c.nostr.url) && first(c.getQuality()) < 0.3)
+ )
+}, 30_000)
diff --git a/src/app/index.ts b/src/app/index.ts
index ee4d1651..1f2eab3e 100644
--- a/src/app/index.ts
+++ b/src/app/index.ts
@@ -7,24 +7,25 @@ import {renderContent} from 'src/util/html'
import {Tags, displayPerson, findReplyId} from 'src/util/nostr'
import {user} from 'src/agent/user'
import {getNetwork} from 'src/agent/social'
-import {relays} from 'src/agent/relays'
+import {relays, getUserReadRelays} from 'src/agent/relays'
import database from 'src/agent/database'
import network from 'src/agent/network'
import keys from 'src/agent/keys'
import cmd from 'src/agent/cmd'
import alerts from 'src/app/alerts'
import messages from 'src/app/messages'
-import {toast, routes, modal, settings, logUsage} from 'src/app/ui'
+import {routes, modal} from 'src/app/ui'
-export {toast, modal, settings, alerts, messages, logUsage}
-
-export const loadAppData = pubkey =>
- Promise.all([
- alerts.load(pubkey),
- alerts.listen(pubkey),
- messages.listen(pubkey),
- network.loadPeople(getNetwork(pubkey)),
- ])
+export const loadAppData = async pubkey => {
+ if (getUserReadRelays().length > 0) {
+ await Promise.all([
+ alerts.load(pubkey),
+ alerts.listen(pubkey),
+ messages.listen(pubkey),
+ network.loadPeople(getNetwork(pubkey)),
+ ])
+ }
+}
export const login = async ({privkey, pubkey}: {privkey?: string, pubkey?: string}) => {
if (privkey) {
@@ -35,31 +36,35 @@ export const login = async ({privkey, pubkey}: {privkey?: string, pubkey?: strin
modal.set({type: 'message', message: "Loading your profile data...", spinner: true})
- // Load our user so we can populate network and show profile info
- await network.loadPeople([pubkey])
+ if (getUserReadRelays().length === 0) {
+ navigate('/relays')
+ } else {
+ // Load our user so we can populate network and show profile info
+ await network.loadPeople([pubkey])
- // Load network and start listening, but don't wait for it
- loadAppData(pubkey)
+ // Load network and start listening, but don't wait for it
+ loadAppData(pubkey)
- // Not ideal, but the network tab depends on the user's social network being
- // loaded, so put them on global when they first log in so we're not slowing
- // down users' first run experience too much
- navigate('/notes/network')
+ // Not ideal, but the network tab depends on the user's social network being
+ // loaded, so put them on global when they first log in so we're not slowing
+ // down users' first run experience too much
+ navigate('/notes/network')
+ }
}
export const addRelay = async url => {
- const person = get(user) as Person
+ const $user = get(user) as Person
relays.update($relays => {
$relays.push({url, write: false, read: true})
- if (person) {
+ if ($user) {
(async () => {
// Publish to the new set of relays
await cmd.setRelays($relays, $relays)
// Reload alerts, messages, etc
- await loadAppData(person.pubkey)
+ await loadAppData($user.pubkey)
})()
}
@@ -68,12 +73,12 @@ export const addRelay = async url => {
}
export const removeRelay = async url => {
- const person = get(user) as Person
+ const $user = get(user) as Person
relays.update($relays => {
$relays = reject(whereEq({url}), $relays)
- if (person && $relays.length > 0) {
+ if ($user && $relays.length > 0) {
cmd.setRelays($relays, $relays)
}
@@ -82,12 +87,12 @@ export const removeRelay = async url => {
}
export const setRelayWriteCondition = async (url, write) => {
- const person = get(user) as Person
+ const $user = get(user) as Person
relays.update($relays => {
$relays = $relays.map(when(whereEq({url}), assoc('write', write)))
- if (person && $relays.length > 0) {
+ if ($user && $relays.length > 0) {
cmd.setRelays($relays, $relays)
}
diff --git a/src/app/messages.js b/src/app/messages.js
index 288044df..d89d583c 100644
--- a/src/app/messages.js
+++ b/src/app/messages.js
@@ -1,5 +1,5 @@
-import {pluck, reject} from 'ramda'
-import {get} from 'svelte/store'
+import {pluck, find, reject} from 'ramda'
+import {get, derived} from 'svelte/store'
import {synced, now, timedelta} from 'src/util/misc'
import {user} from 'src/agent/user'
import {getUserReadRelays} from 'src/agent/relays'
@@ -12,6 +12,18 @@ const since = now() - timedelta(30, 'days')
const mostRecentByPubkey = synced('app/messages/mostRecentByPubkey', {})
const lastCheckedByPubkey = synced('app/messages/lastCheckedByPubkey', {})
+const hasNewMessages = derived(
+ [lastCheckedByPubkey, mostRecentByPubkey],
+ ([$lastCheckedByPubkey, $mostRecentByPubkey]) => {
+ return Boolean(find(
+ ([k, t]) => {
+ return t > now() - timedelta(7, 'days') && ($lastCheckedByPubkey[k] || 0) < t
+ },
+ Object.entries($mostRecentByPubkey)
+ ))
+ }
+)
+
const listen = async pubkey => {
if (listener) {
listener.unsub()
@@ -44,4 +56,4 @@ const listen = async pubkey => {
)
}
-export default {listen, mostRecentByPubkey, lastCheckedByPubkey}
+export default {listen, mostRecentByPubkey, lastCheckedByPubkey, hasNewMessages}
diff --git a/src/app/ui.ts b/src/app/ui.ts
index a4e3441e..25ab732f 100644
--- a/src/app/ui.ts
+++ b/src/app/ui.ts
@@ -35,6 +35,10 @@ toast.show = (type, message, timeout = 5) => {
}, timeout * 1000)
}
+// Menu
+
+export const menuIsOpen = writable(false)
+
// Modals
export const modal = {
@@ -110,5 +114,3 @@ export const logUsage = async name => {
}
}
}
-
-export const showOnboarding = writable(false)
diff --git a/src/main.js b/src/main.js
index 7099d651..d60b0d51 100644
--- a/src/main.js
+++ b/src/main.js
@@ -7,9 +7,9 @@ Bugsnag.start({
collectUserIp: false,
})
-import Shell from 'src/Shell.svelte'
+import App from 'src/App.svelte'
-const app = new Shell({
+const app = new App({
target: document.getElementById('app')
})
diff --git a/src/partials/Like.svelte b/src/partials/Like.svelte
index 76143da6..08a0fbdb 100644
--- a/src/partials/Like.svelte
+++ b/src/partials/Like.svelte
@@ -6,7 +6,7 @@
import {formatTimestamp} from 'src/util/misc'
import {killEvent} from 'src/util/html'
import database from 'src/agent/database'
- import {modal} from 'src/app'
+ import {modal} from 'src/app/ui'
export let note
diff --git a/src/partials/Modal.svelte b/src/partials/Modal.svelte
index 53f6b8e3..4e72d57d 100644
--- a/src/partials/Modal.svelte
+++ b/src/partials/Modal.svelte
@@ -1,7 +1,7 @@
+
+
+
+
+
+
+
diff --git a/src/partials/NewNoteButton.svelte b/src/partials/NewNoteButton.svelte
index 91271732..8cc2ea4e 100644
--- a/src/partials/NewNoteButton.svelte
+++ b/src/partials/NewNoteButton.svelte
@@ -1,6 +1,6 @@
diff --git a/src/partials/Note.svelte b/src/partials/Note.svelte
index fdc6a3e2..191e6776 100644
--- a/src/partials/Note.svelte
+++ b/src/partials/Note.svelte
@@ -12,7 +12,8 @@
import ImageCircle from 'src/partials/ImageCircle.svelte'
import Preview from 'src/partials/Preview.svelte'
import Anchor from 'src/partials/Anchor.svelte'
- import {toast, settings, modal, renderNote} from "src/app"
+ import {toast, settings, modal} from "src/app/ui"
+ import {renderNote} from "src/app"
import {formatTimestamp, stringToColor} from 'src/util/misc'
import Compose from "src/partials/Compose.svelte"
import Card from "src/partials/Card.svelte"
diff --git a/src/partials/Notes.svelte b/src/partials/Notes.svelte
index ef00359e..eaac0527 100644
--- a/src/partials/Notes.svelte
+++ b/src/partials/Notes.svelte
@@ -9,11 +9,13 @@
import Content from 'src/partials/Content.svelte'
import Note from "src/partials/Note.svelte"
import {user} from 'src/agent/user'
+ import {getUserReadRelays} from 'src/agent/relays'
import network from 'src/agent/network'
- import {modal, mergeParents} from "src/app"
+ import {modal} from "src/app/ui"
+ import {mergeParents} from "src/app"
- export let relays
export let filter
+ export let relays = []
export let shouldDisplay = always(true)
let notes = []
@@ -74,6 +76,10 @@
}
onMount(() => {
+ if (relays.length === 0) {
+ relays = getUserReadRelays()
+ }
+
const sub = network.listen(relays, {...filter, since}, onChunk)
const scroller = createScroller(() => {
diff --git a/src/partials/RelayCard.svelte b/src/partials/RelayCard.svelte
index 0efd5b7f..3cf93969 100644
--- a/src/partials/RelayCard.svelte
+++ b/src/partials/RelayCard.svelte
@@ -50,7 +50,8 @@
on:mouseout={() => {showStatus = false}}
on:mouseover={() => {showStatus = true}}
class="w-2 h-2 rounded-full bg-medium cursor-pointer"
- class:bg-danger={quality <= 0.3}
+ class:bg-medium={message === 'Not connected'}
+ class:bg-danger={quality <= 0.3 && message !== 'Not connected'}
class:bg-warning={between(0.3, 0.7, quality)}
class:bg-success={quality > 0.7}>
@@ -79,8 +80,8 @@
Publish to this relay?
setRelayWriteCondition(relay.url, relay.write === "!" ? "" : "!")} />
+ value={relay.write}
+ on:change={() => setRelayWriteCondition(relay.url, !relay.write)} />
{/if}
diff --git a/src/partials/Room.svelte b/src/partials/Room.svelte
index 226b592a..e53ebaab 100644
--- a/src/partials/Room.svelte
+++ b/src/partials/Room.svelte
@@ -5,7 +5,7 @@
import Anchor from 'src/partials/Anchor.svelte'
import {displayPerson} from 'src/util/nostr'
import {now, timedelta} from 'src/util/misc'
- import {messages} from 'src/app'
+ import messages from 'src/app/messages'
export let joined = false
export let room
diff --git a/src/partials/SideNav.svelte b/src/partials/SideNav.svelte
new file mode 100644
index 00000000..2ff95cab
--- /dev/null
+++ b/src/partials/SideNav.svelte
@@ -0,0 +1,96 @@
+
+
+
+
diff --git a/src/partials/TopNav.svelte b/src/partials/TopNav.svelte
new file mode 100644
index 00000000..947d08e9
--- /dev/null
+++ b/src/partials/TopNav.svelte
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+ Coracle
+
+ {#if $mostRecentAlert > $lastCheckedAlerts || $hasNewMessages}
+
+ {/if}
+
diff --git a/src/routes/AddRelay.svelte b/src/routes/AddRelay.svelte
index f9c0b1ad..61282b96 100644
--- a/src/routes/AddRelay.svelte
+++ b/src/routes/AddRelay.svelte
@@ -1,6 +1,7 @@
@@ -33,3 +42,9 @@
+
+{#if $relays.length === 0}
+
+
+
+{/if}
diff --git a/src/routes/Person.svelte b/src/routes/Person.svelte
index 0a4816d9..11a013c0 100644
--- a/src/routes/Person.svelte
+++ b/src/routes/Person.svelte
@@ -21,8 +21,7 @@
import network from "src/agent/network"
import keys from "src/agent/keys"
import database from "src/agent/database"
- import {routes} from "src/app/ui"
- import {modal} from "src/app"
+ import {routes, modal} from "src/app/ui"
export let npub
export let activeTab
diff --git a/src/routes/Profile.svelte b/src/routes/Profile.svelte
index 93bc9880..efca2df4 100644
--- a/src/routes/Profile.svelte
+++ b/src/routes/Profile.svelte
@@ -14,8 +14,7 @@
import {user} from "src/agent/user"
import {getUserWriteRelays} from 'src/agent/relays'
import cmd from "src/agent/cmd"
- import {toast} from "src/app"
- import {routes} from "src/app/ui"
+ import {routes, toast} from "src/app/ui"
let values = {picture: null, about: null, name: null, nip05: null}
diff --git a/src/routes/RelayList.svelte b/src/routes/RelayList.svelte
index 72c3f838..856b7b06 100644
--- a/src/routes/RelayList.svelte
+++ b/src/routes/RelayList.svelte
@@ -2,13 +2,14 @@
import {pluck} from 'ramda'
import {fly} from 'svelte/transition'
import {fuzzy} from "src/util/misc"
+ import {isRelay} from "src/util/nostr"
import Input from "src/partials/Input.svelte"
import Anchor from "src/partials/Anchor.svelte"
import Content from "src/partials/Content.svelte"
import RelayCard from "src/partials/RelayCard.svelte"
import {relays} from "src/agent/relays"
import database from 'src/agent/database'
- import {modal} from "src/app"
+ import {modal} from "src/app/ui"
let q = ""
let search
@@ -18,55 +19,60 @@
const joined = new Set(pluck('url', $relays))
search = fuzzy(
- $knownRelays.filter(r => !joined.has(r.url)),
+ $knownRelays.filter(r => isRelay(r.url) && !joined.has(r.url)),
{keys: ["name", "description", "url"]}
)
}
-
-
-
-
-
Your relays
-
-
modal.set({type: 'relay/add', url: q})}>
- Add Relay
-
-
-
- Relays are hubs for your content and connections. At least one is required to
- interact with the network, but you can join as many as you like.
-
- {#if $relays.length === 0}
- No relays connected
- {/if}
-
- {#each $relays as relay (relay.url)}
-
- {/each}
-
-
- {#if ($knownRelays || []).length > 0}
-
-
-
-
Other relays
+
+
+
+
+
+
Your relays
+
+
modal.set({type: 'relay/add', url: q})}>
+ Add Relay
+
- Coracle automatically discovers relays as you browse the network. Adding more relays
- will generally make things quicker to load, at the expense of higher data usage.
+ Relays are hubs for your content and connections. At least one is required to
+ interact with the network, but you can join as many as you like.
-
-
-
+ {#if $relays.length === 0}
+
+
+ No relays connected
+
{/if}
- {#each (search(q) || []).slice(0, 50) as relay (relay.url)}
-
- {/each}
-
- Showing {Math.min(($knownRelays || []).length - $relays.length, 50)}
- of {($knownRelays || []).length - $relays.length} known relays
-
-
-
+
+ {#each $relays as relay (relay.url)}
+
+ {/each}
+
+
+ {#if ($knownRelays || []).length > 0}
+
+
+
+
Other relays
+
+
+ Coracle automatically discovers relays as you browse the network. Adding more relays
+ will generally make things quicker to load, at the expense of higher data usage.
+
+
+
+
+ {/if}
+ {#each (search(q) || []).slice(0, 50) as relay (relay.url)}
+
+ {/each}
+
+ Showing {Math.min(($knownRelays || []).length - $relays.length, 50)}
+ of {($knownRelays || []).length - $relays.length} known relays
+
+
+
+
diff --git a/src/routes/Search.svelte b/src/routes/Search.svelte
index c8e65d1e..1ccd8e18 100644
--- a/src/routes/Search.svelte
+++ b/src/routes/Search.svelte
@@ -2,8 +2,11 @@
import {navigate} from 'svelte-routing'
import Content from 'src/partials/Content.svelte'
import Tabs from 'src/partials/Tabs.svelte'
+ import Modal from 'src/partials/Modal.svelte'
import SearchPeople from 'src/views/SearchPeople.svelte'
+ import RelayList from "src/routes/RelayList.svelte"
import Scan from 'src/views/Scan.svelte'
+ import {relays} from 'src/agent/relays'
export let activeTab
@@ -18,3 +21,9 @@
{/if}
+
+{#if $relays.length === 0}
+
+
+
+{/if}
diff --git a/src/routes/Settings.svelte b/src/routes/Settings.svelte
index 43e1cca7..8305a21c 100644
--- a/src/routes/Settings.svelte
+++ b/src/routes/Settings.svelte
@@ -8,7 +8,7 @@
import Content from "src/partials/Content.svelte"
import Heading from "src/partials/Heading.svelte"
import {user} from 'src/agent/user'
- import {toast, settings} from "src/app"
+ import {toast, settings} from "src/app/ui"
let values = {...$settings}
diff --git a/src/views/ChatEdit.svelte b/src/views/ChatEdit.svelte
index f42d8fa3..5bd5dd44 100644
--- a/src/views/ChatEdit.svelte
+++ b/src/views/ChatEdit.svelte
@@ -10,7 +10,7 @@
import {getUserWriteRelays} from 'src/agent/relays'
import database from 'src/agent/database'
import cmd from "src/agent/cmd"
- import {toast, modal} from "src/app"
+ import {toast, modal} from "src/app/ui"
export let room = {name: null, id: null, about: null, picture: null}
diff --git a/src/views/NoteCreate.svelte b/src/views/NoteCreate.svelte
index 64890497..7f1b5688 100644
--- a/src/views/NoteCreate.svelte
+++ b/src/views/NoteCreate.svelte
@@ -6,6 +6,7 @@
import {fly} from 'svelte/transition'
import {navigate} from "svelte-routing"
import {fuzzy} from "src/util/misc"
+ import {isRelay} from "src/util/nostr"
import Button from "src/partials/Button.svelte"
import Compose from "src/partials/Compose.svelte"
import Input from "src/partials/Input.svelte"
@@ -17,7 +18,7 @@
import {getUserWriteRelays} from 'src/agent/relays'
import database from 'src/agent/database'
import cmd from "src/agent/cmd"
- import {toast, modal} from "src/app"
+ import {toast, modal} from "src/app/ui"
export let pubkey = null
@@ -33,7 +34,7 @@
const joined = new Set(pluck('url', relays))
search = fuzzy(
- $knownRelays.filter(r => !joined.has(r.url)),
+ $knownRelays.filter(r => isRelay(r.url) && !joined.has(r.url)),
{keys: ["name", "description", "url"]}
)
}
diff --git a/src/views/PersonSettings.svelte b/src/views/PersonSettings.svelte
index 5c90672d..59b48b30 100644
--- a/src/views/PersonSettings.svelte
+++ b/src/views/PersonSettings.svelte
@@ -8,7 +8,7 @@
import {user} from 'src/agent/user'
import {getUserWriteRelays} from 'src/agent/relays'
import cmd from 'src/agent/cmd'
- import {modal} from 'src/app'
+ import {modal} from 'src/app/ui'
const muffle = $user.muffle || []
const muffleOptions = ['Never', 'Sometimes', 'Often', 'Always']
diff --git a/src/views/PersonShare.svelte b/src/views/PersonShare.svelte
index 3761eacf..d84b87fc 100644
--- a/src/views/PersonShare.svelte
+++ b/src/views/PersonShare.svelte
@@ -7,7 +7,7 @@
import Content from 'src/partials/Content.svelte'
import Input from 'src/partials/Input.svelte'
import {getPubkeyWriteRelays} from 'src/agent/relays'
- import {modal, toast} from 'src/app'
+ import {modal, toast} from 'src/app/ui'
const {pubkey} = $modal.person
const relays = [prop('url', getPubkeyWriteRelays(pubkey))]
diff --git a/src/views/PrivKeyLogin.svelte b/src/views/PrivKeyLogin.svelte
index 4c02ca67..99bde161 100644
--- a/src/views/PrivKeyLogin.svelte
+++ b/src/views/PrivKeyLogin.svelte
@@ -4,7 +4,8 @@
import Anchor from 'src/partials/Anchor.svelte'
import Content from 'src/partials/Content.svelte'
import Heading from 'src/partials/Heading.svelte'
- import {toast, login} from "src/app"
+ import {toast} from "src/app/ui"
+ import {login} from "src/app"
let nsec = ''
const nip07 = "https://github.com/nostr-protocol/nips/blob/master/07.md"
diff --git a/src/views/PubKeyLogin.svelte b/src/views/PubKeyLogin.svelte
index 304446f8..74f5cfd4 100644
--- a/src/views/PubKeyLogin.svelte
+++ b/src/views/PubKeyLogin.svelte
@@ -4,7 +4,8 @@
import Anchor from 'src/partials/Anchor.svelte'
import Content from 'src/partials/Content.svelte'
import Heading from 'src/partials/Heading.svelte'
- import {toast, login} from "src/app"
+ import {toast} from "src/app/ui"
+ import {login} from "src/app"
let npub = ''
diff --git a/src/views/SearchPeople.svelte b/src/views/SearchPeople.svelte
index 4d9036c9..8a88314f 100644
--- a/src/views/SearchPeople.svelte
+++ b/src/views/SearchPeople.svelte
@@ -1,4 +1,5 @@
-
+
diff --git a/src/views/notes/Top.svelte b/src/views/notes/Top.svelte
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/src/views/notes/Top.svelte
@@ -0,0 +1 @@
+