aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdrien Hopkins <[email protected]>2026-01-09 09:00:32 -0500
committerAdrien Hopkins <[email protected]>2026-01-09 09:00:32 -0500
commit9175c76d7f03c0c62323202fdab3e4ab35854855 (patch)
tree6195dc3eac3c55fbd84cd32f34077dab10ec7ac5
parent4bc94247c382898c0e0d76439fe0bdce68755d69 (diff)
downloadcoinos-ui-9175c76d7f03c0c62323202fdab3e4ab35854855.tar.gz
coinos-ui-9175c76d7f03c0c62323202fdab3e4ab35854855.zip
Hide muted chats
-rw-r--r--src/lib/nip51.ts47
-rw-r--r--src/locales/en.json2
-rw-r--r--src/routes/(app)/dm/+page.svelte24
3 files changed, 69 insertions, 4 deletions
diff --git a/src/lib/nip51.ts b/src/lib/nip51.ts
new file mode 100644
index 00000000..142f0bbf
--- /dev/null
+++ b/src/lib/nip51.ts
@@ -0,0 +1,47 @@
+import { SimplePool } from 'nostr-tools/pool';
+const pool = new SimplePool();
+
+import { PUBLIC_DM_RELAYS } from '$env/static/public';
+const DM_RELAYS_LIST = PUBLIC_DM_RELAYS.split(',');
+
+// export const mute = async (pubkey: string, hidden: bool) => {
+
+// }
+
+// export const unmute = async (pubkey: string) => {
+
+// }
+
+export const mutedAccounts = async (user: object): Set<string> => {
+ const { shown, hidden } = await getMuteLists(user);
+ let muted = new Set<string>();
+ for (const pubkey of shown) {
+ muted.add(pubkey);
+ }
+ for (const pubkey of hidden) {
+ muted.add(pubkey);
+ }
+ return muted;
+}
+
+const getMuteLists = async (user: object): object => {
+ const listEvents = await pool.querySync(
+ DM_RELAYS_LIST, { kinds: [10000], "authors": [user.pubkey], limit: 1 }
+ );
+ if (listEvents.length == 0) {
+ return { shown: [], hidden: [] };
+ }
+
+ let shown = [];
+ for (const tag of listEvents[0].tags) {
+ if (tag.length >= 2 && tag[0] == "p") {
+ shown.push(tag[1]);
+ }
+ }
+
+ return { shown, hidden: [] };
+}
+
+// const publishMuteList = async (shown: Array<string>, hidden: Array<string>) => {
+
+// }
diff --git a/src/locales/en.json b/src/locales/en.json
index e7b8a90f..16e60d1f 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -102,6 +102,8 @@
"findByPubkey": "Find by pubkey",
"recipientMsg": "You are sending nostr messages to [R].",
"sendMessage": "Send Message",
+ "mute": "Mute",
+ "unmute": "Unmute",
"expiry": "Enable message expiry",
"cantSendExpiring": "None of [R]'s DM relays support message expiry. You can't send expiring messages to them.",
"fadedWarning": "To avoid metadata leaks, messages expire at a random time up to 2 days after you specify; each message has its own random expiry time. Messages which appear faded are close to expiring; nearby messages may be missing.",
diff --git a/src/routes/(app)/dm/+page.svelte b/src/routes/(app)/dm/+page.svelte
index 32b39def..336733e0 100644
--- a/src/routes/(app)/dm/+page.svelte
+++ b/src/routes/(app)/dm/+page.svelte
@@ -10,6 +10,7 @@
import { getMessageRumours } from '$lib/nip17';
import * as libnip17 from '$lib/nip17';
import { npubEncode, decode } from 'nostr-tools/nip19';
+ import { mutedAccounts } from '$lib/nip51';
import { sign, getPrivateKey, pTagKeys } from '$lib/nostr';
import { t } from "$lib/translations";
import { theme } from "$lib/store";
@@ -17,6 +18,9 @@
let { data } = $props();
const { user } = data;
+ let muted = $state(new Set<string>());
+ mutedAccounts(user).then((m) => muted = m);
+
/// chat selection
const usernameFromPubkey = async (pubkey: string): string => {
const response = await fetch(`/api/users/${pubkey}`);
@@ -129,9 +133,18 @@
let canSendExpiring = $state(false);
let nostrUserInfo = $state({});
+ const includesPubkey = (chats: object[], pubkey: string) => {
+ for (const c of chats) {
+ if (c.pubkey === pubkey) {
+ return true;
+ }
+ }
+ return false;
+ }
+
const selectChat = (c: object) => {
selectedChat = c;
- if (!chats.includes(c)) {
+ if (!includesPubkey(chats, c.pubkey)) {
chats.unshift(c);
}
userInfo(selectedChat.pubkey).then(info => nostrUserInfo = info);
@@ -394,9 +407,11 @@
{/if}
{#if !creatingNewChat}
{#each chats as c}
- <button class={"chat-btn tall-btn " + ($theme === "light" ? "light-chat-btn" : "dark-chat-btn") + (selectedChat && selectedChat.pubkey == c.pubkey ? ($theme === "light" ? " light-selected" : " dark-selected") : "")} on:click={() => selectChat(c)}>
- <span class="text-xl">{name(c)}</span> <span class={(idValid(c) ? "" : "invalid ") + "secondary text-xs"}>{id(c)}</span>
- </button>
+ {#if (selectedChat && selectedChat.pubkey === c.pubkey) || !(muted && muted.has(c.pubkey))}
+ <button class={"chat-btn tall-btn " + ($theme === "light" ? "light-chat-btn" : "dark-chat-btn") + (selectedChat && selectedChat.pubkey == c.pubkey ? ($theme === "light" ? " light-selected" : " dark-selected") : "")} on:click={() => selectChat(c)}>
+ <span class="{"text-xl" + (muted && muted.has(c.pubkey) ? " secondary" : "")}">{name(c)}</span> <span class={(idValid(c) ? "" : "invalid ") + "secondary text-xs"}>{id(c)}</span>
+ </button>
+ {/if}
{/each}
{:else if searchQuery !== ""}
<button class={"chat-btn tall-btn " + ($theme === "light" ? "light-chat-btn" : "dark-chat-btn")} on:click={() => selectChatUsername(searchQuery)}>
@@ -427,6 +442,7 @@
<span class="text-3xl">{name(selectedChat)}</span>
{/if}
<span class={"secondary timestamp text-small" + (idValid(selectedChat) ? "" : " invalid")} title="{npubEncode(selectedChat.pubkey)}">{id(selectedChat)}</span>
+ <!-- <button class="push-right">{$t(muted && muted.has(selectedChat.pubkey) ? "dm.unmute" : "dm.mute")}</button> -->
</h1>
<div id="messages" style="overflow-y: scroll; max-height: 600px;">