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 Logo -

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 @@ + + +
    +