mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-18 19:23:40 +00:00
Add mutes
This commit is contained in:
parent
bfc011551a
commit
c4c72abddc
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
- [x] Make "show new notes" button fixed position
|
- [x] Make "show new notes" button fixed position
|
||||||
- [x] Gray out buttons that don't work when logged in with pubkey
|
- [x] Gray out buttons that don't work when logged in with pubkey
|
||||||
|
- [x] Clean up popovers, re-design notes on small screens
|
||||||
|
- [x] Migrate muffle to mute, add thread muting
|
||||||
|
|
||||||
## 0.2.16
|
## 0.2.16
|
||||||
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
# Current
|
# Current
|
||||||
|
|
||||||
|
- [ ] Check mention interpolation indexes nevent1qqsx27cspgfcj93kryt2zpzzt5ua60rtucckvcmsrqc949e6t83jaxspzemhxue69uhhyetvv9ujumn0wd68ytnzv9hxg46e8sv
|
||||||
|
- [ ] Muffle -> mute
|
||||||
|
- Add mute button on profile
|
||||||
- [ ] Show loading/success on zap invoice screen
|
- [ ] Show loading/success on zap invoice screen
|
||||||
- [ ] Fix iOS/safari/firefox
|
- [ ] Fix iOS/safari/firefox
|
||||||
- [ ] Update https://nostr.com/clients/coracle
|
- [ ] Update https://nostr.com/clients/coracle
|
||||||
|
@ -60,7 +60,6 @@
|
|||||||
import NoteDetail from "src/views/notes/NoteDetail.svelte"
|
import NoteDetail from "src/views/notes/NoteDetail.svelte"
|
||||||
import PersonList from "src/views/person/PersonList.svelte"
|
import PersonList from "src/views/person/PersonList.svelte"
|
||||||
import PersonProfileInfo from "src/views/person/PersonProfileInfo.svelte"
|
import PersonProfileInfo from "src/views/person/PersonProfileInfo.svelte"
|
||||||
import PersonSettings from "src/views/person/PersonSettings.svelte"
|
|
||||||
import PersonShare from "src/views/person/PersonShare.svelte"
|
import PersonShare from "src/views/person/PersonShare.svelte"
|
||||||
import AddRelay from "src/views/relays/AddRelay.svelte"
|
import AddRelay from "src/views/relays/AddRelay.svelte"
|
||||||
import RelayCard from "src/views/relays/RelayCard.svelte"
|
import RelayCard from "src/views/relays/RelayCard.svelte"
|
||||||
@ -250,8 +249,6 @@
|
|||||||
<PubKeyLogin />
|
<PubKeyLogin />
|
||||||
{:else if $modal.type === "login/connect"}
|
{:else if $modal.type === "login/connect"}
|
||||||
<ConnectUser />
|
<ConnectUser />
|
||||||
{:else if $modal.type === "person/settings"}
|
|
||||||
<PersonSettings person={$modal.person} />
|
|
||||||
{:else if $modal.type === "person/info"}
|
{:else if $modal.type === "person/info"}
|
||||||
<PersonProfileInfo person={$modal.person} />
|
<PersonProfileInfo person={$modal.person} />
|
||||||
{:else if $modal.type === "person/share"}
|
{:else if $modal.type === "person/share"}
|
||||||
|
@ -26,8 +26,8 @@ const setRelays = newRelays =>
|
|||||||
const setPetnames = petnames =>
|
const setPetnames = petnames =>
|
||||||
new PublishableEvent(3, {tags: petnames})
|
new PublishableEvent(3, {tags: petnames})
|
||||||
|
|
||||||
const muffle = muffle =>
|
const setMutes = mutes =>
|
||||||
new PublishableEvent(12165, {tags: muffle})
|
new PublishableEvent(10000, {tags: mutes})
|
||||||
|
|
||||||
const createRoom = room =>
|
const createRoom = room =>
|
||||||
new PublishableEvent(40, {content: JSON.stringify(pick(roomAttrs, room))})
|
new PublishableEvent(40, {content: JSON.stringify(pick(roomAttrs, room))})
|
||||||
@ -132,7 +132,7 @@ class PublishableEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
updateUser, setRelays, setPetnames, muffle, createRoom, updateRoom,
|
updateUser, setRelays, setPetnames, setMutes, createRoom, updateRoom,
|
||||||
createChatMessage, createDirectMessage, createNote, createReaction,
|
createChatMessage, createDirectMessage, createNote, createReaction,
|
||||||
createReply, requestZap, deleteEvent, PublishableEvent,
|
createReply, requestZap, deleteEvent, PublishableEvent,
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,23 @@ const processProfileEvents = async events => {
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
12165: () => ({muffle: e.tags}),
|
10000: () => {
|
||||||
|
if (e.created_at > (person.mutes_updated_at || 0)) {
|
||||||
|
return {
|
||||||
|
mutes_updated_at: e.created_at,
|
||||||
|
mutes: e.tags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// DEPRECATED
|
||||||
|
12165: () => {
|
||||||
|
if (e.created_at > (person.mutes_updated_at || 0)) {
|
||||||
|
return {
|
||||||
|
mutes_updated_at: e.created_at,
|
||||||
|
mutes: e.tags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
// DEPRECATED
|
// DEPRECATED
|
||||||
10001: () => {
|
10001: () => {
|
||||||
if (e.created_at > (person.relays_updated_at || 0)) {
|
if (e.created_at > (person.relays_updated_at || 0)) {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import type {Person} from 'src/util/types'
|
import type {Person} from 'src/util/types'
|
||||||
import type {Readable} from 'svelte/store'
|
import type {Readable} from 'svelte/store'
|
||||||
import {last, prop, find, pipe, assoc, whereEq, when, concat, reject, nth, map} from 'ramda'
|
import {slice, identity, prop, find, pipe, assoc, whereEq, when, concat, reject, nth, map} from 'ramda'
|
||||||
|
import {findReplyId, findRootId} from 'src/util/nostr'
|
||||||
import {synced} from 'src/util/misc'
|
import {synced} from 'src/util/misc'
|
||||||
import {Tags} from 'src/util/nostr'
|
|
||||||
import {derived} from 'svelte/store'
|
import {derived} from 'svelte/store'
|
||||||
import database from 'src/agent/database'
|
import database from 'src/agent/database'
|
||||||
import keys from 'src/agent/keys'
|
import keys from 'src/agent/keys'
|
||||||
@ -19,9 +19,11 @@ let settingsCopy = null
|
|||||||
let profileCopy = null
|
let profileCopy = null
|
||||||
let petnamesCopy = []
|
let petnamesCopy = []
|
||||||
let relaysCopy = []
|
let relaysCopy = []
|
||||||
|
let mutesCopy = []
|
||||||
|
|
||||||
const anonPetnames = synced('agent/user/anonPetnames', [])
|
const anonPetnames = synced('agent/user/anonPetnames', [])
|
||||||
const anonRelays = synced('agent/user/anonRelays', [])
|
const anonRelays = synced('agent/user/anonRelays', [])
|
||||||
|
const anonMutes = synced('agent/user/anonMutes', [])
|
||||||
|
|
||||||
const settings = synced("agent/user/settings", {
|
const settings = synced("agent/user/settings", {
|
||||||
relayLimit: 20,
|
relayLimit: 20,
|
||||||
@ -42,17 +44,17 @@ const profile = derived(
|
|||||||
}
|
}
|
||||||
) as Readable<Person>
|
) as Readable<Person>
|
||||||
|
|
||||||
const petnames = derived(
|
const profileKeyWithDefault = (key, stores) => derived(
|
||||||
[profile, anonPetnames],
|
[profile, ...stores],
|
||||||
([$profile, $anonPetnames]) =>
|
([$profile, ...values]) =>
|
||||||
$profile?.petnames || $anonPetnames
|
$profile?.[key] || find(identity, values)
|
||||||
)
|
)
|
||||||
|
|
||||||
const relays = derived(
|
const petnames = profileKeyWithDefault('petnames', [anonPetnames])
|
||||||
[profile, anonRelays],
|
const relays = profileKeyWithDefault('relays', [anonRelays])
|
||||||
([$profile, $anonRelays]) =>
|
|
||||||
$profile?.relays || $anonRelays
|
// Backwards compat, migrate muffle to mute temporarily
|
||||||
)
|
const mutes = profileKeyWithDefault('mutes', [anonMutes, derived(profile, prop('muffle'))])
|
||||||
|
|
||||||
const canPublish = derived(
|
const canPublish = derived(
|
||||||
[keys.pubkey, relays],
|
[keys.pubkey, relays],
|
||||||
@ -74,6 +76,10 @@ petnames.subscribe($petnames => {
|
|||||||
petnamesCopy = $petnames
|
petnamesCopy = $petnames
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mutes.subscribe($mutes => {
|
||||||
|
mutesCopy = $mutes
|
||||||
|
})
|
||||||
|
|
||||||
relays.subscribe($relays => {
|
relays.subscribe($relays => {
|
||||||
relaysCopy = $relays
|
relaysCopy = $relays
|
||||||
})
|
})
|
||||||
@ -92,34 +98,6 @@ const user = {
|
|||||||
canPublish,
|
canPublish,
|
||||||
getProfile: () => profileCopy,
|
getProfile: () => profileCopy,
|
||||||
getPubkey: () => profileCopy?.pubkey,
|
getPubkey: () => profileCopy?.pubkey,
|
||||||
muffle: events => {
|
|
||||||
const muffle = user.getMuffle()
|
|
||||||
|
|
||||||
return events.filter(e => !muffle.has(e.pubkey))
|
|
||||||
},
|
|
||||||
getMuffle: () => {
|
|
||||||
return new Set(
|
|
||||||
Tags
|
|
||||||
.wrap((profileCopy?.muffle || []))
|
|
||||||
.filter(t => Math.random() > parseFloat(last(t)))
|
|
||||||
.values()
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
},
|
|
||||||
mute: events => {
|
|
||||||
const mutes = user.getMutes()
|
|
||||||
|
|
||||||
return events.filter(e => !mutes.has(e.pubkey))
|
|
||||||
},
|
|
||||||
getMutes: () => {
|
|
||||||
return new Set(
|
|
||||||
Tags
|
|
||||||
.wrap((profileCopy?.muffle || []))
|
|
||||||
.filter(t => parseFloat(last(t)) === 0)
|
|
||||||
.values()
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
// Petnames
|
// Petnames
|
||||||
|
|
||||||
@ -166,6 +144,39 @@ const user = {
|
|||||||
setRelayWriteCondition(url, write) {
|
setRelayWriteCondition(url, write) {
|
||||||
return this.updateRelays(map(when(whereEq({url}), assoc('write', write))))
|
return this.updateRelays(map(when(whereEq({url}), assoc('write', write))))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Mutes
|
||||||
|
|
||||||
|
mutes,
|
||||||
|
getMutes: () => mutesCopy,
|
||||||
|
applyMutes: events => {
|
||||||
|
const m = new Set(mutesCopy.map(m => m[1]))
|
||||||
|
|
||||||
|
return events.filter(e =>
|
||||||
|
!(m.has(e.id) || m.has(e.pubkey) || m.has(findReplyId(e)) || m.has(findRootId(e)))
|
||||||
|
)
|
||||||
|
},
|
||||||
|
updateMutes(f) {
|
||||||
|
const $mutes = f(mutesCopy)
|
||||||
|
console.log(mutesCopy, $mutes)
|
||||||
|
|
||||||
|
anonMutes.set($mutes)
|
||||||
|
|
||||||
|
if (profileCopy) {
|
||||||
|
return cmd.setMutes($mutes.map(slice(0, 2))).publish(relaysCopy)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addMute(type, value) {
|
||||||
|
return this.updateMutes(
|
||||||
|
pipe(
|
||||||
|
reject(t => t[1] === value),
|
||||||
|
concat([[type, value]])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
removeMute(pubkey) {
|
||||||
|
return this.updateMutes(reject(t => t[1] === pubkey))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default user
|
export default user
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import {sortBy, assoc} from "ramda"
|
import {sortBy, any, assoc} from "ramda"
|
||||||
import {onMount} from "svelte"
|
import {onMount} from "svelte"
|
||||||
import {fly} from "svelte/transition"
|
import {fly} from "svelte/transition"
|
||||||
import {now, createScroller} from "src/util/misc"
|
import {now, createScroller} from "src/util/misc"
|
||||||
@ -22,13 +22,11 @@
|
|||||||
return createScroller(async () => {
|
return createScroller(async () => {
|
||||||
limit += 10
|
limit += 10
|
||||||
|
|
||||||
// Filter out alerts for which we failed to find the required context. The bug
|
// Filter out mutes, and alerts for which we failed to find the required context. The bug
|
||||||
// is really upstream of this, but it's an easy fix
|
// is really upstream of this, but it's an easy fix
|
||||||
const events = user
|
const events = user
|
||||||
.mute(database.alerts.all())
|
.applyMutes(database.alerts.all())
|
||||||
.filter(
|
.filter(e => any(k => e[k].length > 0, ["replies", "likedBy", "zappedBy"]) || e.isMention)
|
||||||
e => e.replies.length > 0 || e.likedBy.length > 0 || e.zappedBy?.length > 0 || e.isMention
|
|
||||||
)
|
|
||||||
|
|
||||||
notes = sortBy(e => -e.created_at, events).slice(0, limit)
|
notes = sortBy(e => -e.created_at, events).slice(0, limit)
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {last} from "ramda"
|
import {last, find} from "ramda"
|
||||||
import {onMount} from "svelte"
|
import {onMount} from "svelte"
|
||||||
import {tweened} from "svelte/motion"
|
import {tweened} from "svelte/motion"
|
||||||
import {fly, fade} from "svelte/transition"
|
import {fly, fade} from "svelte/transition"
|
||||||
@ -26,11 +26,12 @@
|
|||||||
export let relays = []
|
export let relays = []
|
||||||
|
|
||||||
const interpolate = (a, b) => t => a + Math.round((b - a) * t)
|
const interpolate = (a, b) => t => a + Math.round((b - a) * t)
|
||||||
const {petnamePubkeys, canPublish} = user
|
const {petnamePubkeys, canPublish, mutes} = user
|
||||||
const getRelays = () => sampleRelays(relays.concat(getPubkeyWriteRelays(pubkey)))
|
const getRelays = () => sampleRelays(relays.concat(getPubkeyWriteRelays(pubkey)))
|
||||||
|
|
||||||
let pubkey = toHex(npub)
|
let pubkey = toHex(npub)
|
||||||
let following = false
|
let following = false
|
||||||
|
let muted = false
|
||||||
let followers = new Set()
|
let followers = new Set()
|
||||||
let followersCount = tweened(0, {interpolate, duration: 1000})
|
let followersCount = tweened(0, {interpolate, duration: 1000})
|
||||||
let person = database.getPersonWithFallback(pubkey)
|
let person = database.getPersonWithFallback(pubkey)
|
||||||
@ -39,6 +40,7 @@
|
|||||||
let actions = []
|
let actions = []
|
||||||
|
|
||||||
$: following = $petnamePubkeys.includes(pubkey)
|
$: following = $petnamePubkeys.includes(pubkey)
|
||||||
|
$: muted = find(m => m[1] === pubkey, $mutes)
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
actions = []
|
actions = []
|
||||||
@ -53,12 +55,17 @@
|
|||||||
actions.push({onClick: follow, label: "Follow", icon: "user-plus"})
|
actions.push({onClick: follow, label: "Follow", icon: "user-plus"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (muted) {
|
||||||
|
actions.push({onClick: unmute, label: "Muted", icon: "microphone-slash"})
|
||||||
|
} else if (user.getPubkey() !== pubkey) {
|
||||||
|
actions.push({onClick: mute, label: "Mute", icon: "microphone"})
|
||||||
|
}
|
||||||
|
|
||||||
actions.push({
|
actions.push({
|
||||||
onClick: () => navigate(`/messages/${npub}`),
|
onClick: () => navigate(`/messages/${npub}`),
|
||||||
label: "Message",
|
label: "Message",
|
||||||
icon: "envelope",
|
icon: "envelope",
|
||||||
})
|
})
|
||||||
actions.push({onClick: openAdvanced, label: "Advanced", icon: "sliders"})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
actions.push({onClick: openProfileInfo, label: "Profile", icon: "info"})
|
actions.push({onClick: openProfileInfo, label: "Profile", icon: "info"})
|
||||||
@ -133,8 +140,12 @@
|
|||||||
user.removePetname(pubkey)
|
user.removePetname(pubkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
const openAdvanced = () => {
|
const mute = async () => {
|
||||||
modal.set({type: "person/settings", person})
|
user.addMute("p", pubkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
const unmute = async () => {
|
||||||
|
user.removeMute(pubkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
const openProfileInfo = () => {
|
const openProfileInfo = () => {
|
||||||
|
@ -3,7 +3,7 @@ import {is, fromPairs, mergeLeft, last, identity, objOf, prop, flatten, uniq} fr
|
|||||||
import {nip19} from 'nostr-tools'
|
import {nip19} from 'nostr-tools'
|
||||||
import {ensurePlural, ellipsize, first} from 'hurdak/lib/hurdak'
|
import {ensurePlural, ellipsize, first} from 'hurdak/lib/hurdak'
|
||||||
|
|
||||||
export const personKinds = [0, 2, 3, 10001, 10002, 12165]
|
export const personKinds = [0, 2, 3, 10000, 10001, 10002, 12165]
|
||||||
|
|
||||||
export class Tags {
|
export class Tags {
|
||||||
tags: Array<any>
|
tags: Array<any>
|
||||||
|
@ -9,9 +9,9 @@ export type Relay = {
|
|||||||
|
|
||||||
export type Person = {
|
export type Person = {
|
||||||
pubkey: string
|
pubkey: string
|
||||||
relays?: Array<Relay>
|
|
||||||
muffle?: Array<Array<string>>
|
|
||||||
petnames?: Array<Array<string>>
|
petnames?: Array<Array<string>>
|
||||||
|
relays?: Array<Relay>
|
||||||
|
mutes?: Array<Array<string>>
|
||||||
kind0?: {
|
kind0?: {
|
||||||
name?: string
|
name?: string
|
||||||
about?: string
|
about?: string
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
const onChunk = async newNotes => {
|
const onChunk = async newNotes => {
|
||||||
// Deduplicate and filter out stuff we don't want, apply user preferences
|
// Deduplicate and filter out stuff we don't want, apply user preferences
|
||||||
const filtered = user.muffle(newNotes.filter(n => !seen.has(n.id) && shouldDisplay(n)))
|
const filtered = user.applyMutes(newNotes.filter(n => !seen.has(n.id) && shouldDisplay(n)))
|
||||||
|
|
||||||
// Drop the oldest 20% of notes. We sometimes get pretty old stuff since we don't
|
// Drop the oldest 20% of notes. We sometimes get pretty old stuff since we don't
|
||||||
// use a since on our filter
|
// use a since on our filter
|
||||||
@ -68,7 +68,7 @@
|
|||||||
depth: 2,
|
depth: 2,
|
||||||
notes: combined,
|
notes: combined,
|
||||||
onChunk: context => {
|
onChunk: context => {
|
||||||
context = user.muffle(context)
|
context = user.applyMutes(context)
|
||||||
|
|
||||||
notesBuffer = network.applyContext(notesBuffer, context)
|
notesBuffer = network.applyContext(notesBuffer, context)
|
||||||
notes = network.applyContext(notes, context)
|
notes = network.applyContext(notes, context)
|
||||||
@ -118,10 +118,11 @@
|
|||||||
|
|
||||||
<Content size="inherit" class="pt-6">
|
<Content size="inherit" class="pt-6">
|
||||||
{#if notesBuffer.length > 0}
|
{#if notesBuffer.length > 0}
|
||||||
<div class="fixed left-0 top-0 z-10 mt-20 flex w-full justify-center">
|
<div class="pointer-events-none fixed left-0 top-0 z-10 mt-20 flex w-full justify-center">
|
||||||
<button
|
<button
|
||||||
in:fly={{y: 20}}
|
in:fly={{y: 20}}
|
||||||
class="cursor-pointer rounded-full border border-solid border-accentl bg-accent py-2 px-4 text-center shadow-lg transition-colors hover:bg-accentl"
|
class="pointer-events-auto cursor-pointer rounded-full border border-solid border-accentl
|
||||||
|
bg-accent py-2 px-4 text-center shadow-lg transition-colors hover:bg-accentl"
|
||||||
on:click={loadBufferedNotes}>
|
on:click={loadBufferedNotes}>
|
||||||
Load {quantify(notesBuffer.length, "new note")}
|
Load {quantify(notesBuffer.length, "new note")}
|
||||||
</button>
|
</button>
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
import {onMount} from "svelte"
|
import {onMount} from "svelte"
|
||||||
import {tweened} from "svelte/motion"
|
import {tweened} from "svelte/motion"
|
||||||
import {slide} from "svelte/transition"
|
import {slide} from "svelte/transition"
|
||||||
import {navigate} from "svelte-routing"
|
|
||||||
import {quantify} from "hurdak/lib/hurdak"
|
import {quantify} from "hurdak/lib/hurdak"
|
||||||
import {Tags, findRootId, findReplyId, displayPerson, isLike} from "src/util/nostr"
|
import {Tags, findRootId, findReplyId, displayPerson, isLike} from "src/util/nostr"
|
||||||
import {formatTimestamp, now, tryJson, formatSats, fetchJson} from "src/util/misc"
|
import {formatTimestamp, now, tryJson, formatSats, fetchJson} from "src/util/misc"
|
||||||
@ -54,7 +53,7 @@
|
|||||||
let visibleNotes = []
|
let visibleNotes = []
|
||||||
let showRelays = false
|
let showRelays = false
|
||||||
|
|
||||||
const {profile, canPublish} = user
|
const {profile, canPublish, mutes} = user
|
||||||
const timestamp = formatTimestamp(note.created_at)
|
const timestamp = formatTimestamp(note.created_at)
|
||||||
const borderColor = invertColors ? "medium" : "dark"
|
const borderColor = invertColors ? "medium" : "dark"
|
||||||
const links = extractUrls(note.content)
|
const links = extractUrls(note.content)
|
||||||
@ -63,6 +62,7 @@
|
|||||||
const person = database.watch("people", () => database.getPersonWithFallback(note.pubkey))
|
const person = database.watch("people", () => database.getPersonWithFallback(note.pubkey))
|
||||||
|
|
||||||
let likes, flags, zaps, like, flag, border, childrenContainer, noteContainer, canZap
|
let likes, flags, zaps, like, flag, border, childrenContainer, noteContainer, canZap
|
||||||
|
let muted = false
|
||||||
|
|
||||||
const interpolate = (a, b) => t => a + Math.round((b - a) * t)
|
const interpolate = (a, b) => t => a + Math.round((b - a) * t)
|
||||||
const likesCount = tweened(0, {interpolate})
|
const likesCount = tweened(0, {interpolate})
|
||||||
@ -70,6 +70,7 @@
|
|||||||
const zapsTotal = tweened(0, {interpolate})
|
const zapsTotal = tweened(0, {interpolate})
|
||||||
const repliesCount = tweened(0, {interpolate})
|
const repliesCount = tweened(0, {interpolate})
|
||||||
|
|
||||||
|
$: muted = find(m => m[1] === note.id, $mutes)
|
||||||
$: likes = note.reactions.filter(n => isLike(n.content))
|
$: likes = note.reactions.filter(n => isLike(n.content))
|
||||||
$: flags = note.reactions.filter(whereEq({content: "-"}))
|
$: flags = note.reactions.filter(whereEq({content: "-"}))
|
||||||
$: zaps = note.zaps
|
$: zaps = note.zaps
|
||||||
@ -143,11 +144,15 @@
|
|||||||
modal.set({type: "note/detail", note: {id: findRootId(note)}, relays})
|
modal.set({type: "note/detail", note: {id: findRootId(note)}, relays})
|
||||||
}
|
}
|
||||||
|
|
||||||
const react = async content => {
|
const mute = async () => {
|
||||||
if (!$profile) {
|
user.addMute("e", note.id)
|
||||||
return navigate("/login")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const unmute = async () => {
|
||||||
|
user.removeMute(note.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const react = async content => {
|
||||||
const relays = getEventPublishRelays(note)
|
const relays = getEventPublishRelays(note)
|
||||||
const [event] = await cmd.createReaction(note, content).publish(relays)
|
const [event] = await cmd.createReaction(note, content).publish(relays)
|
||||||
|
|
||||||
@ -173,11 +178,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const startReply = () => {
|
const startReply = () => {
|
||||||
if ($profile) {
|
|
||||||
reply = reply || true
|
reply = reply || true
|
||||||
} else {
|
|
||||||
navigate("/login")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeMention = pubkey => {
|
const removeMention = pubkey => {
|
||||||
@ -385,6 +386,10 @@
|
|||||||
You have flagged this content as offensive.
|
You have flagged this content as offensive.
|
||||||
<Anchor on:click={() => deleteReaction(flag)}>Unflag</Anchor>
|
<Anchor on:click={() => deleteReaction(flag)}>Unflag</Anchor>
|
||||||
</p>
|
</p>
|
||||||
|
{:else if muted}
|
||||||
|
<p class="border-l-2 border-solid border-medium pl-4 text-light">
|
||||||
|
You have muted this note.
|
||||||
|
</p>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex flex-col gap-2 overflow-hidden text-ellipsis">
|
<div class="flex flex-col gap-2 overflow-hidden text-ellipsis">
|
||||||
<p>{@html renderNote(note, {showEntire})}</p>
|
<p>{@html renderNote(note, {showEntire})}</p>
|
||||||
@ -394,10 +399,11 @@
|
|||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
<div class="flex justify-between text-light">
|
<div class="flex justify-between text-light">
|
||||||
<div
|
<div
|
||||||
class={cx("flex", {
|
class={cx("flex", {
|
||||||
"pointer-events-none opacity-75": !$canPublish,
|
"pointer-events-none opacity-75": !$canPublish || flag || muted,
|
||||||
})}>
|
})}>
|
||||||
<button class="w-16 text-left" on:click|stopPropagation={startReply}>
|
<button class="w-16 text-left" on:click|stopPropagation={startReply}>
|
||||||
<i class="fa fa-reply cursor-pointer" />
|
<i class="fa fa-reply cursor-pointer" />
|
||||||
@ -440,6 +446,15 @@
|
|||||||
}}>
|
}}>
|
||||||
<i class="fa fa-server" />
|
<i class="fa fa-server" />
|
||||||
</Anchor>
|
</Anchor>
|
||||||
|
{#if muted}
|
||||||
|
<Anchor type="button-circle" on:click={unmute}>
|
||||||
|
<i class="fa fa-microphone" />
|
||||||
|
</Anchor>
|
||||||
|
{:else}
|
||||||
|
<Anchor type="button-circle" on:click={mute}>
|
||||||
|
<i class="fa fa-microphone-slash" />
|
||||||
|
</Anchor>
|
||||||
|
{/if}
|
||||||
<Anchor type="button-circle" on:click={() => react("-")}>
|
<Anchor type="button-circle" on:click={() => react("-")}>
|
||||||
<i class="fa fa-flag" />
|
<i class="fa fa-flag" />
|
||||||
</Anchor>
|
</Anchor>
|
||||||
@ -447,7 +462,6 @@
|
|||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@ -503,7 +517,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if visibleNotes.length > 0 && depth > 0}
|
{#if visibleNotes.length > 0 && depth > 0 && !muted}
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div class={`absolute w-px bg-${borderColor} z-10 -mt-4 ml-4 h-0`} bind:this={border} />
|
<div class={`absolute w-px bg-${borderColor} z-10 -mt-4 ml-4 h-0`} bind:this={border} />
|
||||||
<div class="note-children relative ml-8 flex flex-col gap-4" bind:this={childrenContainer}>
|
<div class="note-children relative ml-8 flex flex-col gap-4" bind:this={childrenContainer}>
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
depth: 6,
|
depth: 6,
|
||||||
notes: [note],
|
notes: [note],
|
||||||
onChunk: context => {
|
onChunk: context => {
|
||||||
note = first(network.applyContext([note], user.muffle(context)))
|
note = first(network.applyContext([note], user.applyMutes(context)))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -54,7 +54,7 @@
|
|||||||
<Content size="lg" class="text-center">Sorry, we weren't able to find this note.</Content>
|
<Content size="lg" class="text-center">Sorry, we weren't able to find this note.</Content>
|
||||||
</div>
|
</div>
|
||||||
{:else if note.pubkey}
|
{:else if note.pubkey}
|
||||||
<div in:fly={{y: 20}} class="m-auto flex max-w-2xl flex-col gap-4 p-4">
|
<div in:fly={{y: 20}} class="m-auto flex w-full max-w-2xl flex-col gap-4 p-4">
|
||||||
<Note showContext depth={6} anchorId={note.id} note={asDisplayEvent(note)} {invertColors} />
|
<Note showContext depth={6} anchorId={note.id} note={asDisplayEvent(note)} {invertColors} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
<script>
|
|
||||||
import {last} from "ramda"
|
|
||||||
import {switcher, first} from "hurdak/lib/hurdak"
|
|
||||||
import {fly} from "svelte/transition"
|
|
||||||
import Button from "src/partials/Button.svelte"
|
|
||||||
import Content from "src/partials/Content.svelte"
|
|
||||||
import SelectButton from "src/partials/SelectButton.svelte"
|
|
||||||
import user from "src/agent/user"
|
|
||||||
import {getUserWriteRelays} from "src/agent/relays"
|
|
||||||
import cmd from "src/agent/cmd"
|
|
||||||
import {publishWithToast} from "src/app"
|
|
||||||
|
|
||||||
export let person
|
|
||||||
|
|
||||||
const muffle = user.getProfile().muffle || []
|
|
||||||
const muffleOptions = ["Never", "Sometimes", "Often", "Always"]
|
|
||||||
const muffleValue = parseFloat(first(muffle.filter(t => t[1] === person.pubkey).map(last)) || 1)
|
|
||||||
|
|
||||||
const values = {
|
|
||||||
// Scale up to integers for each choice we have
|
|
||||||
muffle: switcher(Math.round(muffleValue * 3), muffleOptions),
|
|
||||||
}
|
|
||||||
|
|
||||||
const save = async e => {
|
|
||||||
e.preventDefault()
|
|
||||||
|
|
||||||
// Scale back down to a decimal based on string value
|
|
||||||
const muffleValue = muffleOptions.indexOf(values.muffle) / 3
|
|
||||||
const muffleTags = muffle
|
|
||||||
.filter(t => t[1] !== person.pubkey)
|
|
||||||
.concat([["p", person.pubkey, muffleValue.toString()]])
|
|
||||||
.filter(t => last(t) !== "1")
|
|
||||||
|
|
||||||
publishWithToast(getUserWriteRelays(), cmd.muffle(muffleTags))
|
|
||||||
|
|
||||||
history.back()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<form in:fly={{y: 20}} on:submit={save}>
|
|
||||||
<Content class="text-white">
|
|
||||||
<div class="flex flex-col gap-2">
|
|
||||||
<h1 class="text-3xl">Advanced Follow</h1>
|
|
||||||
<p>Fine grained controls for interacting with other people.</p>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col gap-1">
|
|
||||||
<strong>How often do you want to see notes from this person?</strong>
|
|
||||||
<SelectButton bind:value={values.muffle} options={muffleOptions} />
|
|
||||||
<p class="text-sm text-light">
|
|
||||||
"Never" is effectively a mute, while "Always" will show posts whenever available. If you
|
|
||||||
want a middle ground, choose "Sometimes" or "Often".
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Button type="submit" class="text-center">Done</Button>
|
|
||||||
</Content>
|
|
||||||
</form>
|
|
Loading…
Reference in New Issue
Block a user