mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-30 00:41:12 +00:00
Add global feeds
This commit is contained in:
parent
c6cdcfb2f8
commit
b92b0b71b5
@ -10,6 +10,7 @@
|
||||
- [x] Fix several community and calendar related bugs
|
||||
- [x] Add reports using tagr-bot
|
||||
- [x] Open links to coracle in same tab
|
||||
- [x] Add global feeds
|
||||
|
||||
# 0.4.6
|
||||
|
||||
|
BIN
package-lock.json
generated
BIN
package-lock.json
generated
Binary file not shown.
@ -56,7 +56,7 @@
|
||||
"@getalby/bitcoin-connect": "^3.2.2",
|
||||
"@scure/base": "^1.1.6",
|
||||
"@welshman/content": "^0.0.5",
|
||||
"@welshman/feeds": "^0.0.11",
|
||||
"@welshman/feeds": "^0.0.12",
|
||||
"@welshman/lib": "^0.0.9",
|
||||
"@welshman/net": "^0.0.13",
|
||||
"@welshman/util": "^0.0.14",
|
||||
|
@ -89,6 +89,8 @@
|
||||
let search = getSearch(feed.definition)
|
||||
|
||||
$: subFeeds = getFeedArgs(feed.definition as any)
|
||||
|
||||
$: console.log(feed)
|
||||
</script>
|
||||
|
||||
<div class="flex flex-grow items-center justify-end gap-2">
|
||||
|
@ -11,15 +11,18 @@
|
||||
isRelayFeed,
|
||||
isListFeed,
|
||||
isDVMFeed,
|
||||
isGlobalFeed,
|
||||
makeListFeed,
|
||||
makeDVMFeed,
|
||||
makeScopeFeed,
|
||||
makeTagFeed,
|
||||
makeRelayFeed,
|
||||
makeGlobalFeed,
|
||||
Scope,
|
||||
} from "@welshman/feeds"
|
||||
import {toSpliced} from "src/util/misc"
|
||||
import Icon from "src/partials/Icon.svelte"
|
||||
import SelectButton from "src/partials/SelectButton.svelte"
|
||||
import SelectTiles from "src/partials/SelectTiles.svelte"
|
||||
import Card from "src/partials/Card.svelte"
|
||||
import FlexColumn from "src/partials/FlexColumn.svelte"
|
||||
@ -29,6 +32,7 @@
|
||||
export let feed
|
||||
|
||||
enum FormType {
|
||||
Global = "global",
|
||||
Advanced = "advanced",
|
||||
DVMs = "dvms",
|
||||
Lists = "lists",
|
||||
@ -60,25 +64,12 @@
|
||||
|
||||
const inferFormType = feed => {
|
||||
for (const subFeed of getFeedArgs(normalize(feed))) {
|
||||
if ([FeedType.Scope, FeedType.Author].includes(subFeed[0])) {
|
||||
return FormType.People
|
||||
}
|
||||
|
||||
if (subFeed[0] === FeedType.Tag && subFeed[1] === "#t") {
|
||||
return FormType.Topics
|
||||
}
|
||||
|
||||
if (subFeed[0] === FeedType.Relay) {
|
||||
return FormType.Relays
|
||||
}
|
||||
|
||||
if (subFeed[0] === FeedType.List) {
|
||||
return FormType.Lists
|
||||
}
|
||||
|
||||
if (subFeed[0] === FeedType.DVM) {
|
||||
return FormType.DVMs
|
||||
}
|
||||
if (isGlobalFeed(subFeed)) return FormType.Global
|
||||
if (isScopeFeed(subFeed) || isAuthorFeed(subFeed)) return FormType.People
|
||||
if (isTagFeed(subFeed) && subFeed[1] === "#t") return FormType.Topics
|
||||
if (isRelayFeed(subFeed)) return FormType.Relays
|
||||
if (isListFeed(subFeed)) return FormType.Lists
|
||||
if (isDVMFeed(subFeed)) return FormType.DVMs
|
||||
}
|
||||
|
||||
return FormType.Advanced
|
||||
@ -96,7 +87,9 @@
|
||||
|
||||
// Remove filters directly related to the previous type
|
||||
if (newFormType !== FormType.Advanced) {
|
||||
if (formType === FormType.People) {
|
||||
if (formType === FormType.Global) {
|
||||
removeSubFeed(isGlobalFeed)
|
||||
} else if (formType === FormType.People) {
|
||||
removeSubFeed(isPeopleFeed)
|
||||
} else if (formType === FormType.Topics) {
|
||||
removeSubFeed(isTopicsFeed)
|
||||
@ -112,7 +105,9 @@
|
||||
formType = newFormType
|
||||
|
||||
// Add a default filter depending on the new form type
|
||||
if (formType === FormType.People) {
|
||||
if (formType === FormType.Global) {
|
||||
prependDefaultSubFeed(isGlobalFeed, makeGlobalFeed())
|
||||
} else if (formType === FormType.People) {
|
||||
prependDefaultSubFeed(isPeopleFeed, makeScopeFeed(Scope.Follows))
|
||||
} else if (formType === FormType.Topics) {
|
||||
prependDefaultSubFeed(isTopicsFeed, makeTagFeed("#t"))
|
||||
@ -132,25 +127,55 @@
|
||||
let formType = inferFormType(feed)
|
||||
|
||||
$: formTypeOptions = [
|
||||
FormType.Global,
|
||||
FormType.People,
|
||||
FormType.Topics,
|
||||
FormType.Relays,
|
||||
FormType.Lists,
|
||||
FormType.DVMs,
|
||||
FormType.Advanced,
|
||||
]
|
||||
</script>
|
||||
|
||||
<FlexColumn>
|
||||
<Card class="-mb-8">
|
||||
<Card>
|
||||
<FlexColumn small>
|
||||
<span class="staatliches text-lg">Choose a feed type</span>
|
||||
<SelectButton
|
||||
class="sm:hidden"
|
||||
options={formTypeOptions}
|
||||
onChange={onFormTypeChange}
|
||||
value={formType}>
|
||||
<div slot="item" class="flex items-center gap-2" let:option let:active>
|
||||
{#if option === FormType.Global}
|
||||
<i class="fa fa-earth-europe" /> Global
|
||||
{:else if option === FormType.People}
|
||||
<i class="fa fa-person" /> People
|
||||
{:else if option === FormType.Topics}
|
||||
<i class="fa fa-tags" /> Topics
|
||||
{:else if option === FormType.Relays}
|
||||
<i class="fa fa-server" /> Relays
|
||||
{:else if option === FormType.Lists}
|
||||
<i class="fa fa-bars-staggered" /> Lists
|
||||
{:else if option === FormType.DVMs}
|
||||
<i class="fa fa-circle-nodes" /> DVMs
|
||||
{:else if option === FormType.Advanced}
|
||||
<i class="fa fa-cogs" /> Advanced
|
||||
{/if}
|
||||
</div>
|
||||
</SelectButton>
|
||||
<SelectTiles
|
||||
class="grid-cols-2 xs:grid-cols-3 md:grid-cols-5"
|
||||
class="hidden grid-cols-4 sm:grid"
|
||||
options={formTypeOptions}
|
||||
onChange={onFormTypeChange}
|
||||
value={formType}>
|
||||
<div slot="item" class="flex flex-col items-center" let:option let:active>
|
||||
{#if option === FormType.People}
|
||||
{#if option === FormType.Global}
|
||||
<span class="flex h-12 w-12 items-center justify-center" class:text-accent={active}>
|
||||
<i class="fa fa-2xl fa-earth-europe" />
|
||||
</span>
|
||||
<span class="staatliches text-2xl">Global</span>
|
||||
{:else if option === FormType.People}
|
||||
<Icon icon="people-nearby" class="h-12 w-12" color={active ? "accent" : "tinted-800"} />
|
||||
<span class="staatliches text-2xl">People</span>
|
||||
{:else if option === FormType.Topics}
|
||||
@ -169,17 +194,25 @@
|
||||
{:else if option === FormType.DVMs}
|
||||
<Icon icon="network" class="h-12 w-12" color={active ? "accent" : "tinted-800"} />
|
||||
<span class="staatliches text-2xl">DVMs</span>
|
||||
{:else if option === FormType.Advanced}
|
||||
<span class="flex h-12 w-12 items-center justify-center" class:text-accent={active}>
|
||||
<i class="fa fa-2xl fa-cogs" />
|
||||
</span>
|
||||
<span class="staatliches text-2xl">Advanced</span>
|
||||
{/if}
|
||||
</div>
|
||||
</SelectTiles>
|
||||
<div
|
||||
class="flex cursor-pointer items-center justify-end gap-2 text-neutral-500"
|
||||
on:click={() => onFormTypeChange(FormType.Advanced)}>
|
||||
<span class="staatliches underline">Advanced Mode</span>
|
||||
</div>
|
||||
</FlexColumn>
|
||||
</Card>
|
||||
<FlexColumn>
|
||||
{#if formType === FormType.Global && feed.length === 2}
|
||||
<Card class="flex gap-4 items-center">
|
||||
<i class="fa fa-triangle-exclamation text-warning fa-xl" />
|
||||
<p>
|
||||
Be aware that feeds with no filters can result in obscene or otherwise objectionable content being displayed.
|
||||
</p>
|
||||
</Card>
|
||||
{/if}
|
||||
{#if formType === FormType.Advanced}
|
||||
<FeedFormAdvanced {feed} onChange={onFeedChange} />
|
||||
{:else}
|
||||
|
@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import {tryCatch} from "@welshman/lib"
|
||||
import Card from "src/partials/Card.svelte"
|
||||
import Field from "src/partials/Field.svelte"
|
||||
import Textarea from "src/partials/Textarea.svelte"
|
||||
import FlexColumn from "src/partials/FlexColumn.svelte"
|
||||
|
||||
export let feed
|
||||
export let onChange
|
||||
@ -35,18 +35,19 @@
|
||||
</script>
|
||||
|
||||
<Card>
|
||||
<Field label="Enter your custom feed below">
|
||||
<FlexColumn>
|
||||
<span class="staatliches text-lg">Enter your custom feed below</span>
|
||||
<Textarea
|
||||
class="h-72 whitespace-pre-wrap"
|
||||
value={json}
|
||||
on:input={onInput}
|
||||
on:focus={onFocus}
|
||||
on:blur={onBlur} />
|
||||
</Field>
|
||||
{#if !isValid && !isFocused}
|
||||
<p>
|
||||
<i class="fa fa-triangle-exclamation" />
|
||||
Your feed is currently invalid. Please double check that it is valid JSON.
|
||||
</p>
|
||||
{/if}
|
||||
{#if !isValid && !isFocused}
|
||||
<p>
|
||||
<i class="fa fa-triangle-exclamation" />
|
||||
Your feed is currently invalid. Please double check that it is valid JSON.
|
||||
</p>
|
||||
{/if}
|
||||
</FlexColumn>
|
||||
</Card>
|
||||
|
@ -2,6 +2,7 @@
|
||||
import {toTitle} from "hurdak"
|
||||
import {
|
||||
getFeedArgs,
|
||||
isGlobalFeed,
|
||||
isCreatedAtFeed,
|
||||
isAuthorFeed,
|
||||
isKindFeed,
|
||||
@ -68,41 +69,48 @@
|
||||
{#each subFeeds as subFeed, i}
|
||||
{@const idx = i + 1}
|
||||
{@const change = f => onSubFeedChange(idx, f)}
|
||||
<Card class="relative">
|
||||
<FlexColumn>
|
||||
<FlexColumn small>
|
||||
{#if isPeopleFeed(subFeed)}
|
||||
<FeedFormSectionPeople feed={subFeed} onChange={change} />
|
||||
{:else if isRelayFeed(subFeed)}
|
||||
<FeedFormSectionRelays feed={subFeed} onChange={change} />
|
||||
{:else if isTopicFeed(subFeed)}
|
||||
<FeedFormSectionTopics feed={subFeed} onChange={change} />
|
||||
{:else if isMentionFeed(subFeed)}
|
||||
<FeedFormSectionMentions feed={subFeed} onChange={change} />
|
||||
{:else if isKindFeed(subFeed)}
|
||||
<FeedFormSectionKinds feed={subFeed} onChange={change} />
|
||||
{:else if isCreatedAtFeed(subFeed)}
|
||||
<FeedFormSectionCreatedAt feed={subFeed} onChange={change} />
|
||||
{:else if isListFeed(subFeed)}
|
||||
<FeedFormSectionList feed={subFeed} onChange={change} />
|
||||
{:else if isDVMFeed(subFeed)}
|
||||
<FeedFormSectionDVM feed={subFeed} onChange={change} />
|
||||
{:else}
|
||||
No support for editing {toTitle(subFeed[0])} filters. Click "Advanced" to edit manually.
|
||||
{@const canSave =
|
||||
isAuthorFeed(subFeed) ||
|
||||
isRelayFeed(subFeed) ||
|
||||
isTopicFeed(subFeed) ||
|
||||
isMentionFeed(subFeed)}
|
||||
{#if canSave || !isGlobalFeed(subFeed)}
|
||||
<Card class="relative">
|
||||
<FlexColumn>
|
||||
<FlexColumn small>
|
||||
{#if isPeopleFeed(subFeed)}
|
||||
<FeedFormSectionPeople feed={subFeed} onChange={change} />
|
||||
{:else if isRelayFeed(subFeed)}
|
||||
<FeedFormSectionRelays feed={subFeed} onChange={change} />
|
||||
{:else if isTopicFeed(subFeed)}
|
||||
<FeedFormSectionTopics feed={subFeed} onChange={change} />
|
||||
{:else if isMentionFeed(subFeed)}
|
||||
<FeedFormSectionMentions feed={subFeed} onChange={change} />
|
||||
{:else if isKindFeed(subFeed)}
|
||||
<FeedFormSectionKinds feed={subFeed} onChange={change} />
|
||||
{:else if isCreatedAtFeed(subFeed)}
|
||||
<FeedFormSectionCreatedAt feed={subFeed} onChange={change} />
|
||||
{:else if isListFeed(subFeed)}
|
||||
<FeedFormSectionList feed={subFeed} onChange={change} />
|
||||
{:else if isDVMFeed(subFeed)}
|
||||
<FeedFormSectionDVM feed={subFeed} onChange={change} />
|
||||
{:else}
|
||||
No support for editing {toTitle(subFeed[0])} filters. Click "Advanced" to edit manually.
|
||||
{/if}
|
||||
</FlexColumn>
|
||||
{#if canSave}
|
||||
<FeedFormSaveAsList feed={subFeed} onChange={change} />
|
||||
{/if}
|
||||
</FlexColumn>
|
||||
{#if isAuthorFeed(subFeed) || isRelayFeed(subFeed) || isTopicFeed(subFeed) || isMentionFeed(subFeed)}
|
||||
<FeedFormSaveAsList feed={subFeed} onChange={change} />
|
||||
{#if i > 0}
|
||||
<div
|
||||
class="absolute right-2 top-2 h-4 w-4 cursor-pointer"
|
||||
on:click={() => onSubFeedRemove(idx)}>
|
||||
<i class="fa fa-times" />
|
||||
</div>
|
||||
{/if}
|
||||
</FlexColumn>
|
||||
{#if i > 0}
|
||||
<div
|
||||
class="absolute right-2 top-2 h-4 w-4 cursor-pointer"
|
||||
on:click={() => onSubFeedRemove(idx)}>
|
||||
<i class="fa fa-times" />
|
||||
</div>
|
||||
{/if}
|
||||
</Card>
|
||||
</Card>
|
||||
{/if}
|
||||
{/each}
|
||||
{/key}
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
$: addresses = feed.slice(1).flatMap(it => it.addresses)
|
||||
</script>
|
||||
|
||||
<span>Which lists would you like to use?</span>
|
||||
<span class="staatliches text-lg">Which lists would you like to use?</span>
|
||||
<SearchSelect
|
||||
multiple
|
||||
value={addresses}
|
||||
|
@ -23,7 +23,7 @@
|
||||
$: scopes = isScopeFeed(feed) ? feed.slice(1) : ["custom"]
|
||||
</script>
|
||||
|
||||
<span>Which authors would you like to see?</span>
|
||||
<span class="staatliches text-lg">Which authors would you like to see?</span>
|
||||
<SelectButton
|
||||
multiple
|
||||
value={scopes}
|
||||
|
@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import {without} from "ramda"
|
||||
import {
|
||||
parse,
|
||||
truncate,
|
||||
@ -51,9 +50,6 @@
|
||||
|
||||
const isStartOrEnd = i => Boolean(isBoundary(i - 1) && isBoundary(i + 1))
|
||||
|
||||
const getLinks = content =>
|
||||
content.filter(x => isLink(x) && x.value.isMedia).map(x => x.value.url.toString())
|
||||
|
||||
$: shortContent = showEntire
|
||||
? fullContent
|
||||
: truncate(
|
||||
@ -65,7 +61,6 @@
|
||||
},
|
||||
)
|
||||
|
||||
$: links = getLinks(shortContent)
|
||||
$: ellipsize = expandable && shortContent.find(isEllipsis)
|
||||
</script>
|
||||
|
||||
|
@ -96,7 +96,7 @@
|
||||
<p class="mt-4 text-lg">The following relays are still pending:</p>
|
||||
<div class="grid gap-2 sm:grid-cols-2">
|
||||
{#each pending as url}
|
||||
<RelayCard hideActions {url} />
|
||||
<RelayCard hideDescription hideActions {url} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
@ -104,7 +104,7 @@
|
||||
<p class="mt-4 text-lg">The following relays accepted your note:</p>
|
||||
<div class="grid gap-2 sm:grid-cols-2">
|
||||
{#each success as url}
|
||||
<RelayCard hideActions {url} />
|
||||
<RelayCard hideDescription hideActions {url} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -18,6 +18,7 @@
|
||||
export let claim = null
|
||||
export let ratings = null
|
||||
export let showStatus = false
|
||||
export let hideDescription = false
|
||||
export let hideRatingsCount = false
|
||||
export let hideActions = false
|
||||
export let showControls = false
|
||||
@ -64,30 +65,32 @@
|
||||
</slot>
|
||||
{/if}
|
||||
</div>
|
||||
<slot name="description">
|
||||
{#if $relay.description}
|
||||
<p>{$relay.description}</p>
|
||||
{#if !hideDescription}
|
||||
<slot name="description">
|
||||
{#if $relay.description}
|
||||
<p>{$relay.description}</p>
|
||||
{/if}
|
||||
</slot>
|
||||
{#if !isNil($relay.count)}
|
||||
<span class="flex items-center gap-1 text-sm text-neutral-400">
|
||||
{#if $relay.contact}
|
||||
<Anchor external underline href={$relay.contact}>{displayUrl($relay.contact)}</Anchor>
|
||||
•
|
||||
{/if}
|
||||
{#if $relay.supported_nips}
|
||||
<Popover>
|
||||
<span slot="trigger" class="cursor-pointer underline">
|
||||
{$relay.supported_nips.length} NIPs
|
||||
</span>
|
||||
<span slot="tooltip">
|
||||
NIPs supported: {$relay.supported_nips.join(", ")}
|
||||
</span>
|
||||
</Popover>
|
||||
•
|
||||
{/if}
|
||||
Seen {quantify($relay.count || 0, "time")}
|
||||
</span>
|
||||
{/if}
|
||||
</slot>
|
||||
{#if !isNil($relay.count)}
|
||||
<span class="flex items-center gap-1 text-sm text-neutral-400">
|
||||
{#if $relay.contact}
|
||||
<Anchor external underline href={$relay.contact}>{displayUrl($relay.contact)}</Anchor>
|
||||
•
|
||||
{/if}
|
||||
{#if $relay.supported_nips}
|
||||
<Popover>
|
||||
<span slot="trigger" class="cursor-pointer underline">
|
||||
{$relay.supported_nips.length} NIPs
|
||||
</span>
|
||||
<span slot="tooltip">
|
||||
NIPs supported: {$relay.supported_nips.join(", ")}
|
||||
</span>
|
||||
</Popover>
|
||||
•
|
||||
{/if}
|
||||
Seen {quantify($relay.count || 0, "time")}
|
||||
</span>
|
||||
{/if}
|
||||
{#if showControls && $canSign}
|
||||
<div class="-mx-6 my-1 h-px bg-tinted-700" />
|
||||
|
@ -90,7 +90,7 @@ export const deriveEventsMapped = <T>({
|
||||
if (dirty) {
|
||||
if (debug) {
|
||||
if (new Set(data.map(item => itemToEvent(item).id)).size < data.length) {
|
||||
console.error(`Duplicate records found:`, copy, data, updates)
|
||||
console.error(`Duplicate records found:`, copy, [...data], updates)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
import Image from "src/partials/Image.svelte"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Spinner from "src/partials/Spinner.svelte"
|
||||
import {getSetting, dufflepud, imgproxy} from "src/engine"
|
||||
import {dufflepud, imgproxy} from "src/engine"
|
||||
|
||||
export let url
|
||||
export let imeta = Tags.wrap([["url", url]])
|
||||
|
@ -5,16 +5,18 @@
|
||||
export let displayOption = x => x
|
||||
|
||||
const getClassName = active =>
|
||||
cx("px-4 h-7 transition-all rounded-full mr-2 mb-2 inline-flex items-center", {
|
||||
cx("px-4 h-6 transition-all rounded-full mr-2 mb-2 inline-block items-center", {
|
||||
"bg-neutral-900 dark:bg-tinted-100 text-accent": active,
|
||||
"bg-neutral-900 text-neutral-400": !active,
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="-mb-2 inline-block">
|
||||
<SelectList {...$$props} class="staatliches inline-flex">
|
||||
<div class={cx($$props.class, "-mb-2 inline-block")}>
|
||||
<SelectList {...$$props} optionClass="staatliches inline-block">
|
||||
<div slot="item" let:i let:active let:option class={getClassName(active)}>
|
||||
{displayOption(option)}
|
||||
<slot name="item" {option} {active}>
|
||||
{displayOption(option)}
|
||||
</slot>
|
||||
</div>
|
||||
</SelectList>
|
||||
</div>
|
||||
|
@ -6,6 +6,7 @@
|
||||
export let onChange = null
|
||||
export let disabled = false
|
||||
export let multiple = false
|
||||
export let optionClass = ""
|
||||
|
||||
const onClick = option => {
|
||||
if (multiple) {
|
||||
@ -24,7 +25,7 @@
|
||||
class:opacity-75={disabled}
|
||||
class:cursor-pointer={!disabled}>
|
||||
{#each options as option, i}
|
||||
<div on:click={() => onClick(option)}>
|
||||
<div class={optionClass} on:click={() => onClick(option)}>
|
||||
<slot
|
||||
name="item"
|
||||
{i}
|
||||
|
Loading…
Reference in New Issue
Block a user