mirror of
https://github.com/coracle-social/coracle.git
synced 2024-10-06 11:43:30 +00:00
Fix mention parsing
This commit is contained in:
parent
ce6993b214
commit
9bc5a35957
14
ROADMAP.md
14
ROADMAP.md
@ -1,5 +1,7 @@
|
|||||||
# Current
|
# Current
|
||||||
|
|
||||||
|
- [ ] Fix issues
|
||||||
|
- [ ] Don't escape html in parseContent
|
||||||
- [ ] Look into AUTH not working for mazin
|
- [ ] Look into AUTH not working for mazin
|
||||||
- [ ] Write NIP to support proxies. Update COUNT NIP to mention how proxies are a good use case for COUNT
|
- [ ] Write NIP to support proxies. Update COUNT NIP to mention how proxies are a good use case for COUNT
|
||||||
- [ ] Write blog post on multiplexer
|
- [ ] Write blog post on multiplexer
|
||||||
@ -11,17 +13,6 @@
|
|||||||
- [ ] Add onError handler to subscriptions for when sockets fail to connect?
|
- [ ] Add onError handler to subscriptions for when sockets fail to connect?
|
||||||
- [ ] Add bugsnag to multiplexr
|
- [ ] Add bugsnag to multiplexr
|
||||||
|
|
||||||
# Others
|
|
||||||
|
|
||||||
- Recommendations
|
|
||||||
- Indexer/multiplexer
|
|
||||||
- relay.coracle.social for people nip-05 verified via coracle
|
|
||||||
- Improve overall design
|
|
||||||
- Stripped down easy version of coracle
|
|
||||||
- Extract library?
|
|
||||||
- Parameterize color scheme
|
|
||||||
- Deploy to special domains with relays built in
|
|
||||||
|
|
||||||
# Custom views
|
# Custom views
|
||||||
|
|
||||||
- [ ] Add customize icon and route with editable custom view cards using "lists" nip
|
- [ ] Add customize icon and route with editable custom view cards using "lists" nip
|
||||||
@ -30,6 +21,7 @@
|
|||||||
|
|
||||||
# More
|
# More
|
||||||
|
|
||||||
|
- [ ] Build per-relay pagination, put it in paravel https://github.com/nostr-protocol/nips/pull/408
|
||||||
- [ ] Linkify topics
|
- [ ] Linkify topics
|
||||||
- [ ] Add suggestion list for topics on compose so people know there are suggestions
|
- [ ] Add suggestion list for topics on compose so people know there are suggestions
|
||||||
- [ ] Badges link to https://badges.page/p/97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322
|
- [ ] Badges link to https://badges.page/p/97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
{#if !hidden}
|
{#if !hidden}
|
||||||
<div in:slide class="relative">
|
<div in:slide class="relative">
|
||||||
<CarouselItem link={annotated[0]} showLoading={false} onClose={close} />
|
<CarouselItem link={annotated[0]} onClose={close} />
|
||||||
{#if annotated.length > 1}
|
{#if annotated.length > 1}
|
||||||
<p class="text-gray-500 py-4 text-center underline" on:click={openModal}>
|
<p class="text-gray-500 py-4 text-center underline" on:click={openModal}>
|
||||||
<i class="fa fa-plus" /> Show {annotated.length} link previews
|
<i class="fa fa-plus" /> Show {annotated.length} link previews
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import cx from "classnames"
|
import cx from "classnames"
|
||||||
import {ellipsize} from "hurdak/lib/hurdak"
|
import {ellipsize} from "hurdak/lib/hurdak"
|
||||||
import {fly} from "svelte/transition"
|
import {fly, slide} from "svelte/transition"
|
||||||
import Anchor from "src/partials/Anchor.svelte"
|
import Anchor from "src/partials/Anchor.svelte"
|
||||||
import Spinner from "src/partials/Spinner.svelte"
|
import Spinner from "src/partials/Spinner.svelte"
|
||||||
import user from "src/agent/user"
|
import user from "src/agent/user"
|
||||||
@ -9,7 +9,6 @@
|
|||||||
export let link
|
export let link
|
||||||
export let onClick = null
|
export let onClick = null
|
||||||
export let onClose = null
|
export let onClose = null
|
||||||
export let showLoading = true
|
|
||||||
|
|
||||||
const loadPreview = async () => {
|
const loadPreview = async () => {
|
||||||
const res = await fetch(user.dufflepud("/link/preview"), {
|
const res = await fetch(user.dufflepud("/link/preview"), {
|
||||||
@ -36,18 +35,14 @@
|
|||||||
href={onClick ? null : link.url}
|
href={onClick ? null : link.url}
|
||||||
on:click={onClick}
|
on:click={onClick}
|
||||||
style="background-color: rgba(15, 15, 14, 0.5)"
|
style="background-color: rgba(15, 15, 14, 0.5)"
|
||||||
class={cx("relative flex flex-col overflow-hidden rounded border-solid border-gray-6", {
|
class={cx("relative flex flex-col overflow-hidden rounded border border-solid border-gray-6")}>
|
||||||
border: showLoading || link.type !== "preview",
|
|
||||||
})}>
|
|
||||||
{#if link.type === "image"}
|
{#if link.type === "image"}
|
||||||
<img alt="Link preview" src={link.url} class="max-h-96 object-contain object-center" />
|
<img alt="Link preview" src={link.url} class="max-h-96 object-contain object-center" />
|
||||||
{:else if link.type === "video"}
|
{:else if link.type === "video"}
|
||||||
<video controls src={link.url} class="max-h-96 object-contain object-center" />
|
<video controls src={link.url} class="max-h-96 object-contain object-center" />
|
||||||
{:else}
|
{:else}
|
||||||
{#await loadPreview()}
|
{#await loadPreview()}
|
||||||
{#if showLoading}
|
|
||||||
<Spinner />
|
<Spinner />
|
||||||
{/if}
|
|
||||||
{:then { title, description, image }}
|
{:then { title, description, image }}
|
||||||
{#if image}
|
{#if image}
|
||||||
<img alt="Link preview" src={image} class="max-h-96 object-contain object-center" />
|
<img alt="Link preview" src={image} class="max-h-96 object-contain object-center" />
|
||||||
@ -68,11 +63,9 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{:catch}
|
{:catch}
|
||||||
{#if showLoading}
|
<p class="mb-1 py-24 px-12 text-center text-gray-5">
|
||||||
<p class="mb-1 py-24 px-12 text-center text-gray-5" in:fly={{y: 20}}>
|
|
||||||
Unable to load a preview for {link.url}
|
Unable to load a preview for {link.url}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
|
||||||
{/await}
|
{/await}
|
||||||
{/if}
|
{/if}
|
||||||
</Anchor>
|
</Anchor>
|
||||||
|
@ -14,8 +14,10 @@
|
|||||||
|
|
||||||
<p class="overflow-hidden text-ellipsis">
|
<p class="overflow-hidden text-ellipsis">
|
||||||
{#each content as { type, value }}
|
{#each content as { type, value }}
|
||||||
{#if type === "br"}
|
{#if type === "newline"}
|
||||||
{@html value}
|
{#each value as _}
|
||||||
|
<br />
|
||||||
|
{/each}
|
||||||
{:else if type === "link"}
|
{:else if type === "link"}
|
||||||
<Anchor external href={value}>
|
<Anchor external href={value}>
|
||||||
{value.replace(/https?:\/\/(www\.)?/, "")}
|
{value.replace(/https?:\/\/(www\.)?/, "")}
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
<script>
|
|
||||||
import {onMount} from "svelte"
|
|
||||||
import {slide} from "svelte/transition"
|
|
||||||
import Anchor from "src/partials/Anchor.svelte"
|
|
||||||
import user from "src/agent/user"
|
|
||||||
|
|
||||||
export let url
|
|
||||||
export let onClose = null
|
|
||||||
|
|
||||||
let preview
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
onClose?.()
|
|
||||||
preview = null
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
if (url.match(".(jpg|jpeg|png|gif)")) {
|
|
||||||
preview = {image: url}
|
|
||||||
} else if (url.match(".(mov|mp4)")) {
|
|
||||||
preview = {video: url}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
const res = await fetch(user.dufflepud("/link/preview"), {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({url}),
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const json = await res.json()
|
|
||||||
|
|
||||||
if (json.title || json.image) {
|
|
||||||
preview = json
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if preview}
|
|
||||||
<div in:slide>
|
|
||||||
<Anchor
|
|
||||||
external
|
|
||||||
href={url}
|
|
||||||
style="background-color: rgba(15, 15, 14, 0.5)"
|
|
||||||
class="relative flex flex-col overflow-hidden rounded border border-solid border-gray-6">
|
|
||||||
{#if preview.image}
|
|
||||||
<img alt="Link preview" src={preview.image} class="max-h-96 object-contain object-center" />
|
|
||||||
{/if}
|
|
||||||
{#if preview.video}
|
|
||||||
<video controls src={preview.video} class="max-h-96 object-contain object-center" />
|
|
||||||
{/if}
|
|
||||||
<div class="h-px bg-gray-6" />
|
|
||||||
{#if preview.title}
|
|
||||||
<div class="flex flex-col bg-white px-4 py-2 text-black">
|
|
||||||
<strong class="overflow-hidden text-ellipsis whitespace-nowrap">{preview.title}</strong>
|
|
||||||
<small>{preview.description}</small>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<div
|
|
||||||
on:click|preventDefault={close}
|
|
||||||
class="absolute top-0 right-0 m-1 flex h-6 w-6 items-center justify-center
|
|
||||||
rounded-full border border-solid border-gray-6 bg-white text-black opacity-50 shadow">
|
|
||||||
<i class="fa fa-times" />
|
|
||||||
</div>
|
|
||||||
</Anchor>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
@ -103,7 +103,7 @@ export const noEvent = f => e => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const parseContent = content => {
|
export const parseContent = content => {
|
||||||
const text = escapeHtml(content.trim())
|
const text = content.trim()
|
||||||
const result = []
|
const result = []
|
||||||
let buffer = "",
|
let buffer = "",
|
||||||
i = 0
|
i = 0
|
||||||
@ -121,10 +121,10 @@ export const parseContent = content => {
|
|||||||
for (; i < text.length; ) {
|
for (; i < text.length; ) {
|
||||||
const tail = text.slice(i)
|
const tail = text.slice(i)
|
||||||
|
|
||||||
const brMatch = tail.match(/^(<br>)+/)
|
const newLine = tail.match(/^\n+/)
|
||||||
|
|
||||||
if (brMatch) {
|
if (newLine) {
|
||||||
push("br", brMatch[0])
|
push("newline", newLine[0])
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
import PersonCircle from "src/partials/PersonCircle.svelte"
|
import PersonCircle from "src/partials/PersonCircle.svelte"
|
||||||
import RelayCard from "src/views/relays/RelayCard.svelte"
|
import RelayCard from "src/views/relays/RelayCard.svelte"
|
||||||
import Modal from "src/partials/Modal.svelte"
|
import Modal from "src/partials/Modal.svelte"
|
||||||
import Preview from "src/partials/Preview.svelte"
|
import CarouselItem from "src/partials/CarouselItem.svelte"
|
||||||
import Anchor from "src/partials/Anchor.svelte"
|
import Anchor from "src/partials/Anchor.svelte"
|
||||||
import {toast, modal} from "src/app/ui"
|
import {toast, modal} from "src/app/ui"
|
||||||
import Compose from "src/partials/Compose.svelte"
|
import Compose from "src/partials/Compose.svelte"
|
||||||
@ -512,8 +512,8 @@
|
|||||||
</div>
|
</div>
|
||||||
{#if image}
|
{#if image}
|
||||||
<div class="bg-gray-7 p-2">
|
<div class="bg-gray-7 p-2">
|
||||||
<Preview
|
<CarouselItem
|
||||||
url={image}
|
link={{type: 'image', url: image}}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
image = null
|
image = null
|
||||||
}} />
|
}} />
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
const entities = []
|
const entities = []
|
||||||
const shouldTruncate = !showEntire && note.content.length > 500
|
const shouldTruncate = !showEntire && note.content.length > 500
|
||||||
const content = parseContent(note.content)
|
const content = parseContent(note.content)
|
||||||
|
const showMedia = user.getSetting("showMedia")
|
||||||
|
|
||||||
let l = 0
|
let l = 0
|
||||||
for (let i = 0; i < content.length; i++) {
|
for (let i = 0; i < content.length; i++) {
|
||||||
@ -42,11 +43,11 @@
|
|||||||
|
|
||||||
// If the link is surrounded by line breaks (or content start/end), remove
|
// If the link is surrounded by line breaks (or content start/end), remove
|
||||||
// the link along with trailing whitespace
|
// the link along with trailing whitespace
|
||||||
if ((!prev || prev.type === "br") && (!next || next.type === "br")) {
|
if (showMedia && (!prev || prev.type === "newline") && (!next || next.type === "newline")) {
|
||||||
let n = 0
|
let n = 0
|
||||||
|
|
||||||
for (let j = i + 1; j < content.length; j++) {
|
for (let j = i + 1; j < content.length; j++) {
|
||||||
if (content[j].type !== "br") {
|
if (content[j].type !== "newline") {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,10 +60,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Keep track of total characters, if we're not dealing with a string just guess
|
// Keep track of total characters, if we're not dealing with a string just guess
|
||||||
if (value instanceof String) {
|
if (typeof value === 'string') {
|
||||||
l += value.length
|
l += value.length
|
||||||
|
|
||||||
if (shouldTruncate && l > 350 && type !== "br") {
|
if (shouldTruncate && l > 350 && type !== "newline") {
|
||||||
content[i].value = value.trim()
|
content[i].value = value.trim()
|
||||||
content.splice(i + 1, content.length, {type: "text", value: "..."})
|
content.splice(i + 1, content.length, {type: "text", value: "..."})
|
||||||
break
|
break
|
||||||
@ -75,9 +76,12 @@
|
|||||||
const getMentionPubkey = text => {
|
const getMentionPubkey = text => {
|
||||||
const i = parseInt(first(text.match(/\d+/)))
|
const i = parseInt(first(text.match(/\d+/)))
|
||||||
|
|
||||||
// Some implementations count only p tags when calculating index
|
// Some implementations count only p tags when calculating index, and some
|
||||||
|
// implementations are 1-indexed
|
||||||
if (note.tags[i]?.[0] === "p") {
|
if (note.tags[i]?.[0] === "p") {
|
||||||
return note.tags[i][1]
|
return note.tags[i][1]
|
||||||
|
} else if (note.tags[i-1]?.[0] === "p") {
|
||||||
|
return note.tags[i-1][1]
|
||||||
} else {
|
} else {
|
||||||
return Tags.from(note).type("p").values().nth(i)
|
return Tags.from(note).type("p").values().nth(i)
|
||||||
}
|
}
|
||||||
@ -104,8 +108,10 @@
|
|||||||
<div class="flex flex-col gap-2 overflow-hidden text-ellipsis">
|
<div class="flex flex-col gap-2 overflow-hidden text-ellipsis">
|
||||||
<p>
|
<p>
|
||||||
{#each content as { type, value }}
|
{#each content as { type, value }}
|
||||||
{#if type === "br"}
|
{#if type === "newline"}
|
||||||
{@html value}
|
{#each value as _}
|
||||||
|
<br />
|
||||||
|
{/each}
|
||||||
{:else if type === "link"}
|
{:else if type === "link"}
|
||||||
<Anchor external href={value}>
|
<Anchor external href={value}>
|
||||||
{value.replace(/https?:\/\/(www\.)?/, "")}
|
{value.replace(/https?:\/\/(www\.)?/, "")}
|
||||||
@ -132,7 +138,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</p>
|
</p>
|
||||||
{#if user.getSetting("showMedia") && links.length > 0}
|
{#if showMedia && links.length > 0}
|
||||||
<div on:click={e => e.stopPropagation()}>
|
<div on:click={e => e.stopPropagation()}>
|
||||||
<Carousel {links} />
|
<Carousel {links} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
import Button from "src/partials/Button.svelte"
|
import Button from "src/partials/Button.svelte"
|
||||||
import Compose from "src/partials/Compose.svelte"
|
import Compose from "src/partials/Compose.svelte"
|
||||||
import ImageInput from "src/partials/ImageInput.svelte"
|
import ImageInput from "src/partials/ImageInput.svelte"
|
||||||
import Preview from "src/partials/Preview.svelte"
|
import CarouselItem from "src/partials/CarouselItem.svelte"
|
||||||
import Input from "src/partials/Input.svelte"
|
import Input from "src/partials/Input.svelte"
|
||||||
import RelayCardSimple from "src/partials/RelayCardSimple.svelte"
|
import RelayCardSimple from "src/partials/RelayCardSimple.svelte"
|
||||||
import Content from "src/partials/Content.svelte"
|
import Content from "src/partials/Content.svelte"
|
||||||
@ -113,8 +113,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if image}
|
{#if image}
|
||||||
<Preview
|
<CarouselItem
|
||||||
url={image}
|
link={{type: 'image', url: image}}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
image = null
|
image = null
|
||||||
}} />
|
}} />
|
||||||
|
Loading…
Reference in New Issue
Block a user