mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-29 08:21:20 +00:00
Fixing some bugs
This commit is contained in:
parent
5c4432e6a4
commit
e9c4ba44fe
2
.env
2
.env
@ -1,6 +1,6 @@
|
||||
VITE_THEME=transparent:transparent,black:#0f0f0e,white:#FFFFFF,accent:#EB5E28,accent-light:#FB652C,gray-dark:#8D8581,gray-light:#A8A5A4,danger:#ff0000,warning:#ebd112,success:#37ab51,input:#FAF6F1,input-hover:#F2EBE1
|
||||
VITE_DEFAULT_FOLLOWS=1739d937dc8c0c7370aa27585938c119e25c41f6c441a5d34c6d38503e3136ef,fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52,cc8d072efdcc676fcbac14f6cd6825edc3576e55eb786a2a975ee034a6a026cb,d91191e30e00444b942c0e82cad470b32af171764c2275bee0bd99377efd4075,3335d373e6c1b5bc669b4b1220c08728ea8ce622e5a7cfeeb4c0001d91ded1de,0b118e40d6f3dfabb17f21a94a647701f140d8b063a9e84fe6e483644edc09cb,b83a28b7e4e5d20bd960c5faeb6625f95529166b8bdb045d42634a2f35919450,958b754a1d3de5b5eca0fe31d2d555f451325f8498a83da1997b7fcd5c39e88c,a4cb51f4618cfcd16b2d3171c466179bed8e197c43b8598823b04de266cef110,e56e7b4326618f3d626c0e398f5082c3b16732e469e0a048b7ddb544c2be294a,011c1b374c12fbd3633e98957d3c46bed67983abecef50706c73a77c171d0d2c,b9e76546ba06456ed301d9e52bc49fa48e70a6bf2282be7a1ae72947612023dc,b708f7392f588406212c3882e7b3bc0d9b08d62f95fa170d099127ece2770e5e,5c508c34f58866ec7341aaf10cc1af52e9232bb9f859c8103ca5ecf2aa93bf78,baf27a4cc4da49913e7fdecc951fd3b971c9279959af62b02b761a043c33384c,2edbcea694d164629854a52583458fd6d965b161e3c48b57d3aff01940558884,0fecf65daa26faf3f668e8143325a4c199a040b6345ed40a08614d7dd85b1823,1bc70a0148b3f316da33fe3c89f23e3e71ac4ff998027ec712b905cd24f6a411,f783ba3b12b91e375aba6594015b90bd95f7e132b03cc8c4c52ce0a7c36aab52,3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24,82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2,3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d,ee11a5dff40c19a555f41fe42b48f00e618c91225622ae37b6c2bb67b76c4e49,97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322
|
||||
VITE_IMGPROXY_URL=https://imgproxy-cbpd.onrender.com
|
||||
VITE_IMGPROXY_URL=https://imgproxy.coracle.social
|
||||
VITE_DUFFLEPUD_URL=https://dufflepud.onrender.com
|
||||
VITE_ENABLE_ZAPS=true
|
||||
VITE_FORCE_RELAYS=
|
||||
|
@ -1,5 +1,7 @@
|
||||
# Current
|
||||
|
||||
- [ ] Self profile detail in drop down
|
||||
- [ ] close menu on click
|
||||
- [ ] Fork and white label blowater
|
||||
- [ ] Add relayset support with kind 30022
|
||||
- [ ] White-labeled
|
||||
|
@ -103,12 +103,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
let scrollY
|
||||
let scrollY
|
||||
|
||||
onMount(() => {
|
||||
// Log modals, keep scroll position on body, but don't allow scrolling
|
||||
const unsubModal = modal.stack.subscribe($stack => {
|
||||
console.log("=======", $stack, scrollY)
|
||||
if (find(x => !x.mini, $stack)) {
|
||||
logUsage(btoa(["modal", last($stack).type].join(":")))
|
||||
|
||||
|
@ -8,7 +8,6 @@
|
||||
let scrollY = 0
|
||||
|
||||
$: showCreateNote = !$location.pathname.match(/messages.|chat.|relays.|keys|settings|logout$/)
|
||||
$: showLogin = !$location.pathname.match(/login$/)
|
||||
|
||||
const {canSign} = Keys
|
||||
|
||||
@ -44,13 +43,4 @@
|
||||
<i class="fa fa-plus" />
|
||||
</button>
|
||||
{/if}
|
||||
{#if !$canSign && showLogin}
|
||||
<button
|
||||
class="color-white flex h-16 w-16 items-center justify-center rounded-full
|
||||
border border-accent-light bg-accent text-white shadow-2xl
|
||||
transition-all hover:scale-105 hover:bg-accent-light"
|
||||
on:click={() => navigate("/login")}>
|
||||
<i class="fa fa-right-to-bracket" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -3,6 +3,7 @@
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Spinner from "src/partials/Spinner.svelte"
|
||||
import ChatEdit from "src/app/views/ChatEdit.svelte"
|
||||
import Login from "src/app/views/Login.svelte"
|
||||
import LoginConnect from "src/app/views/LoginConnect.svelte"
|
||||
import LoginPrivKey from "src/app/views/LoginPrivKey.svelte"
|
||||
import LoginAdvanced from "src/app/views/LoginAdvanced.svelte"
|
||||
@ -54,6 +55,8 @@
|
||||
<Onboarding stage={m.stage} />
|
||||
{:else if m.type === "channel/edit"}
|
||||
<ChatEdit {...m} />
|
||||
{:else if m.type === "login/intro"}
|
||||
<Login />
|
||||
{:else if m.type === "login/privkey"}
|
||||
<LoginPrivKey />
|
||||
{:else if m.type === "login/advanced"}
|
||||
|
@ -31,7 +31,7 @@
|
||||
</script>
|
||||
|
||||
{#if ready}
|
||||
<div class="pt-16 text-gray-2 lg:ml-56">
|
||||
<div class="pt-16 text-gray-2 lg:ml-48">
|
||||
<TypedRoute path="/notifications" component={Notifications} />
|
||||
<TypedRoute path="/notifications/:activeTab" component={Notifications} />
|
||||
<TypedRoute path="/notes" let:params>
|
||||
@ -69,7 +69,6 @@
|
||||
</TypedRoute>
|
||||
<TypedRoute path="/profile" component={UserProfile} />
|
||||
<TypedRoute path="/settings" component={UserSettings} />
|
||||
<TypedRoute path="/login" component={Login} />
|
||||
<TypedRoute path="/logout" component={Logout} />
|
||||
<TypedRoute path="/:entity" let:params>
|
||||
{#key params.entity}
|
||||
|
@ -28,7 +28,7 @@
|
||||
<ul
|
||||
class="fixed bottom-0 left-0 top-0 z-20 mt-16 w-48 overflow-hidden border-r border-gray-6 bg-gray-7 pb-20
|
||||
pt-4 text-gray-2 shadow-xl transition-all lg:ml-0"
|
||||
class:-ml-56={!$menuIsOpen}>
|
||||
class:-ml-48={!$menuIsOpen}>
|
||||
<li class="cursor-pointer">
|
||||
<a class="block px-4 py-2 transition-all hover:bg-accent hover:text-white" href="/notes">
|
||||
<i class="fa fa-rss mr-2" /> Feed
|
||||
|
@ -34,6 +34,8 @@
|
||||
let scanner
|
||||
let searchInput
|
||||
|
||||
const showLogin = () => modal.push({type: 'login/intro'})
|
||||
|
||||
const showSearch = () => {
|
||||
term = ""
|
||||
searchInput.focus()
|
||||
@ -173,7 +175,7 @@
|
||||
<div slot="trigger" class="relative flex cursor-pointer items-center">
|
||||
<PersonCircle size={10} pubkey={$pubkey} />
|
||||
{#if $hasNewNotfications}
|
||||
<div class="absolute right-0 h-[9px] w-[9px] rounded bg-accent" />
|
||||
<div class="absolute right-0 top-0 h-[9px] w-[9px] rounded bg-accent" />
|
||||
{/if}
|
||||
</div>
|
||||
<div slot="tooltip" class="flex justify-end">
|
||||
@ -217,16 +219,19 @@
|
||||
</div>
|
||||
</Popover>
|
||||
{:else}
|
||||
<Anchor theme="button-primary" href="/login">Log In</Anchor>
|
||||
<Anchor theme="button-accent" on:click={showLogin}>Log In</Anchor>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class={cx(
|
||||
"search-input pointer-events-none fixed top-0 z-10 w-full px-2 text-white sm:pr-16",
|
||||
"search-input pointer-events-none fixed top-0 z-10 w-full px-2 text-gray-1",
|
||||
"flex h-16 items-center justify-end gap-4",
|
||||
{
|
||||
"pr-16": term === null,
|
||||
"sm:pr-16": $pubkey,
|
||||
"sm:pr-28": !$pubkey,
|
||||
"pr-16": term === null && $pubkey,
|
||||
"pr-28": term === null && !$pubkey,
|
||||
"z-40 pr-0": term,
|
||||
}
|
||||
)}>
|
||||
@ -247,7 +252,7 @@
|
||||
"pointer-events-auto cursor-pointer text-black transition-all",
|
||||
{
|
||||
"-mr-6 w-0 opacity-0": term === null,
|
||||
"opacity-1 -ml-12 sm:-mr-1 sm:w-64 w-full pl-10": term !== null,
|
||||
"opacity-1 -ml-12 w-full pl-10 sm:-mr-1 sm:w-64": term !== null,
|
||||
}
|
||||
)} />
|
||||
<div
|
||||
|
@ -22,7 +22,7 @@
|
||||
<NoteContentKind0 {note} />
|
||||
{:else if note.kind === 3}
|
||||
<NoteContentKind3 {note} {showEntire} />
|
||||
{:else if note.kind === 40}
|
||||
{:else if [40, 41].includes(note.kind)}
|
||||
<NoteContentKind40 {note} />
|
||||
{:else if note.kind === 1985}
|
||||
<NoteContentKind1985 {note} {anchorId} {maxLength} {showEntire} />
|
||||
|
@ -3,6 +3,7 @@
|
||||
import {navigate} from "svelte-routing"
|
||||
import {nip19} from "nostr-tools"
|
||||
import {tryJson} from "src/util/misc"
|
||||
import {Tags} from "src/util/nostr"
|
||||
import Card from "src/partials/Card.svelte"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import ImageCircle from "src/partials/ImageCircle.svelte"
|
||||
@ -14,7 +15,7 @@
|
||||
const channel = Nip28.channels
|
||||
.key(note.id)
|
||||
.derived(defaultTo({id: note.id, name, picture, about}))
|
||||
const noteId = nip19.noteEncode(note.id)
|
||||
const noteId = nip19.noteEncode(note.kind === 40 ? note.id : Tags.from(note).getMeta("e"))
|
||||
</script>
|
||||
|
||||
<Card interactive invertColors on:click={() => navigate(`/chat/${noteId}`)}>
|
||||
|
@ -101,7 +101,7 @@
|
||||
<div class="flex gap-2 rounded-b bg-gray-7 p-2 text-sm text-gray-2">
|
||||
<div class="inline-block border-r border-solid border-gray-6 py-2 pl-1 pr-3">
|
||||
<div class="flex cursor-pointer items-center gap-3">
|
||||
<ImageInput bind:value={data.image} icon="image" hideInput>
|
||||
<ImageInput bind:value={data.image}>
|
||||
<i slot="button" class="fa fa-paperclip" />
|
||||
</ImageInput>
|
||||
<i class="fa fa-at" />
|
||||
|
@ -66,7 +66,7 @@
|
||||
</script>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
{#if canSign && !isSelf}
|
||||
{#if !isSelf}
|
||||
<Popover triggerType="mouseenter">
|
||||
<div slot="trigger" class="w-6 text-center">
|
||||
{#if $muted}
|
||||
|
@ -24,7 +24,9 @@
|
||||
</div>
|
||||
</Anchor>
|
||||
{#if !hideActions}
|
||||
<PersonActions {pubkey} />
|
||||
<slot name="actions">
|
||||
<PersonActions {pubkey} />
|
||||
</slot>
|
||||
{/if}
|
||||
</div>
|
||||
<PersonAbout truncate {pubkey} />
|
||||
|
@ -1,11 +1,9 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {fly} from "src/util/transition"
|
||||
import {error} from "src/util/logger"
|
||||
import {stripExifData} from "src/util/html"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Textarea from "src/partials/Textarea.svelte"
|
||||
import ImageInput from "src/partials/ImageInput.svelte"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import {toast, modal} from "src/partials/state"
|
||||
import {Builder, user} from "src/app/engine"
|
||||
@ -13,22 +11,6 @@
|
||||
|
||||
export let channel = {name: null, id: null, about: null, picture: null}
|
||||
|
||||
onMount(async () => {
|
||||
document.querySelector("[name=picture]").addEventListener("change", async e => {
|
||||
const target = e.target as HTMLInputElement
|
||||
const [file] = target.files
|
||||
|
||||
if (file) {
|
||||
const reader = new FileReader()
|
||||
reader.onerror = error
|
||||
reader.onload = () => (channel.picture = reader.result)
|
||||
reader.readAsDataURL(await stripExifData(file))
|
||||
} else {
|
||||
channel.picture = null
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const submit = async e => {
|
||||
e.preventDefault()
|
||||
|
||||
@ -73,7 +55,7 @@
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<strong>Picture</strong>
|
||||
<input type="file" name="picture" />
|
||||
<ImageInput icon="image" bind:value={channel.picture} />
|
||||
<p class="text-sm text-gray-1">A picture to help people remember your room.</p>
|
||||
</div>
|
||||
<Anchor tag="button" theme="button" type="submit" class="text-center">Done</Anchor>
|
||||
|
@ -3,17 +3,24 @@
|
||||
import {debounce} from "throttle-debounce"
|
||||
import {batch, seconds} from "hurdak"
|
||||
import {complement, propEq, sortBy, pipe, pluck, filter, uniq, prop} from "ramda"
|
||||
import {now} from "src/util/misc"
|
||||
import {now, createScroller} from "src/util/misc"
|
||||
import {Tags} from "src/util/nostr"
|
||||
import {modal} from "src/partials/state"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import ChatListItem from "src/app/views/ChatListItem.svelte"
|
||||
import {pubkeyLoader, Nip28, Nip65, Network, Keys} from "src/app/engine"
|
||||
import {pubkeyLoader, Nip28, Nip65, Network, Keys, user} from "src/app/engine"
|
||||
|
||||
let q = ""
|
||||
let results = []
|
||||
let limit = 5
|
||||
|
||||
const loadMore = () => {
|
||||
limit += 5
|
||||
}
|
||||
|
||||
const scroller = createScroller(loadMore)
|
||||
|
||||
const channels = Nip28.channels.derived(
|
||||
pipe(
|
||||
@ -21,6 +28,7 @@
|
||||
sortBy(c => -(c.last_sent || c.last_received))
|
||||
)
|
||||
)
|
||||
|
||||
const joined = channels.derived(filter(prop("joined")))
|
||||
const other = channels.derived(filter(complement(prop("joined"))))
|
||||
const search = Nip28.getSearchChannels(other)
|
||||
@ -36,7 +44,7 @@
|
||||
})
|
||||
|
||||
$: searchChannels(q)
|
||||
$: results = $search(q).slice(0, 50)
|
||||
$: results = $search(q).slice(0, limit)
|
||||
|
||||
document.title = "Chat"
|
||||
|
||||
@ -51,7 +59,13 @@
|
||||
timeout: 2000,
|
||||
filter: [
|
||||
{kinds: [40, 41], authors: [Keys.pubkey.get()]},
|
||||
{kinds: [42], since: now() - seconds(1, "day"), limit: 100},
|
||||
{kinds: [40, 41], authors: user.getFollows().slice(0, 256), limit: 100},
|
||||
{
|
||||
limit: 100,
|
||||
kinds: [42],
|
||||
since: now() - seconds(1, "day"),
|
||||
authors: user.getFollows().slice(0, 256),
|
||||
},
|
||||
],
|
||||
onEvent: batch(500, events => {
|
||||
const channelIds = uniq(
|
||||
@ -76,6 +90,7 @@
|
||||
|
||||
return () => {
|
||||
subs.map(s => s.close())
|
||||
scroller.stop()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@ -107,14 +122,9 @@
|
||||
<i slot="before" class="fa-solid fa-search" />
|
||||
</Input>
|
||||
</div>
|
||||
{#if results.length > 0}
|
||||
{#each results as channel (channel.id)}
|
||||
<ChatListItem {channel} />
|
||||
{/each}
|
||||
<small class="text-center">
|
||||
Showing {Math.min(50, $other.length)} of {$other.length} known rooms
|
||||
</small>
|
||||
{#each results as channel (channel.id)}
|
||||
<ChatListItem {channel} />
|
||||
{:else}
|
||||
<small class="text-center"> No matching rooms found </small>
|
||||
{/if}
|
||||
{/each}
|
||||
</Content>
|
||||
|
@ -11,7 +11,12 @@
|
||||
const enter = () => navigate(`/chat/${nip19.noteEncode(channel.id)}`)
|
||||
const join = () => user.joinChannel(channel.id)
|
||||
const leave = () => user.leaveChannel(channel.id)
|
||||
const picture = Settings.imgproxy(channel.picture, {w: 112, h: 112})
|
||||
|
||||
// Accommodate data urls from legacy
|
||||
const picture =
|
||||
channel.picture?.length > 500
|
||||
? channel.picture
|
||||
: Settings.imgproxy(channel.picture, {w: 112, h: 112})
|
||||
</script>
|
||||
|
||||
<button
|
||||
|
@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import cx from "classnames"
|
||||
import type {DynamicFilter} from "src/engine/types"
|
||||
import {objOf} from "ramda"
|
||||
import {Tags, noteKinds} from "src/util/nostr"
|
||||
import {modal, theme} from "src/partials/state"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
@ -19,9 +18,9 @@
|
||||
authors: user.getFollows().length > 0 ? "follows" : "network",
|
||||
} as DynamicFilter
|
||||
|
||||
const showLists = () => {
|
||||
modal.push({type: "list/list"})
|
||||
}
|
||||
const showLists = () => modal.push({type: "list/list"})
|
||||
|
||||
const showLogin = () => modal.push({type: 'login/intro'})
|
||||
|
||||
const loadListFeed = naddr => {
|
||||
const list = engine.Content.lists.key(naddr).get()
|
||||
@ -30,7 +29,7 @@
|
||||
const urls = Tags.wrap(list.tags).urls()
|
||||
|
||||
if (urls.length > 0) {
|
||||
relays = urls.map(objOf("url"))
|
||||
relays = urls
|
||||
}
|
||||
|
||||
filter = {kinds: [1, 1985], authors: "global"} as DynamicFilter
|
||||
@ -54,7 +53,7 @@
|
||||
<Content size="lg" class="text-center">
|
||||
<p class="text-xl">Don't have an account?</p>
|
||||
<p>
|
||||
Click <Anchor class="underline" href="/login">here</Anchor> to join the nostr network.
|
||||
Click <Anchor class="underline" on:click={showLogin}>here</Anchor> to join the nostr network.
|
||||
</p>
|
||||
</Content>
|
||||
{/if}
|
||||
|
@ -15,7 +15,7 @@
|
||||
// Give them a moment to see the state transition. IndexedDB
|
||||
// also apparently needs some time
|
||||
setTimeout(() => {
|
||||
window.location.href = "/login"
|
||||
window.location.href = "/notes"
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
|
@ -140,7 +140,7 @@
|
||||
<div class="flex gap-2">
|
||||
<Anchor tag="button" theme="button" type="submit" class="flex-grow text-center"
|
||||
>Send</Anchor>
|
||||
<ImageInput multi onChange={addImage} icon="image" hideInput />
|
||||
<ImageInput multi onChange={addImage} />
|
||||
</div>
|
||||
<small
|
||||
class="flex cursor-pointer items-center justify-end gap-1"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import {reject} from "ramda"
|
||||
import {reject, prop} from "ramda"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Heading from "src/partials/Heading.svelte"
|
||||
@ -10,15 +10,15 @@
|
||||
import {modal} from "src/partials/state"
|
||||
|
||||
const {searchProfiles} = Directory
|
||||
const follows = Nip02.graph.derived(() => user.getFollowsSet())
|
||||
const follows = Nip02.graph.key(user.getStateKey()).derived(g => g ? g.petnames.map(t => t[1]) : [])
|
||||
|
||||
if ($follows.size === 0) {
|
||||
if ($follows.length === 0) {
|
||||
user.setPetnames(Env.DEFAULT_FOLLOWS.map(Builder.mention))
|
||||
}
|
||||
|
||||
let q = ""
|
||||
|
||||
$: results = reject((p: Profile) => $follows.has(p.pubkey), $searchProfiles(q))
|
||||
$: results = reject((p: Profile) => $follows.includes(p.pubkey), $searchProfiles(q))
|
||||
</script>
|
||||
|
||||
<Content>
|
||||
@ -38,14 +38,20 @@
|
||||
<i class="fa fa-user-astronaut fa-lg" />
|
||||
<h2 class="staatliches text-2xl">Your follows</h2>
|
||||
</div>
|
||||
{#if $follows.size === 0}
|
||||
{#if $follows.length === 0}
|
||||
<div class="mt-8 flex items-center justify-center gap-2 text-center">
|
||||
<i class="fa fa-triangle-exclamation" />
|
||||
<span>No follows selected</span>
|
||||
</div>
|
||||
{:else}
|
||||
{#each Array.from($follows) as pubkey}
|
||||
<PersonSummary {pubkey} />
|
||||
{#each $follows as pubkey (pubkey)}
|
||||
<PersonSummary {pubkey}>
|
||||
<div slot="actions">
|
||||
<Anchor theme="button" class="flex items-center gap-2" on:click={() => user.unfollow(pubkey)}>
|
||||
<i class="fa fa-user-slash" /> Unfollow
|
||||
</Anchor>
|
||||
</div>
|
||||
</PersonSummary>
|
||||
{/each}
|
||||
{/if}
|
||||
<div class="flex items-center gap-2">
|
||||
@ -56,6 +62,12 @@
|
||||
<i slot="before" class="fa-solid fa-search" />
|
||||
</Input>
|
||||
{#each results.slice(0, 50) as profile (profile.pubkey)}
|
||||
<PersonSummary pubkey={profile.pubkey} />
|
||||
<PersonSummary pubkey={profile.pubkey}>
|
||||
<div slot="actions">
|
||||
<Anchor theme="button-accent" class="flex items-center gap-2" on:click={() => user.follow(profile.pubkey)}>
|
||||
<i class="fa fa-user-plus" /> Follow
|
||||
</Anchor>
|
||||
</div>
|
||||
</PersonSummary>
|
||||
{/each}
|
||||
</Content>
|
||||
|
@ -1,5 +1,4 @@
|
||||
<script>
|
||||
import {onMount} from "svelte"
|
||||
import {fly} from "src/util/transition"
|
||||
import {navigate} from "svelte-routing"
|
||||
import {nip19} from "nostr-tools"
|
||||
@ -26,12 +25,6 @@
|
||||
toast.show("info", `Your ${type} key has been copied to the clipboard.`)
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
if (!$pubkey) {
|
||||
return navigate("/login")
|
||||
}
|
||||
})
|
||||
|
||||
document.title = "Keys"
|
||||
</script>
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {fly} from "src/util/transition"
|
||||
import {navigate} from "svelte-routing"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
@ -17,12 +16,6 @@
|
||||
const pseudUrl =
|
||||
"https://www.coindesk.com/markets/2020/06/29/many-bitcoin-developers-are-choosing-to-use-pseudonyms-for-good-reason/"
|
||||
|
||||
onMount(async () => {
|
||||
if (!Keys.canSign.get()) {
|
||||
return navigate("/login")
|
||||
}
|
||||
})
|
||||
|
||||
const submit = async event => {
|
||||
const relays = user.getRelayUrls("write")
|
||||
|
||||
|
@ -3,16 +3,14 @@ import {nth, inc} from "ramda"
|
||||
import {fuzzy} from "src/util/misc"
|
||||
import {Tags} from "src/util/nostr"
|
||||
import type {Topic, List} from "src/engine/types"
|
||||
import {derived, collection} from "src/engine/util/store"
|
||||
import {collection} from "src/engine/util/store"
|
||||
import type {Engine} from "src/engine/Engine"
|
||||
import type {Event} from "src/engine/types"
|
||||
|
||||
export class Content {
|
||||
topics = collection<Topic>("name")
|
||||
lists = collection<List>("naddr")
|
||||
searchTopics = derived(this.topics, $topics =>
|
||||
fuzzy($topics.values(), {keys: ["name"], threshold: 0.3})
|
||||
)
|
||||
searchTopics = this.topics.derived($topics => fuzzy($topics, {keys: ["name"], threshold: 0.3}))
|
||||
|
||||
getLists = (f: (l: List) => boolean) =>
|
||||
this.lists.get().filter(l => !l.deleted_at && (f ? f(l) : true))
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {partition, identity, uniqBy, pluck, sortBy, without, any, prop, assoc} from "ramda"
|
||||
import {partition, uniqBy, pluck, sortBy, without, any, prop, assoc} from "ramda"
|
||||
import {ensurePlural, seconds, doPipe, throttle, batch} from "hurdak"
|
||||
import {now, race} from "src/util/misc"
|
||||
import {findReplyId} from "src/util/nostr"
|
||||
@ -106,9 +106,10 @@ export class FeedLoader {
|
||||
|
||||
addToFeed = (notes: Event[]) => {
|
||||
this.feed.update($feed => {
|
||||
// Avoid showing the same note twice, even if it's once as
|
||||
// a parent and once as a child
|
||||
const ids = new Set(pluck("id", $feed).concat($feed.map(findReplyId).filter(identity)))
|
||||
// Avoid showing the same note twice, even if it's once as a parent and once as a child
|
||||
const feedIds = new Set(pluck("id", $feed))
|
||||
// Maybe this?
|
||||
// const feedIds = new Set(pluck("id", $feed).concat($feed.concat(notes).map(findReplyId).filter(identity)))
|
||||
|
||||
return uniqBy(
|
||||
prop("id"),
|
||||
@ -116,7 +117,7 @@ export class FeedLoader {
|
||||
this.context.applyContext(
|
||||
sortBy(
|
||||
e => -e.created_at,
|
||||
notes.filter(e => !ids.has(e.id) && !ids.has(findReplyId(e)))
|
||||
notes.filter(e => !feedIds.has(findReplyId(e)))
|
||||
),
|
||||
true
|
||||
)
|
||||
|
@ -85,7 +85,7 @@ export class StorageAdapter {
|
||||
if (window.indexedDB) {
|
||||
const policies = [
|
||||
policy("Alerts.events", 500, sortBy(prop("created_at"))),
|
||||
policy("Nip28.channels", 1000, sortChannels),
|
||||
policy("Nip28.channels", 2000, sortChannels),
|
||||
policy("Nip28.messages", 10000, sortBy(prop("created_at"))),
|
||||
policy("Nip04.contacts", 1000, sortContacts),
|
||||
policy("Nip04.messages", 10000, sortBy(prop("created_at"))),
|
||||
|
@ -91,7 +91,7 @@
|
||||
|
||||
<div class="flex h-full gap-4">
|
||||
<div class="relative w-full">
|
||||
<div class="-mt-16 pt-20 flex h-screen flex-col" class:pb-20={Keys.canSign.get()}>
|
||||
<div class="-mt-16 flex h-screen flex-col pt-20" class:pb-20={Keys.canSign.get()}>
|
||||
<ul
|
||||
class="channel-messages flex flex-grow flex-col-reverse justify-start overflow-auto p-4 pb-6">
|
||||
{#each $groupedMessages as m (m.id)}
|
||||
@ -111,7 +111,7 @@
|
||||
</div>
|
||||
{#if Keys.canSign.get()}
|
||||
<div
|
||||
class="fixed bottom-0 z-10 flex w-full border-t border-solid border-gray-6 border-gray-7 bg-gray-6 lg:-ml-56 lg:pl-56">
|
||||
class="fixed bottom-0 z-10 flex w-full border-t border-solid border-gray-6 border-gray-7 bg-gray-6 lg:-ml-48 lg:pl-48">
|
||||
<textarea
|
||||
rows="3"
|
||||
autofocus
|
||||
@ -121,7 +121,7 @@
|
||||
class="w-full resize-none bg-gray-6 p-2
|
||||
text-gray-2 outline-0 placeholder:text-gray-1" />
|
||||
<div>
|
||||
<ImageInput hideInput onChange={addImage} icon="image">
|
||||
<ImageInput onChange={addImage}>
|
||||
<button
|
||||
slot="button"
|
||||
class="flex cursor-pointer flex-col justify-center gap-2 border-l border-solid border-gray-7 p-3
|
||||
|
@ -8,12 +8,11 @@
|
||||
import {listenForFile, stripExifData, blobToFile} from "src/util/html"
|
||||
import {Settings} from "src/app/engine"
|
||||
|
||||
export let icon
|
||||
export let icon = null
|
||||
export let value = null
|
||||
export let multi = false
|
||||
export let maxWidth = null
|
||||
export let maxHeight = null
|
||||
export let hideInput = false
|
||||
export let onChange = null
|
||||
|
||||
let input, listener, quote
|
||||
@ -70,23 +69,24 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !hideInput}
|
||||
<Input type="text" wrapperClass="flex-grow" bind:value placeholder="https://">
|
||||
<i slot="before" class={`fa fa-${icon}`} />
|
||||
</Input>
|
||||
{/if}
|
||||
|
||||
<div
|
||||
on:click={() => {
|
||||
isOpen = true
|
||||
}}>
|
||||
<slot name="button">
|
||||
<div class="flex">
|
||||
<Anchor theme="button">
|
||||
<i class="fa fa-upload" />
|
||||
</Anchor>
|
||||
</div>
|
||||
</slot>
|
||||
<div class="flex gap-2">
|
||||
{#if icon}
|
||||
<Input type="text" wrapperClass="flex-grow" bind:value placeholder="https://">
|
||||
<i slot="before" class={`fa fa-${icon}`} />
|
||||
</Input>
|
||||
{/if}
|
||||
<div
|
||||
on:click={() => {
|
||||
isOpen = true
|
||||
}}>
|
||||
<slot name="button">
|
||||
<div class="flex">
|
||||
<Anchor theme="button">
|
||||
<i class="fa fa-upload" />
|
||||
</Anchor>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if quote}
|
||||
|
@ -171,7 +171,7 @@ export const mergeFilter = (filter: Filter | Filter[], extra: Filter) =>
|
||||
|
||||
export const fromNostrURI = (s: string) => s.replace(/^[\w\+]+:\/?\/?/, "")
|
||||
|
||||
export const toNostrURI = (s: string) => `web+nostr://${s}`
|
||||
export const toNostrURI = (s: string) => `nostr://${s}`
|
||||
|
||||
export const getLabelQuality = (label: string, event: Event) => {
|
||||
const json = tryJson(() => JSON.parse(last(Tags.from(event).type("l").equals(label).first())))
|
||||
|
Loading…
Reference in New Issue
Block a user